:root {
  --bg: #f4f5f7;
  --panel: #ffffff;
  --border: #d9dce1;
  --text: #1f2430;
  --muted: #6b7280;
  /* DockRelayX brand colors, sampled directly from the logo file (median
     pixel color of each element): deep navy wordmark + circuit-ring icon,
     vivid blue "RelayX", pale blue ring accents, cool gray "BY ACG". */
  --accent: #0057d8;
  --accent-dark: #0044ad;
  --brand-navy: #0a1220;
  --brand-blue-light: #cbe2f8;
  --radius: 10px;

  --c-scheduled: #9ca3af;
  --c-precooling: #3b82f6;
  --c-plugged_in: #f59e0b;
  --c-pulled: #8b5cf6;
  --c-in_yard: #14b8a6;
  --c-departed: #22c55e;
  --c-shuttled: #ec4899;
  --c-movement: #64748b;
  --c-realign: #06b6d4;
  --c-open-doors: #84cc16;
  --c-door-available: #10b981;
  --c-load-ready: #0ea5e9;
  --c-verify-temp: #6366f1;
  --c-assign: #7c3aed;
  --c-no-ship: #ef4444;
  --c-late-yard: #f97316;
  --c-late-loading: #d946ef;
  /* Board card "needs attention" ribbon (Realign/Open Doors/Verify Temp
     still outstanding) -- deliberately its own color, not reused from
     any of the three status/action colors above, since it's a combined
     flag covering all three at once. */
  --c-attention: #f59e0b;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14161c;
    --panel: #1c1f27;
    --border: #2c313c;
    --text: #e7e9ee;
    --muted: #9aa1ac;
  }
}
:root[data-theme="dark"] {
  --bg: #14161c;
  --panel: #1c1f27;
  --border: #2c313c;
  --text: #e7e9ee;
  --muted: #9aa1ac;
}
:root[data-theme="light"] {
  --bg: #f4f5f7;
  --panel: #ffffff;
  --border: #d9dce1;
  --text: #1f2430;
  --muted: #6b7280;
}

* { box-sizing: border-box; }
html, body {
  margin: 0; padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  overflow-x: hidden;
}

/* Live board-change alert -- fixed overlay, not in normal document flow,
   so it never shifts anything else when it shows/hides. Slides down from
   fully off-screen (translateY(-100%)) rather than just fading, so it
   reads as "popping up" the way the ask describes. */
.alert-banner {
  position: fixed; top: 0; left: 0; right: 0; z-index: 150;
  padding: 14px 20px; text-align: center;
  font-size: 15px; font-weight: 700; color: #fff;
  transform: translateY(-100%); transition: transform .25s ease-out;
  cursor: pointer; box-shadow: 0 2px 12px rgba(0,0,0,.2);
}
.alert-banner.show { transform: translateY(0); }
.alert-banner.alert-new { background: var(--accent); }
.alert-banner.alert-ready { background: #16a34a; }
.alert-banner.alert-message { background: #7c3aed; }

/* The board/log/dashboard tables and the yard-check style tools this app
   embeds can all run long -- this is a way back to the top (site switcher,
   date picker, +New Entry) without a long scroll. Hidden until scrolled
   down a bit (see updateScrollTopBtn in app.js); z-index sits below modal
   overlays (50) so it's covered rather than floating on top of one. */
.scroll-top-btn {
  position: fixed; right: 16px; bottom: calc(16px + env(safe-area-inset-bottom));
  z-index: 45; width: 46px; height: 46px; border-radius: 50%;
  background: var(--accent); color: #fff; border: none;
  box-shadow: 0 2px 12px rgba(0,0,0,.25); cursor: pointer;
  font-size: 20px; font-weight: 700; line-height: 1;
  display: none; align-items: center; justify-content: center;
}
.scroll-top-btn:hover { background: var(--accent-dark); }

.topbar {
  display: flex; justify-content: space-between; align-items: center;
  padding: 12px 20px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  border-top: 3px solid var(--accent);
  flex-wrap: wrap; gap: 10px;
}
.topbar-left { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.topbar h1 { font-size: 18px; margin: 0; white-space: nowrap; }
.topbar select, .topbar input { padding: 6px 8px; }

/* Logo has a white background baked in (gradients/shadows in the artwork
   depend on it) -- rather than trying to key it transparent and risking
   the light-blue "RelayX" text getting eaten too, it sits on its own
   white card so it looks correct in both light and dark theme. */
.brand-logo {
  background: #fff;
  border-radius: 8px;
  padding: 4px 10px;
  display: flex;
  align-items: center;
  line-height: 0;
}
.brand-logo img { height: 32px; width: auto; display: block; }

.whoami { display: flex; align-items: center; gap: 12px; }
.whoami input { width: 140px; }

/* English/Spanish toggle, header top-right -- a small two-way pill rather
   than a dropdown since there are only ever two options. The active
   language is filled with --accent; the other stays plain text. See the
   I18N block in app.js for what setLang()/applyI18n() actually cover. */
.lang-toggle {
  display: flex; align-items: center; gap: 2px;
  border: 1px solid var(--border); border-radius: 6px; overflow: hidden;
}
.lang-toggle .lang-option {
  background: none; border: none; cursor: pointer;
  padding: 4px 9px; font-size: 12px; font-weight: 700;
  color: var(--muted);
}
.lang-toggle .lang-option.active {
  background: var(--accent); color: #fff;
}

/* Only ever shown once a 2nd terminal exists -- see updateTerminalIndicator()
   in app.js. A vertical rule separates it from the site picker that follows,
   same visual grouping idea as .whoami on the right side of the topbar. */
.terminal-indicator {
  display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--muted);
  padding-right: 10px; border-right: 1px solid var(--border);
}

.link-btn {
  background: none; border: none; color: var(--accent); padding: 2px 4px;
  font-size: 13px; font-weight: 600; text-decoration: underline; cursor: pointer;
}

/* Site switcher -- its own full-width bar below the topbar (not squeezed
   in next to the logo), sharing a row with the quick search box, a row of
   buttons instead of a <select> for the sites, each with a small red
   badge showing today's outstanding Plug+Pull count (see
   renderSiteIcons()/loadSiteActionCounts() in app.js) -- "a quick
   reference for managers... to see where the focus needs to be." */
.site-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 20px; background: var(--panel); border-bottom: 1px solid var(--border);
}
/* Scrolls horizontally rather than wrapping, so it stays one line with
   many DDPs instead of pushing the rest of the bar down. */
.site-icon-row { flex: 1 1 auto; display: flex; gap: 6px; overflow-x: auto; min-width: 0; }
.site-icon-btn {
  position: relative; flex: none; display: flex; align-items: center; gap: 6px;
  padding: 7px 12px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-size: 13px; font-weight: 600;
  cursor: pointer; white-space: nowrap;
}
.site-icon-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.site-icon-badge {
  display: inline-flex; align-items: center; justify-content: center; gap: 2px;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;
  background: #dc2626; color: #fff; font-size: 11px; font-weight: 700; line-height: 1;
}
.site-icon-bolt { font-size: 10px; line-height: 1; }

/* Quick search -- searches every DDP in the terminal at once, not just
   whichever one is currently selected among the site icons. Shares
   .site-bar's row (per the user's request) instead of its own full-width
   bar -- position:relative still anchors the results dropdown, just to
   this narrower wrapper now, not the whole row. Doesn't grow (flex: 0 ...)
   and margin-left: auto pushes it flush to the right edge, so it never
   competes with .site-icon-row above for space -- the site icons get
   first claim on the row and only need their own internal scroll once
   there truly isn't room for all of them. */
/* Next ~4 hours for Ruther Glen, VA -- own thin strip right under the
   site bar (see #weatherBar in index.html/loadWeather() in app.js), not
   folded into .site-bar's own row, so it doesn't compete with the site
   icons/search box for space on a narrow screen. Same panel/border
   language as .site-bar above it for visual continuity. */
.weather-bar {
  display: flex; align-items: center; justify-content: flex-end; gap: 14px; flex-wrap: wrap;
  padding: 8px 20px; background: var(--panel); border-bottom: 1px solid var(--border);
  font-size: 12px; color: var(--muted);
}
.weather-bar-location { font-weight: 700; color: var(--text); white-space: nowrap; }
.weather-chip {
  display: flex; align-items: center; gap: 5px; white-space: nowrap;
}
.weather-chip-icon { font-size: 16px; line-height: 1; }
.weather-chip-temp { font-weight: 700; color: var(--text); }
.weather-chip-precip { color: var(--c-precooling); }

.quick-search-bar { position: relative; flex: 0 1 300px; min-width: 160px; margin-left: auto; }
#quickSearchInput {
  width: 100%; max-width: 420px; padding: 9px 14px; font-size: 14px;
  border-radius: var(--radius); border: 1px solid var(--border);
  background: var(--bg); color: var(--text);
}
#quickSearchInput:focus { border-color: var(--accent); outline: none; }
.quick-search-results {
  position: absolute; top: 100%; right: 0; left: auto; width: 100%;
  max-width: 420px;
  margin-top: 4px; background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: 0 8px 24px rgba(0,0,0,.18);
  max-height: 360px; overflow-y: auto; z-index: 60;
}
.quick-search-row {
  display: flex; align-items: center; gap: 10px; padding: 9px 14px;
  border-bottom: 1px solid var(--border); cursor: pointer; font-size: 13px;
}
.quick-search-row:last-child { border-bottom: none; }
.quick-search-row:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.quick-search-row .qs-site {
  flex: none; font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .03em; padding: 2px 7px; border-radius: 10px;
  background: var(--muted); color: #fff; white-space: nowrap;
}
.quick-search-row .qs-main { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.quick-search-row .qs-main strong { color: var(--text); }
.quick-search-row .qs-date { flex: none; color: var(--muted); font-size: 12px; }
.quick-search-empty { padding: 12px 14px; color: var(--muted); font-size: 13px; }

.tabs { display: flex; gap: 4px; padding: 10px 20px 0; }
.tab-btn {
  background: none; border: none; padding: 10px 16px; cursor: pointer;
  border-bottom: 2px solid transparent; color: var(--muted); font-size: 14px; font-weight: 600;
}
.tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }

main { padding: 16px 20px 40px; max-width: 1400px; margin: 0 auto; }
.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* Sits below <main>, outside every tab-panel, so it's on screen regardless
   of which tab (Board/Log/Dashboard/Company) is currently active. */
.app-footer {
  text-align: center; padding: 18px 20px 24px;
  font-size: 12px; color: var(--muted);
}

input, select, textarea, button {
  font: inherit; color: inherit;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
}
input, select, textarea { padding: 7px 9px; }
#bulkText { width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; resize: vertical; }
button { cursor: pointer; padding: 7px 12px; }
button.primary { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
button.primary:hover { background: var(--accent-dark); }
button.danger { background: #dc2626; border-color: #dc2626; color: #fff; }
button.icon-btn { background: none; border: none; font-size: 18px; padding: 2px 8px; }

.board-controls, .log-controls {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-bottom: 14px;
}
.updated-note { color: var(--muted); font-size: 12px; margin-left: auto; }
.log-controls input[type="text"] { flex: 1; min-width: 200px; }

.board-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px;
  align-items: start;
}
.card {
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  border-left: 5px solid var(--muted);
  padding: 12px 14px; cursor: pointer; transition: box-shadow .15s;
}
.card:hover { box-shadow: 0 2px 10px rgba(0,0,0,.08); }
.card.status-scheduled { border-left-color: var(--c-scheduled); }
.card.status-precooling { border-left-color: var(--c-precooling); }
.card.status-plugged_in { border-left-color: var(--c-plugged_in); }
.card.status-pulled { border-left-color: var(--c-pulled); }
.card.status-in_yard { border-left-color: var(--c-in_yard); }
.card.status-departed { border-left-color: var(--c-departed); opacity: .7; }
.card.status-shuttled { border-left-color: var(--c-shuttled); opacity: .85; }
.card.status-movement { border-left-color: var(--c-movement); }
/* No Ship: the load isn't shipping today, so nothing else on the card
   matters -- blacked out except the checkbox itself (still needs to stay
   legible/clickable to flip back off) and the status badge. */
.card.no-ship { border-left-color: var(--c-no-ship); }
.card.no-ship .card-trailer,
.card.no-ship .card-row:not(.no-ship-info),
.card.no-ship .quick-actions,
.card.no-ship .card-divider { opacity: .35; }

.card-top { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 6px; }
.card-door { font-size: 18px; font-weight: 700; }
.status-badge {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  padding: 2px 8px; border-radius: 20px; color: #fff; white-space: nowrap;
}
.status-badge.status-scheduled { background: var(--c-scheduled); }
.status-badge.status-precooling { background: var(--c-precooling); }
.status-badge.status-plugged_in { background: var(--c-plugged_in); }
.status-badge.status-pulled { background: var(--c-pulled); }
.status-badge.status-in_yard { background: var(--c-in_yard); }
.status-badge.status-departed { background: var(--c-departed); }
.status-badge.status-shuttled { background: var(--c-shuttled); }
.status-badge.status-movement { background: var(--c-movement); }
/* Movement Type labels ("Backhaul Staging", "Dunnage Cleanout") run longer
   than the usual one-word statuses -- shrink and let it wrap instead of
   overflowing the card or forcing the card wider. */
.status-badge.movement-badge {
  font-size: 9px; white-space: normal; text-align: right;
  max-width: 55%; line-height: 1.3;
}

.card-row { display: flex; justify-content: space-between; font-size: 13px; margin: 3px 0; }
.card-row .route-arrow { color: var(--muted); }
.card-row .k { color: var(--muted); }

.card-row.no-ship-info { color: var(--c-no-ship); font-weight: 700; justify-content: flex-start; gap: 6px; }

/* Movement Sheet cards read more like a compact activity log than a
   schedule card -- slightly smaller text throughout for a cleaner look. */
.movement-card .card-door { font-size: 15px; }
.movement-card .card-row { font-size: 12px; margin: 2px 0; }
.card-trailer { font-weight: 600; margin-bottom: 4px; }
.card-divider { border-top: 1px dashed var(--border); margin: 8px 0 6px; }

.quick-actions { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }
.quick-btn {
  flex: 1; min-width: 64px; min-height: 34px; padding: 6px 10px;
  border: none; border-radius: 8px; font-weight: 700; font-size: 13px;
  color: #fff; cursor: pointer;
}
.quick-btn.plug { background: var(--c-plugged_in); }
.quick-btn.call { background: #64748b; }
.quick-btn.pulled { background: var(--c-pulled); }
.quick-btn.shuttle { background: var(--c-shuttled); }
.quick-btn.realign { background: var(--c-realign); }
.quick-btn.open-doors { background: var(--c-open-doors); }
.quick-btn.door-available { background: var(--c-door-available); }
.quick-btn.verify-temp { background: var(--c-verify-temp); }
.card-row.shuttle-info { color: var(--c-shuttled); font-weight: 700; }
.card-row.realign-info { color: var(--c-realign); font-weight: 700; }
.card-row.open-doors-info { color: var(--c-open-doors); font-weight: 700; }
.card-row.door-avail-info { color: var(--c-door-available); font-weight: 700; }
.card-row.verify-temp-info { color: var(--c-verify-temp); font-weight: 700; }
.card-row.assign-info { color: var(--c-assign); font-weight: 700; justify-content: flex-start; gap: 6px; }

.quick-btn:active { filter: brightness(0.85); }
.quick-btn-sm { flex: none; min-width: 0; min-height: 0; padding: 5px 12px; font-size: 12px; white-space: nowrap; }

/* Bottom-left "Assign"/"Reassign" (see openAssignModal in app.js) and
   bottom-right Undo (see undoBtnHtml/undoLastAction) share one row at
   the very bottom of the card, under "By" -- per the user's request,
   Assign now looks like Undo instead of a plain text link: same small
   muted outlined pill, same weight, so neither one competes for
   attention with the card's actual next-action button above. Flex, not
   text-align/floats -- .undo-btn's own margin-left: auto pushes it to
   the right edge on its own, whether or not assignBtn is present
   alongside it (a single flex child with an auto margin still gets
   pushed all the way to that edge), so Assign just falls in naturally
   at the left with no margin rule of its own needed. */
.card-bottom-actions { display: flex; align-items: center; margin: 8px 0 0; }
.card-assign-btn, .undo-btn {
  background: none; border: 1px solid var(--border); border-radius: 6px;
  color: var(--muted); font-size: 11px; font-weight: 600;
  padding: 3px 8px; cursor: pointer;
}
.card-assign-btn:hover, .undo-btn:hover { color: var(--text); border-color: var(--muted); }
.undo-btn { margin-left: auto; }

.table-wrap { overflow-x: auto; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); }
table { border-collapse: collapse; width: 100%; font-size: 13px; white-space: nowrap; }
/* Door Req./Door Avail./Response columns only mean anything for a site
   with track_door_availability on (Mixing Center) -- always in the DOM,
   just hidden everywhere else via this class on #logTable (toggled in
   applyRoleVisibility()) rather than conditionally building the row HTML. */
.door-avail-col { display: none; }
#logTable.tracks-door-availability .door-avail-col { display: table-cell; }
th, td { padding: 7px 10px; text-align: left; border-bottom: 1px solid var(--border); }
thead th { position: sticky; top: 0; background: var(--panel); color: var(--muted); font-weight: 600; }
tbody tr { cursor: pointer; }
tbody tr:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }

/* Team tab (see loadTeamTab in app.js) -- small colored dot ahead of the
   Online/Offline text, same green already used for a "good" stat tile/geofence
   test result elsewhere in this app. */
.presence-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  margin-right: 6px; vertical-align: middle;
}
.presence-dot.online { background: #0ca30c; box-shadow: 0 0 0 3px color-mix(in srgb, #0ca30c 20%, transparent); }
.presence-dot.offline { background: var(--border); }

/* Direct messages (see openMessagesModal in app.js) -- a fixed-height chat
   panel, not the auto-growing-with-content shape most other modals in this
   app use, so the reply box stays pinned at the bottom and only the
   message list itself scrolls. */
.messages-modal {
  max-width: 480px; height: 620px; max-height: 85vh;
  display: flex; flex-direction: column; overflow: hidden;
}
.messages-view { flex: 1; display: flex; flex-direction: column; min-height: 0; padding: 14px 18px; }
.messages-view.hidden { display: none; }

.messages-new-thread-row { margin-bottom: 10px; flex: none; }
.messages-new-thread-row select {
  width: 100%; padding: 8px 10px; font-size: 13px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--bg); color: var(--text);
}
.messages-new-thread-row.hidden { display: none; }
.messages-thread-list { flex: 1; overflow-y: auto; }
.messages-thread-row {
  display: flex; align-items: center; gap: 10px; padding: 10px 6px;
  border-bottom: 1px solid var(--border); cursor: pointer; border-radius: 8px;
}
.messages-thread-row:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }
.messages-thread-meta { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.messages-thread-name { font-weight: 700; font-size: 13px; }
.messages-thread-preview { font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.messages-thread-time { font-size: 11px; color: var(--muted); flex: none; }

.messages-thread-body { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; padding: 4px 0 10px; }
.message-bubble { max-width: 78%; padding: 8px 12px; border-radius: 14px; font-size: 13px; line-height: 1.4; white-space: pre-wrap; }
.message-bubble.mine { align-self: flex-end; background: var(--accent); color: #fff; border-bottom-right-radius: 4px; }
.message-bubble.theirs { align-self: flex-start; background: var(--bg); border: 1px solid var(--border); border-bottom-left-radius: 4px; }
.message-bubble .message-time { display: block; margin-top: 4px; font-size: 10px; opacity: .7; }

.messages-reply-form { display: flex; gap: 8px; align-items: flex-end; flex: none; padding-top: 10px; }
.messages-reply-form textarea {
  flex: 1; resize: none; padding: 8px 10px; font-size: 13px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--bg); color: var(--text); font-family: inherit;
}

/* Assign Move (see openAssignModal in app.js) -- small single-field form,
   not the messages-modal's fixed-height chat shape. */
.assign-modal { max-width: 400px; }
.assign-modal form { padding: 16px 18px; }
.assign-modal label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--muted); margin-top: 6px; }
.assign-modal select {
  padding: 8px 10px; font-size: 13px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--bg); color: var(--text);
}

/* Incoming message popup (see queueIncomingMessagePopup in app.js) -- a
   tinted header (same purple as the message alert-banner/Assign badge)
   makes it read as distinct from an ordinary modal at a glance, matching
   its "make sure this actually gets seen" purpose. */
.incoming-message-modal { max-width: 420px; }
.incoming-message-modal .modal-header { background: color-mix(in srgb, var(--c-assign) 14%, transparent); }
.incoming-message-body { padding: 16px 18px; }
.incoming-message-body p { font-size: 14px; line-height: 1.5; margin: 0 0 14px; white-space: pre-wrap; }
.incoming-message-body textarea {
  width: 100%; resize: none; padding: 8px 10px; font-size: 13px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--bg); color: var(--text); font-family: inherit; box-sizing: border-box;
}
.incoming-message-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 10px; }

/* Unread count next to the header's ✉ Messages button and (reused) the
   Team tab's presence-derived state -- small red pill, hidden at 0. */
.messages-btn { position: relative; }
.unread-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; padding: 0 4px; margin-left: 4px;
  background: #dc2626; color: #fff; border-radius: 999px; font-size: 10px; font-weight: 700;
}
.unread-badge.hidden { display: none; }

/* Modal */
.modal-overlay {
  display: none; position: fixed; inset: 0; background: rgba(0,0,0,.45);
  align-items: center; justify-content: center; z-index: 50; padding: 16px;
}
.modal-overlay.open { display: flex; }
.modal {
  background: var(--panel); border-radius: var(--radius); width: 100%; max-width: 640px;
  max-height: 90vh; overflow-y: auto; border: 1px solid var(--border);
}
.modal-header { display: flex; justify-content: space-between; align-items: center; padding: 14px 18px; border-bottom: 1px solid var(--border); }
.modal-header h2 { margin: 0; font-size: 16px; }

/* Schedule ("everyday plan") editor -- a real table of labeled fields
   instead of raw tab-separated text, so a non-technical user can add/edit/
   remove one row at a time. Wider than the standard modal since it has ten
   columns; the table itself scrolls horizontally on a narrow screen rather
   than the whole page (same .table-wrap pattern the Log tab already uses). */
.schedule-modal { max-width: 980px; }
.schedule-table-wrap { margin-bottom: 8px; max-height: 46vh; }
.schedule-table { font-size: 13px; white-space: normal; }
.schedule-table th { padding: 6px 5px; position: sticky; top: 0; background: var(--panel); }
.schedule-table td { padding: 4px 5px; }
.schedule-table input {
  width: 100%; min-width: 64px; padding: 6px 7px; font-size: 13px;
  border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text);
  font-family: inherit;
}
.schedule-table input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.schedule-table td:first-child input, .schedule-table td:nth-child(2) input { min-width: 78px; }
.schedule-table td:last-child { text-align: center; }
.schedule-table .remove-schedule-row {
  border: none; background: transparent; color: var(--muted); font-size: 18px;
  line-height: 1; padding: 2px 6px; cursor: pointer; border-radius: 6px;
}
.schedule-table .remove-schedule-row:hover { color: #ef4444; background: color-mix(in srgb, #ef4444 12%, transparent); }
#addScheduleRowBtn { margin-bottom: 10px; }

/* Power-user fallback -- collapsed by default so the plain table above is
   what everyone sees first; pasting fills the table rather than saving
   directly, so it's still reviewable before either footer button commits it. */
.schedule-paste-advanced { margin: 14px 0; border-top: 1px solid var(--border); padding-top: 10px; }
.schedule-paste-advanced summary { cursor: pointer; font-size: 13px; color: var(--muted); font-weight: 600; }
.schedule-paste-advanced summary:hover { color: var(--text); }
.schedule-paste-advanced textarea { margin-top: 8px; }
.schedule-paste-advanced #fillFromPasteBtn { margin-top: 8px; }

@media (max-width: 640px) {
  .schedule-table-wrap { max-height: 38vh; }
}

/* Plug / Pulled / Shuttle / Log Movement popups — sized to read from the
   driver's seat with the device mounted on the dash: bigger text and tap
   targets than the regular form, but not so big the popup itself needs
   scrolling to reach the buttons (Log Movement in particular has enough
   fields that the earlier, larger sizing pushed it past one screen). */
.plug-modal {
  max-width: 480px; width: 95%;
}
.plug-modal .modal-header { padding: 14px 18px; }
.plug-modal .modal-header h2 { font-size: 19px; }
.plug-modal .modal-header .icon-btn { font-size: 24px; padding: 2px 8px; }
#plugForm, #pullForm { padding: 16px; }
.plug-modal .form-grid { gap: 12px 12px; }
.plug-modal .form-grid label { font-size: 15px; font-weight: 600; gap: 5px; }
.plug-modal .form-grid input,
.plug-modal .form-grid select,
.plug-modal .form-grid textarea {
  font-size: 16px; padding: 10px 12px; min-height: 42px; border-radius: 8px;
  font-family: inherit;
}
.plug-modal .form-grid textarea { min-height: unset; }
/* Marks a Plug/Pull popup field as required -- the input's own `required`
   attribute is what actually blocks submission (and is automatically
   skipped by the browser for a field hidden via a site's hidden_fields,
   since a display:none field is never a constraint-validation candidate),
   this is just the visible cue next to the label. */
.required-mark { color: #ef4444; font-weight: 700; }
.plug-modal .modal-footer { gap: 10px; margin-top: 14px; }
.plug-modal .modal-footer button {
  font-size: 16px; font-weight: 700; padding: 11px 18px; min-height: 44px; border-radius: 8px;
}
#entryForm { padding: 16px 18px; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 14px; }
.form-grid label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
/* .form-grid label's own specificity (class + type) outranks a lone
   .hidden class, which otherwise silently loses this fight and leaves
   the element visible -- see #staffNewAccountField in the add-employee
   form. */
.form-grid label.hidden { display: none; }
.form-grid .section-label { grid-column: 1 / -1; margin-top: 8px; font-weight: 700; color: var(--text); font-size: 12px; text-transform: uppercase; letter-spacing: .04em; border-top: 1px solid var(--border); padding-top: 10px; }
.form-grid .full-width { grid-column: 1 / -1; }
.form-grid label.checkbox-field {
  flex-direction: row; align-items: center; gap: 8px;
  font-size: 13px; font-weight: 600; color: var(--text);
  padding: 6px 8px; border-radius: 6px; cursor: pointer;
}
.form-grid label.checkbox-field:has(input:checked) {
  color: var(--c-realign); background: color-mix(in srgb, var(--c-realign) 12%, transparent);
}
.form-grid label.checkbox-field input { width: 16px; height: 16px; accent-color: var(--c-realign); cursor: pointer; flex: none; }
.form-grid label.checkbox-field .field-hint { font-weight: 400; color: var(--muted); }
/* WCD's grey-out of the Precool / Plug In / Loading & Temp sections (see
   applyRoleVisibility in app.js) -- dims the whole field, label text
   included, rather than relying only on each input's native :disabled
   look, so a greyed-out section reads as "not for you" at a glance. */
.form-grid label[data-role-section].role-locked { opacity: .5; }
.form-grid label[data-role-section].role-locked input,
.form-grid label[data-role-section].role-locked select,
.form-grid label[data-role-section].role-locked textarea {
  cursor: not-allowed; background: var(--panel);
}
.modal-footer { display: flex; align-items: center; gap: 8px; margin-top: 16px; }
.modal-footer .spacer { flex: 1; }

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

/* Sign-in -- reorganized per the user's request now that multiple
   terminals ("companies") and multiple role types are both real in
   practice, not just in theory. A brand accent bar + logo card at the
   top of both the terminal picker and the "Who's working?" screen (same
   white-card logo treatment the shift-briefing welcome popup already
   uses -- see .welcome-logo-wrap below) gives both a deliberate,
   branded first impression instead of a bare heading on white. */
.signin-overlay { z-index: 100; }
.signin-modal {
  max-width: 420px; padding: 0 24px 22px; text-align: center;
  border-top: 3px solid var(--accent);
}
.signin-modal h2 { margin: 0 0 4px; }
.signin-modal hr { border: none; border-top: 1px solid var(--border); margin: 18px 0; }
.signin-modal .form-grid { grid-template-columns: 1fr; text-align: left; }
.signin-subtitle { margin: 0 0 18px; color: var(--muted); font-size: 13px; }
#signInError { color: #ef4444; min-height: 16px; }
#pinInput { width: 100%; font-size: 22px; text-align: center; letter-spacing: 6px; padding: 10px; margin-top: 8px; }
#pinEntryName { color: var(--accent); }

/* Same white-card-on-brand-navy idea as .welcome-logo-wrap, condensed
   for the smaller sign-in modal -- logo sits on its own light card since
   the artwork bakes in a white background (see .brand-logo in the
   topbar for the same reasoning), with a bit of breathing room above the
   heading below it. */
.signin-brand {
  background: var(--brand-navy); margin: 0 -24px 18px; padding: 22px 24px 18px;
  border-radius: calc(var(--radius) - 3px) calc(var(--radius) - 3px) 0 0;
}
.signin-logo { height: 32px; width: auto; display: block; margin: 0 auto; }

/* Persistent context for which terminal this sign-in is scoped to (see
   openSignIn() in app.js for how its content is built) -- doubles as the
   way back to the terminal picker, so there's no separate "wrong
   terminal?" link anymore. A pill/badge rather than a plain link, matching
   the more deliberately-designed feel of the rest of this screen. */
.terminal-badge {
  display: inline-flex; align-items: center; gap: 6px;
  margin: 0 0 14px; padding: 6px 14px;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--bg); color: var(--text); font-size: 13px; font-weight: 600;
  cursor: pointer;
}
.terminal-badge:hover { border-color: var(--accent); }
.terminal-badge-switch { color: var(--accent); font-weight: 700; }

#employeeSelect {
  width: 100%; padding: 12px 14px; font-size: 16px; font-weight: 600;
  border-radius: var(--radius); border: 1px solid var(--border); background: var(--panel); color: var(--text);
}
#employeeSelect:hover { border-color: var(--accent); }

/* Terminal picker -- one big tappable row per terminal, building/location
   icon + name + a chevron affordance, same idea as the old site button
   grid this app used before the sign-in dropdown but with a more
   deliberate "pick a destination" list-row feel (subtle lift on hover)
   instead of a plain bordered rectangle. */
.terminal-picker-list { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.terminal-pick-btn {
  display: flex; align-items: center; gap: 12px;
  width: 100%; padding: 14px 16px; font-size: 16px; font-weight: 700;
  border-radius: var(--radius); border: 1px solid var(--border);
  background: var(--panel); color: var(--text); cursor: pointer;
  text-align: left; transition: box-shadow .12s, border-color .12s;
}
.terminal-pick-btn:hover { border-color: var(--accent); box-shadow: 0 2px 10px rgba(0,0,0,.08); }
.terminal-pick-icon { font-size: 20px; line-height: 1; }
.terminal-pick-name { flex: 1; }
.terminal-pick-chevron { color: var(--muted); font-size: 20px; line-height: 1; }
.terminal-pick-btn:hover .terminal-pick-chevron { color: var(--accent); }

/* Shift-briefing welcome popup (Ops/Yard, right after a real sign-in --
   see maybeShowWelcome() in app.js). Same z-index tier as the sign-in
   overlay since it can immediately follow it. */
.welcome-overlay { z-index: 100; }
.welcome-modal { max-width: 560px; width: 95%; padding: 0; overflow: hidden; }
.welcome-logo-wrap {
  background: #fff; display: flex; align-items: center; justify-content: center;
  padding: 16px;
}
.welcome-logo { height: 40px; width: auto; display: block; }
.welcome-header {
  background: var(--brand-navy); color: #fff; padding: 16px 22px;
  font-size: 15px; font-weight: 700; letter-spacing: .02em;
  display: flex; align-items: center; gap: 8px;
}
.welcome-brand { color: var(--brand-blue-light); }
.welcome-brand-divider { color: color-mix(in srgb, #fff 35%, transparent); }
#welcomeHeaderRole { color: #fff; }
.welcome-body { padding: 22px 24px 6px; }
.welcome-greeting { margin: 0 0 6px; font-size: 21px; }
.welcome-tagline {
  margin: 0 0 18px; font-weight: 700; color: var(--accent); font-size: 14px;
}
.welcome-before-shift {
  margin: 0 0 12px; font-weight: 700; text-transform: uppercase;
  font-size: 11px; letter-spacing: .06em; color: var(--muted);
}
.welcome-items { display: flex; flex-direction: column; gap: 14px; margin-bottom: 18px; }
.welcome-item { display: flex; gap: 12px; align-items: flex-start; }
.welcome-item-num {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent);
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 13px;
}
.welcome-item-text strong { display: block; font-size: 14px; margin-bottom: 2px; }
.welcome-item-text p { margin: 0; font-size: 13px; color: var(--muted); line-height: 1.45; }
.welcome-item-text p strong { display: inline; font-size: inherit; }
.welcome-closing {
  margin: 4px 0 20px; padding-top: 14px; border-top: 1px solid var(--border);
  font-weight: 700; text-align: center; font-size: 14px;
}
.welcome-modal .modal-footer { margin: 0; padding: 14px 24px 22px; }
.welcome-footer-brand {
  text-align: center; font-size: 11px; color: var(--muted); padding: 0 24px 18px; margin-top: -10px;
}

/* Staff management */
#staffNoTerminalNotice {
  text-align: center; padding: 28px 14px; color: var(--muted); font-size: 14px;
}
#staffNoTerminalNotice p { margin: 0 0 14px; }
.join-code-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 10px 14px; margin: 14px 0; font-size: 13px;
}
#joinCodeValue { font-family: monospace; font-size: 16px; letter-spacing: .06em; color: var(--accent); }
.join-code-row button { flex: none; white-space: nowrap; }
.staff-table { width: 100%; border-collapse: collapse; margin-bottom: 4px; }
.staff-table th, .staff-table td { text-align: left; padding: 8px 6px; border-bottom: 1px solid var(--border); font-size: 14px; }
.staff-table td:last-child { text-align: right; white-space: nowrap; }
.staff-table button { font-size: 12px; padding: 4px 8px; margin-left: 6px; }
hr { border: none; border-top: 1px solid var(--border); margin: 16px 0; }

/* Board: flat, chronological by plug time — each plug is its own card,
   sorted by time rather than grouped by door (a reused door's two plugs can
   land far apart in the list, at their own times). */
.board-subsection { margin-top: 18px; }
.board-subsection h3 { font-size: 13px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; margin: 0 0 8px; }
.board-subsection:empty, .board-subsection.hidden { display: none; }
.hidden { display: none; }

/* A site that's just a pointer to a separate tool (site.external_link_url)
   shows that tool right in the board area via this iframe instead of the
   normal board content -- see #boardContent/#externalToolPanel toggling
   in loadBoard()/renderBoard() (app.js). */
.external-tool-frame {
  width: 100%; height: calc(100vh - 160px); min-height: 480px;
  border: 1px solid var(--border); border-radius: var(--radius);
  display: block;
}

.card-time {
  font-size: 12px; font-weight: 700; color: var(--muted);
  background: color-mix(in srgb, var(--muted) 15%, transparent);
  border-radius: 12px; padding: 2px 10px; margin-left: 8px;
}
/* Store # (or whatever a site relabels it to) shown right next to the door
   number at the top of the card -- moved up from the generic details list
   below so it's visible at a glance instead of needing to scroll a card's
   full detail rows to find it. */
.card-store {
  font-size: 12px; font-weight: 700; color: #fff;
  background: var(--accent);
  border-radius: 12px; padding: 2px 10px; margin-left: 8px;
}
.card.overdue { border-left-color: #ef4444; }
.card-door.overdue-text { color: #ef4444; }
.card-door.overdue-text .card-time { color: #fff; background: #ef4444; }

/* Priority (see schema.sql/cardHtml in app.js): dispatch/ops flags this as
   the next thing the yard should act on. Same red as the danger button/
   overdue-time text elsewhere in this app, read as "urgent" -- a full-
   width ribbon at the top of the card, not just a border color, so it
   reads before anything else on the card does. Outranks the status-color
   border underneath it (still visible along the card's left edge) rather
   than replacing it, so the workflow status stays legible too. */
.card.priority { outline: 2px solid #dc2626; outline-offset: -1px; }
.card-priority-ribbon {
  background: #dc2626; color: #fff;
  font-size: 11px; font-weight: 800; letter-spacing: .03em; text-align: center;
  padding: 4px 8px; margin: -12px -14px 10px;
  border-radius: var(--radius) var(--radius) 0 0;
}

/* Realign/Open Doors/Verify Temp, while still outstanding (see
   needsAttention in app.js) -- same "jump to the front of the section,
   full-width ribbon" treatment as Priority above, per the user's
   request, just its own amber ❗ instead of Priority's red ⚡ so the two
   never look identical. Only ever shown/outlined when Priority isn't
   already showing on the same card -- see attentionBadge in cardHtml. */
.card.needs-attention { outline: 2px solid var(--c-attention); outline-offset: -1px; }
.card-attention-ribbon {
  background: var(--c-attention); color: #fff;
  font-size: 11px; font-weight: 800; letter-spacing: .03em; text-align: center;
  padding: 4px 8px; margin: -12px -14px 10px;
  border-radius: var(--radius) var(--radius) 0 0;
}

/* Mixing Center-only (see isDoorReadyToPlug in app.js): a small green
   checkmark badge right next to the door number -- not a border color --
   the moment a door actually opened up and hasn't been plugged yet, so
   "go plug this one" is unmistakable at a glance instead of something to
   notice peripherally. Doesn't touch the Plug quick-action button's own
   color. Disappears the moment it's actually plugged in. */
.card-ready-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; margin-left: 8px;
  border-radius: 50%; background: #16a34a; color: #fff;
  font-size: 12px; font-weight: 700; vertical-align: middle;
}

/* Carry-over: loads still sitting at a door (not yet pulled) from an
   earlier date, surfaced above today's own cards so a container/mixing
   load that takes more than one day to finish never quietly falls off the
   board just because the calendar rolled over. */
.carry-over-section h3 { color: #b45309; }
.carry-over-section .card { border-top: 3px solid #f59e0b; }
.card-age-badge {
  font-size: 12px; font-weight: 700; color: #fff;
  background: #f59e0b;
  border-radius: 12px; padding: 2px 10px; margin-left: 8px;
}

/* Inbound Receiving-only (see vendors.recommended_door in app.js
   cardHtml): a quiet reference note, not a status badge -- just a hint for
   whoever assigns the real door (entries.dock_door), never written there
   automatically, so it stays visually secondary rather than competing with
   the store/age badges above. */
.card-suggested-door {
  font-size: 12px; font-weight: 600; color: var(--muted);
  margin-left: 8px;
}

.empty-doors { display: flex; flex-wrap: wrap; gap: 8px; }
.empty-doors .chip {
  border: 1px dashed var(--border); border-radius: 20px; padding: 5px 12px;
  font-size: 13px; color: var(--muted); cursor: pointer; background: var(--panel);
}
.empty-doors .chip:hover { border-color: var(--accent); color: var(--accent); }
.empty-doors .chip.chip-disabled { cursor: default; opacity: .6; }
.empty-doors .chip.chip-disabled:hover { border-color: var(--border); color: var(--muted); }

.role-badge {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  padding: 1px 6px; border-radius: 10px; margin-left: 6px; background: var(--muted); color: #fff;
}
/* ops and wcd share a color -- same permission tier, two labels for it. */
.role-badge.ops, .role-badge.wcd { background: var(--accent); }
.role-badge.super_admin { background: #b45309; }
.role-badge.company_admin { background: #7c3aed; }

.field-hint { font-size: 11px; color: var(--muted); font-weight: 400; }
.default-temp-row { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.default-temp-row label { font-size: 12px; color: var(--muted); }

.format-row { display: flex; align-items: center; gap: 16px; margin-top: 12px; }
.format-row label { display: flex; align-items: center; gap: 5px; font-size: 13px; font-weight: 600; cursor: pointer; }

/* "Email this report to" -- a saved per-site list (see report_recipients in
   schema.sql), shown as removable chips inside the Report/Staff Report
   modals so "Email Report" is a single click once the list exists. */
.recipients-block { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.recipients-label { display: block; font-size: 12px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; margin-bottom: 8px; }
.recipients-chips { display: flex; flex-wrap: wrap; gap: 6px; min-height: 26px; margin-bottom: 8px; }
.recipients-empty { font-size: 12px; color: var(--muted); }
.recipient-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 6px 4px 10px;
  background: color-mix(in srgb, var(--accent) 12%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: 999px; font-size: 12px; color: var(--text);
}
.recipient-chip .remove-recipient {
  border: none; background: transparent; color: var(--muted); font-size: 15px; line-height: 1;
  padding: 0 3px; cursor: pointer; border-radius: 50%;
}
.recipient-chip .remove-recipient:hover { color: #ef4444; }
.recipients-add-row { display: flex; gap: 6px; }
.recipients-add-row input { flex: 1; padding: 7px 10px; font-size: 13px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text); }
.recipients-add-row button { padding: 7px 12px; font-size: 13px; }
.recipients-block .updated-note { margin: 6px 0 0; }

/* ---------- Admin Dashboard ---------- */
.dashboard-controls {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 16px;
}
.dashboard-controls select { padding: 6px 8px; }
.date-range-fields { display: flex; align-items: center; gap: 10px; }
.date-range-fields input[type="date"] { padding: 5px 6px; }

/* Dashboard site filter -- a small dropdown checkbox list, same popover
   look as .quick-search-results, anchored under its toggle button. */
.site-filter { position: relative; }
#dashboardSiteFilterBtn {
  padding: 6px 10px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); color: var(--text); font-size: 13px; cursor: pointer;
}
.site-filter-menu {
  position: absolute; top: 100%; left: 0; margin-top: 4px; z-index: 60;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0,0,0,.18); padding: 8px 0; min-width: 200px;
  max-height: 320px; overflow-y: auto;
}
.site-filter-menu.hidden { display: none; }
.site-filter-menu label {
  display: flex; align-items: center; gap: 8px; padding: 7px 14px;
  font-size: 13px; cursor: pointer; white-space: nowrap;
}
.site-filter-menu label:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.site-filter-menu .site-filter-divider { border-top: 1px solid var(--border); margin: 6px 0; }

/* Reports tab -- DDP/KPI checklists (plain flow, not a dropdown, since
   picking what goes in the report is the primary interaction here, not a
   secondary filter) and the save-as-template row. */
.report-checklist { display: flex; flex-wrap: wrap; gap: 6px 24px; }
.report-checklist label { display: flex; align-items: center; gap: 6px; font-size: 13px; cursor: pointer; white-space: nowrap; }
.report-kpi-grid { columns: 2; column-gap: 28px; }
.report-kpi-grid label { break-inside: avoid; margin-bottom: 7px; }
@media (max-width: 640px) {
  .report-kpi-grid { columns: 1; }
}
.report-kpi-actions { display: flex; gap: 14px; margin-bottom: 10px; }
.report-save-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.report-save-row input[type="text"] {
  padding: 7px 10px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); color: var(--text); min-width: 220px;
}

.kpi-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px; margin-bottom: 20px;
}
.stat-tile {
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 16px;
}
.stat-tile .stat-label { font-size: 12px; color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: .03em; }
.stat-tile .stat-value { font-size: 30px; font-weight: 700; margin-top: 4px; color: var(--text); }
.stat-tile.stat-alert .stat-value { color: #d03b3b; }
.stat-tile.stat-good .stat-value { color: #0ca30c; }

.dashboard-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px;
}
@media (max-width: 800px) {
  .dashboard-grid { grid-template-columns: 1fr; }
}

.dashboard-card {
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 16px 18px; margin-bottom: 16px;
}
.dashboard-card h3 { margin: 0 0 2px; font-size: 15px; }
.dashboard-card .updated-note { margin-bottom: 12px; }

/* Yard Geofence tab -- Leaflet needs an explicit height on its container
   (it doesn't fill height:auto), and its own dark-mode override since a
   raster map tile is a real photo/rendered image the app's CSS variables
   can't recolor -- the panel chrome around it still follows the theme. */
.geofence-map {
  height: 520px; border: 1px solid var(--border); border-radius: var(--radius);
  margin-bottom: 16px; background: var(--panel);
}
@media (max-width: 640px) {
  .geofence-map { height: 380px; }
}
/* Bay/door number shown directly below its box (see renderGeofenceMap in
   app.js) -- strips Leaflet's default tooltip chrome (arrow, default
   padding/shadow) and replaces it with a solid white box + black text,
   which reads far more reliably over both satellite and street tiles than
   white-text-on-outline did at small sizes. */
.geofence-bay-label {
  background: #fff !important; border: 1px solid rgba(0, 0, 0, 0.4) !important;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35) !important;
  padding: 1px 4px !important; border-radius: 3px; color: #000; font-size: 10px; font-weight: 700;
  text-shadow: none;
  pointer-events: none;
}
.geofence-bay-label::before { display: none; }

/* Shape editor (company_admin only -- see enterGeofenceEditMode in app.js).
   The bar sits between the summary line and the map, shown only while a
   shape is actively being edited. */
.geofence-edit-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: var(--radius); padding: 10px 14px; margin-bottom: 10px; font-size: 13px;
}
/* .hidden alone (single class, same specificity as .geofence-edit-bar
   above) loses to whichever of the two comes later in this file -- this
   compound selector outranks both regardless of source order, so toggling
   .hidden always actually hides the bar. */
.geofence-edit-bar.hidden { display: none; }
/* #geofenceEditBar's two rows (the normal editing controls, and the
   "Yes, delete" confirmation swapped in over them) -- same compound-
   selector-outranks-source-order reasoning as .geofence-edit-bar.hidden
   above, so toggling between them is reliable regardless of edit order. */
.geofence-edit-bar-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; width: 100%;
}
.geofence-edit-bar-row.hidden { display: none; }
/* #geofenceAddBar reuses .geofence-edit-bar's look but holds real form
   fields (Bay/Door #, Type, Zone) instead of a status line -- same
   label/input styling as the "Test a Coordinate" row below the map, for
   the two little forms on this tab to read as one family. */
.geofence-edit-bar label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
.geofence-edit-bar input, .geofence-edit-bar select {
  padding: 7px 10px; font-size: 13px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); color: var(--text);
}
.geofence-edit-bar input { width: 120px; }
/* "Keep adding" checkbox -- overrides the label-above-input column layout
   every other field in this bar uses, since a checkbox reads naturally
   inline with its own text instead. */
.geofence-add-continuous-label {
  flex-direction: row !important; align-items: center; gap: 6px !important;
}
.geofence-add-continuous-label input { width: auto; }
/* One draggable handle per corner -- a plain div-icon marker (a small
   filled circle) rather than Leaflet's default pin, since a pin's visual
   weight/shape reads as "a place," not "a grab point." White ring makes it
   findable against both dark satellite pavement and light street tiles. */
.geofence-vertex-handle {
  width: 14px; height: 14px; margin-left: -7px; margin-top: -7px;
  background: var(--accent); border: 2px solid #fff; border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(0,0,0,.4); cursor: grab;
}
.geofence-vertex-handle:active { cursor: grabbing; }
/* Smaller/hollow "add a point here" handle at each edge's midpoint (see
   rebuildGeofenceEditMidpoints in app.js) -- visually a lighter ghost of
   the full corner handle above, since it isn't a real corner yet; dragging
   it promotes it into one (matching size/color) and a fresh midpoint set
   appears on the newly split edges. */
.geofence-midpoint-handle {
  width: 10px; height: 10px; margin-left: -5px; margin-top: -5px;
  background: color-mix(in srgb, var(--accent) 55%, transparent);
  border: 2px solid #fff; border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(0,0,0,.35); cursor: copy;
}
/* Crosshair handle at a shape's center (see repositionGeofenceMoveHandle
   in app.js) -- dragging it slides the whole shape without reshaping it,
   distinct on sight from the round corner/midpoint handles above so it
   never gets mistaken for "just another corner." Drawn as a "+" from two
   overlapping bars (no image asset, and renders identically everywhere,
   unlike relying on a Unicode glyph's font support). */
.geofence-move-handle { width: 22px; height: 22px; margin-left: -11px; margin-top: -11px; cursor: move; }
.geofence-move-handle::before,
.geofence-move-handle::after {
  content: ""; position: absolute;
  background: var(--accent); box-shadow: 0 0 0 1px rgba(0,0,0,.4);
}
.geofence-move-handle::before { left: 50%; top: 2px; bottom: 2px; width: 3px; margin-left: -1.5px; }
.geofence-move-handle::after { top: 50%; left: 2px; right: 2px; height: 3px; margin-top: -1.5px; }
/* Rotate handle (see onGeofenceRotateHandleDrag in app.js) -- sits at the
   end of a thin dashed spoke off the crosshair, distinct amber color so it
   never reads as "just another corner" next to the blue vertex/move
   handles, with a ↻ glyph so its purpose is legible at a glance. */
.geofence-rotate-handle {
  width: 22px; height: 22px; margin-left: -11px; margin-top: -11px;
  background: #d97706; border: 2px solid #fff; border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(0,0,0,.4); cursor: grab;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: 14px; line-height: 1;
}
.geofence-rotate-handle:active { cursor: grabbing; }
.geofence-test-row label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
.geofence-test-row input { padding: 7px 10px; font-size: 13px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text); width: 140px; }
.geofence-test-result { margin-top: 12px; font-size: 13px; }
.geofence-test-result .gt-line { margin: 4px 0; }
.geofence-test-result .gt-yes { color: #0ca30c; font-weight: 700; }
.geofence-test-result .gt-no { color: #d03b3b; font-weight: 700; }
.geofence-test-result .gt-warn { color: #b45309; font-weight: 700; }

/* Yard Mapping tab (company_admin only) -- built mobile-first since this
   is used standing in a yard on a phone, not at a desk. */
.yard-mapping-form {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end;
}
.yard-mapping-form label {
  display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted);
}
.yard-mapping-form input, .yard-mapping-form select {
  padding: 9px 10px; font-size: 14px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); color: var(--text); min-width: 140px;
}
.yard-mapping-mark-btn {
  font-size: 16px; padding: 12px 20px; white-space: nowrap;
}
.yard-mapping-status { margin: 10px 0 0; min-height: 18px; }
.yard-mapping-map {
  height: 320px; border: 1px solid var(--border); border-radius: var(--radius);
  margin-bottom: 16px; background: var(--panel);
}
.yard-mapping-row-group { margin-bottom: 14px; }
.yard-mapping-row-group h4 { margin: 0 0 6px; font-size: 14px; }
.yard-mapping-point-row {
  display: flex; align-items: center; gap: 10px; padding: 6px 0;
  border-bottom: 1px solid var(--border); font-size: 13px;
}
.yard-mapping-point-row .ymp-type { color: var(--muted); width: 40px; flex-shrink: 0; }
.yard-mapping-point-row .ymp-id { font-weight: 700; width: 60px; flex-shrink: 0; }
.yard-mapping-point-row .ymp-zone { color: var(--muted); flex: 1; }
.yard-mapping-point-row .ymp-accuracy { color: var(--muted); width: 160px; flex-shrink: 0; text-align: right; }
.yard-mapping-export-text {
  width: 100%; box-sizing: border-box; font-family: ui-monospace, monospace; font-size: 12px;
  padding: 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text);
  resize: vertical;
}
@media (max-width: 640px) {
  .yard-mapping-map { height: 260px; }
  .yard-mapping-form input, .yard-mapping-form select { min-width: 0; width: 100%; }
  .yard-mapping-form { flex-direction: column; align-items: stretch; }
}

/* Per-site stacked status bars */
.site-status-row { display: flex; align-items: center; gap: 10px; margin: 10px 0; }
.site-status-row .site-name { width: 130px; flex-shrink: 0; font-size: 13px; font-weight: 600; }
.site-status-row .site-total { width: 34px; flex-shrink: 0; font-size: 12px; color: var(--muted); text-align: right; }
.status-bar-track {
  flex: 1; display: flex; height: 20px; border-radius: 5px; overflow: hidden;
  background: var(--bg); border: 1px solid var(--border);
}
.status-bar-seg { height: 100%; }
.status-bar-seg + .status-bar-seg { border-left: 2px solid var(--panel); }

.chart-legend { display: flex; flex-wrap: wrap; gap: 12px 16px; margin-top: 14px; }
.chart-legend .legend-item { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.chart-legend .legend-swatch { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }

/* Trend bar chart */
.trend-chart { display: flex; align-items: flex-end; gap: 6px; height: 140px; padding-top: 10px; }
.trend-col { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; }
.trend-bar { width: 100%; max-width: 28px; border-radius: 4px 4px 0 0; background: var(--accent); min-height: 2px; }
.trend-col .trend-day-label { font-size: 10px; color: var(--muted); margin-top: 6px; white-space: nowrap; }

/* Leaderboard */
.leaderboard-item { margin: 10px 0; }
.leaderboard-row { display: flex; align-items: center; gap: 10px; }
.leaderboard-row .lb-rank { width: 22px; flex-shrink: 0; font-size: 12px; font-weight: 700; color: var(--muted); text-align: right; }
.leaderboard-row .lb-name { width: 140px; flex-shrink: 0; font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.leaderboard-row .lb-track { flex: 1; background: var(--bg); border: 1px solid var(--border); border-radius: 5px; height: 20px; overflow: hidden; }
/* Filled to each person's share of the top performer's total (unchanged
   from before), but now color-coded by action type instead of one solid
   fill -- same technique as the "Today by Site" status bar (.status-bar-seg)
   just reused here for the leaderboard's own action-type legend. */
.leaderboard-row .lb-fill { height: 100%; display: flex; min-width: 2px; }
.leaderboard-row .lb-seg { height: 100%; }
.leaderboard-row .lb-seg + .lb-seg { border-left: 2px solid var(--panel); }
.leaderboard-row .lb-value { width: 30px; flex-shrink: 0; font-size: 12px; font-weight: 700; text-align: right; }
/* Per-action breakdown line under each person's bar (Plug/Load Ready/Pull/
   Shuttle/Movement) -- indented to roughly align under lb-name (rank width
   + gap). Zero-count actions are left out entirely, not shown grayed. */
.lb-breakdown { margin: 4px 0 0 32px; font-size: 12px; color: var(--muted); display: flex; flex-wrap: wrap; gap: 4px 12px; }
.lb-breakdown b { color: var(--text); font-weight: 700; }

/* ---------- Print (see printBoard()/printLog() in app.js) ---------- */
/* Screen-only placeholder -- filled in and only actually shown once
   @media print below flips it on, right before window.print() runs. A
   printed handoff sheet needs its own title/date since the on-screen site
   dropdown isn't something paper can show. Lives outside both .tab-panels
   in index.html (a sibling of #boardTab/#logTab) so either tab's print
   button can fill and show it regardless of which one is currently active. */
.print-header { display: none; }

@media print {
  /* Force a clean light palette regardless of whatever theme is active on
     screen -- dark background/light text would waste ink and look wrong
     on paper, and isn't something print-color-adjust alone fixes since
     it's the CSS variable values themselves, not just backgrounds. */
  :root { --bg: #fff; --panel: #fff; --text: #000; --border: #999; --muted: #444; }
  body { background: #fff; }
  /* Landscape gives the Log tab's wide table (15+ columns) real room to
     breathe instead of wrapping every cell to several lines -- the Board
     tab's 3-column card grid reads just as well rotated, so one page
     orientation serves both print buttons rather than needing to key off
     which tab triggered the print. */
  @page { size: landscape; }

  /* Only the Board or Log tab is ever printed (both print buttons live in
     their own tab's controls bar), and only that tab's actual content is
     worth handing off on paper -- header, nav, quick search, and either
     tab's own controls (date pickers, +New Entry, Refresh, etc.) are
     either meaningless once printed or actively wrong (a button can't be
     tapped on paper). #printHeader is the one exception, flipped back on
     below. .tab-panel's own display:none/.active rule (unscoped from any
     media query) already keeps whichever tab ISN'T active off the printed
     page entirely, with no extra rule needed here. */
  .topbar, .tabs, #quickSearchInput, .quick-search-results, .weather-bar,
  .board-controls, .log-controls, .quick-actions, .card-bottom-actions, .updated-note, #alertBanner {
    display: none !important;
  }
  .print-header { display: block; margin-bottom: 14px; }
  .print-header h1 { font-size: 20px; margin: 0 0 2px; }
  .print-header .meta { font-size: 12px; color: #444; }

  .board-grid { grid-template-columns: repeat(3, 1fr); gap: 8px; }
  .card { cursor: default; box-shadow: none !important; break-inside: avoid; }
  /* A colored background silently vanishes under most printers' default
     "skip background colors" setting, leaving white-on-white text --
     border + colored text survives that either way a badge's own
     background does not. */
  .status-badge, .card-priority-ribbon, .card-attention-ribbon, .card-store, .card-age-badge, .card-ready-badge {
    background: none !important; color: #000 !important; border: 1px solid #000;
  }

  /* Log table: neither the Next Action (one-tap quick-action buttons) nor
     the trailing edit-pencil column does anything on paper, so both are
     dropped rather than printed as dead buttons. overflow-x:auto normally
     lets .table-wrap scroll on screen, but a browser's print renderer just
     clips a scrollable container at the page edge instead of scaling or
     paginating it -- switching to visible lets the (now landscape, smaller-
     font, wrapping) table actually flow onto the page instead of losing
     its right-hand columns. */
  .log-action-col, .log-edit-col { display: none !important; }
  .table-wrap { overflow-x: visible; max-height: none !important; border: none; }
  #logTable { font-size: 10px; white-space: normal; }
  #logTable th, #logTable td { padding: 4px 5px; }
}
