/* ── Reset & Base ───────────────────────────────────────────── */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; overflow: hidden; font-family: 'Oxanium', 'Manrope', 'Segoe UI', system-ui, -apple-system, sans-serif; background: #0a0e17; color: #e2e8f0; }
/* From Uiverse.io by Deri-Kurniawan */


button {
  position: relative;
  overflow: hidden;
  border: none;
  color: #fff;
  display: inline-block;
  font-size: 15px;
  line-height: 15px;
  padding: 18px 18px 17px;
  text-decoration: none;
  cursor: pointer;
     isolation: isolate;        
  background: #163880;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
}

button span:first-child {
  position: relative;
  transition: color 800ms cubic-bezier(0.48, 0, 0.12, 1);
  z-index: 10;
}
button:hover {
  color: #14587F;
}

button:after {
  content: "";
  position: absolute;
  bottom: -50%;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #071831;
  transform-origin: bottom center;
  transition: transform 600ms cubic-bezier(0.48, 0, 0.12, 1);
  transform: scaleY(0);
  z-index: 1;
}

button:hover:after {
  transform-origin: bottom center;
  transform: scaleY(2);
}

/* 8px corners on every element using the .button class (matches the
   .aoi-item rounding so list rows and the action button it sits beside
   read as one family). Inner ::after overlay also clipped to the same
   radius so the skew animation stays inside the rounded shape. */
.button {
  border-radius: 8px;
}
.button:after {
  border-radius: 8px;
}

/* Greyscale variant — use on Cancel / dismissive actions so they visually
   recede next to the primary navy button. Only colours shift; the skew
   animation and timing are inherited from the base `button` rule. */
button.animated-button-cancel {
  background: #3f3f46;            /* zinc-700 — muted neutral grey */
  color: #e5e5e5;                  /* zinc-200 — soft off-white label */
}
button.animated-button-cancel:hover {
  color: #a1a1aa;                  /* zinc-400 — label recedes on hover */
}
button.animated-button-cancel:after {
  background-color: #18181b;       /* zinc-900 — deep grey overlay */
}



.blue-glow {
  width: 46px;
  height: 31px;
  border-radius: 11px;
  cursor: pointer;
  transition: 0.3s ease;
  background: linear-gradient(
    to bottom right,
    #2e8eff 0%,
    rgba(46, 142, 255, 0) 30%
  );
  background-color: rgba(46, 142, 255, 0.2);
  display: flex;
  align-items: center;
  justify-content: center;
}

.blue-glow:hover,
.blue-glow:focus {
  background-color: rgba(46, 142, 255, 0.7);
  box-shadow: 0 0 10px rgba(46, 142, 255, 0.5);
  outline: none;
}

/* Active state — marks the last-clicked userbar button. The inner panel
   adopts the icon's blue (rgb 96 165 250) at 30% so the icon reads as
   "currently selected" without losing legibility. JS adds .active on
   click in #user-bar; only one button carries it at a time. */
#user-bar .blue-glow.active .blue-glow-inner {
  background-color: rgba(96, 165, 250, 0.3);
}

.blue-glow-inner {
  width: 43px;
  height: 28px;
  border-radius: 10px;

  background-color: #1a1a1a;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 11px;
  font-size: 0.75em;
  color: rgba(96, 165, 250, 1);
  font-weight: 600;
}



/* Dev-only CSS hot-reload — now lives inside the click-recorder pill as
   another .rec-btn variant. See `.ui-click-recorder-indicator .rec-css-reload`
   below for the styling. */






@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}



/* ── Dev-only: UI click-recorder indicator (see AGENTS.md) ──── */
/* The hotspot is NOT a DOM element — it's a geometric region in the
   bottom-right, detected via mousemove in app.js. This keeps the corner
   clickable (no invisible overlay) and means modal close buttons elsewhere
   are never accidentally swallowed. */
.ui-click-recorder-indicator {
    position: fixed;
    bottom: 12px;
    right: 12px;
    z-index: 100000;
    background: rgba(239, 68, 68, 0.95);
    color: #fff;
    padding: 6px 12px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
    user-select: none;
}
.ui-click-recorder-indicator .dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #fff;
    animation: ui-click-recorder-pulse 1s ease-in-out infinite;
}
.ui-click-recorder-indicator .rec-label {
    margin-right: 4px;
}
.ui-click-recorder-indicator .rec-btn {
    background: rgba(255, 255, 255, 0.2);
    border: none;
    color: #fff;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    font-size: 11px;
    font-weight: 700;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    line-height: 1;
    transition: background 0.15s;
}
.ui-click-recorder-indicator .rec-btn:hover { background: rgba(255, 255, 255, 0.35); }
.ui-click-recorder-indicator .rec-stop   { background: rgba(34, 197, 94, 0.7); }
.ui-click-recorder-indicator .rec-stop:hover   { background: rgba(34, 197, 94, 0.9); }
.ui-click-recorder-indicator .rec-cancel { background: rgba(0, 0, 0, 0.3); }
.ui-click-recorder-indicator .rec-cancel:hover { background: rgba(0, 0, 0, 0.55); }
.ui-click-recorder-indicator .rec-css-reload { background: rgba(59, 130, 246, 0.55); }
.ui-click-recorder-indicator .rec-css-reload:hover { background: rgba(59, 130, 246, 0.85); }
.ui-click-recorder-indicator .rec-css-reload i { font-size: 10px; }
.ui-click-recorder-indicator .rec-css-reload.flash {
    background: rgba(59, 130, 246, 1);
    box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.35);
}

/* Hover highlight while recording — dashed white outline with a black glow */
.ui-click-recorder-hover {
    outline: 3px dashed #fff !important;
    outline-offset: 2px;
    box-shadow: 0 0 12px 3px rgba(0, 0, 0, 0.7) !important;
    cursor: crosshair !important;
}
/* Persistent capture highlight — green, stays until the row is deleted or
   the session is cancelled. Declared AFTER .ui-click-recorder-hover so its
   outline colour wins when both classes co-exist briefly. */
.ui-click-recorder-captured {
    outline: 3px dashed #22c55e !important;
    outline-offset: 2px;
    box-shadow: 0 0 10px 2px rgba(34, 197, 94, 0.45) !important;
}
/* Captured-items panel — floats above the REC indicator in bottom-right.
   Each row shows [UI:N] (click-to-copy), the element descriptor, and a
   cascading-delete × button. */
.ui-click-recorder-items {
    position: fixed;
    bottom: 52px;
    right: 12px;
    z-index: 100000;
    max-width: 360px;
    max-height: 50vh;
    overflow-y: auto;
    display: flex;
    flex-direction: column-reverse;
    gap: 4px;
    padding: 6px;
    background: rgba(15, 23, 42, 0.92);
    border: 1px solid rgba(34, 197, 94, 0.35);
    border-radius: 8px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
}
.ui-click-recorder-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 6px 3px 4px;
    background: rgba(12, 17, 30, 0.85);
    border-radius: 4px;
    font-size: 11px;
    color: #e2e8f0;
}
.ui-click-recorder-item .uic-row-label {
    background: rgba(34, 197, 94, 0.18);
    color: #86efac;
    border: 1px solid rgba(34, 197, 94, 0.45);
    font-family: 'SF Mono', Monaco, Consolas, monospace;
    font-size: 10px;
    font-weight: 700;
    padding: 2px 6px;
    border-radius: 4px;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s;
}
.ui-click-recorder-item .uic-row-label:hover {
    background: rgba(34, 197, 94, 0.32);
}
.ui-click-recorder-item .uic-row-desc {
    flex: 1;
    font-family: 'SF Mono', Monaco, Consolas, monospace;
    color: #7dd3fc;
    font-size: 11px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.uic-row-del {
    background: rgba(239, 68, 68, 0.35);
    border: none;
    color: #fff;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    font-size: 10px;
    font-weight: 700;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    line-height: 1;
    transition: background 0.15s;
}
.uic-row-del:hover {
    background: rgba(239, 68, 68, 0.7);
}
/* Post-click descriptor pill — shows what we identified */
.ui-click-recorder-pill {
    position: fixed;
    z-index: 100001;
    background: rgba(15, 23, 42, 0.96);
    color: #7dd3fc;
    font-family: 'SF Mono', Monaco, Consolas, monospace;
    font-size: 11px;
    font-weight: 600;
    padding: 4px 9px;
    border-radius: 4px;
    border: 1px solid rgba(56, 189, 248, 0.5);
    pointer-events: none;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
    white-space: nowrap;
    animation: ui-click-recorder-pill-in 0.18s ease-out;
}
@keyframes ui-click-recorder-pill-in {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes ui-click-recorder-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.25; }
}

#map { position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: 1; }

/* ── Glass-morphism Panel ──────────────────────────────────── */
.glass-panel {
    background: rgba(0, 0, 0, 0.28);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;

    color: #e2e8f0;
    padding: 16px;
    max-height:1000px;
}

.glass-panel-buoy{
    background: rgba(0, 0, 0, 0.58);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(34px);
    border: 1px solid rgba(255, 255, 255, 0.08);
       border-radius: 12px;

    max-height:1000px;
}

.glass-panel-dark{
    background: rgb(13 13 13 / 77%);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border: 1px solid rgba(255, 255, 255, 0.08);
       border-radius: 12px;
        padding: 16px;
    max-height:1000px;
}



/* ── Logo ──────────────────────────────────────────────────── */
#logo-container {
    position: fixed;
    top: 16px;
    left: 16px;
    z-index: 1001;
}
#logo-container img {
    height: 48px;
    filter: drop-shadow(0 2px 8px rgba(0,0,0,0.6));
}

/* ── Side Panels ───────────────────────────────────────────── */
.side-panel {
    position: fixed;
    top: 100px;
    width: 290px;
    max-height: calc(100vh - 160px);
    overflow-y: auto;
    z-index: 1000;
}
.left-panel { left: 16px; }
.right-panel { right: 16px; }

/* Collapse toggle — pinned to the right edge of the date-display row.
   Margin-left:auto pushes it past the date pill's text/icon. The global
   <button> skew/wave animation is suppressed so this reads as a simple
   chevron control, not a hero CTA. */
.left-panel-toggle {
    margin-left: auto;
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    padding: 0;
    background: transparent;
    border: none;
    color: inherit;
    cursor: pointer;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, color 0.15s;
    isolation: auto;
    overflow: visible;
    line-height: 1;
}
.left-panel-toggle::after { content: none; display: none; }
.left-panel-toggle:hover {
    background: rgba(255, 255, 255, 0.12);
    color: #ffffff;
}
.left-panel-toggle i {
    font-size: 14px;
    transition: transform 0.3s ease;
}

/* Slide the entire left panel off-screen, leaving just enough on the
   right edge for the toggle button to remain clickable. */
.side-panel.left-panel {
    transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
.side-panel.left-panel.left-panel--collapsed {
    transform: translateX(calc(-100% + 50px));
}
.side-panel.left-panel.left-panel--collapsed .left-panel-toggle i {
    transform: rotate(180deg);
}

/* Right panel houses two siblings: scrollable buoys section on top,
   AOI carousel pane on bottom. The pane needs overflow:visible so
   the carousel's previous/next neighbour cards can extend left of
   the panel into the map area — that means the whole right-panel
   has to be overflow:visible too (CSS quirk: any overflow != visible
   on one axis forces the other to auto). The buoys child gets its
   own scroll context so the existing list still scrolls cleanly. */
.side-panel.right-panel {
    display: flex;
    flex-direction: column;
    gap: 12px;
    overflow: visible;
}
.right-panel-buoys {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}
.side-panel::-webkit-scrollbar { width: 4px; }
.side-panel::-webkit-scrollbar-track { background: transparent; }
.side-panel::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 2px; }

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

.module-menu {
    padding: 0;
    margin-bottom:10px;

}
.panel-section { margin-bottom: 16px; }
.panel-section:last-child { margin-bottom: 0; }

.panel-section-title {
    font-weight: 600;
    font-size: 13px;
    letter-spacing: 1.2px;
    color: rgba(255, 255, 255, 0.71);
    margin-bottom: 8px;
}
.panel-empty-state {
    font-weight: 600;
    letter-spacing: 1.5px;
    color: rgba(255, 255, 255, 0.71);
    text-align: center;
    padding: 16px 8px;
    font-size: 12px;
}

.collapsible-toggle {
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    user-select: none;
    margin-bottom: 0;
}
.collapsible-toggle::after {
    content: '\25B6';
    font-size: 8px;
    color: #94a3b8;
    transition: transform 0.2s ease;
}
.collapsible.open > .collapsible-toggle::after {
    transform: rotate(90deg);
}
.collapsible-body {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.25s ease;
}
.collapsible.open > .collapsible-body {
    max-height: 500px;
    margin-top: 8px;
}

/* ── Mode Badge ────────────────────────────────────────────── */
.mode-badge {
    display: inline-block;
    padding: 4px 12px;
    border-radius: 20px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 1.2px;
    text-transform: uppercase;
}
.mode-observation { background: #1d4ed8; color: #dbeafe; }
.mode-forecast {
    background: #dc2626;
    color: #fecaca;
    animation: pulse-forecast 1.5s ease-in-out infinite;
}
@keyframes pulse-forecast {
    0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.4); }
    50% { opacity: 0.85; box-shadow: 0 0 12px 4px rgba(220, 38, 38, 0.2); }
}

/* Events panel sits transparent over the map — no glass-panel background
   or border. Mode state now lives on each event-card pill. */
#events-panel {
    background: transparent !important;
    border: none !important;
    box-shadow: none !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
}
#events-panel.forecast-mode {
    background: transparent !important;
    border: none !important;
    box-shadow: none !important;
}

/* ── AI Slide-out Panel ────────────────────────────────────── */
.ai-panel-overlay {
    display: none !important;
}
.ai-slide-panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: 33vw;
    max-width: 450px;
    min-width: 300px;
    /* Above .modal-overlay (2000) so the AI panel always sits on top of any
       open modal (e.g. opened from the dodge-tide modal's deep-analysis
       button), not behind it. */
    z-index: 3000;
    background: rgb(12, 17, 30);
    border-left: 1px solid rgba(255, 255, 255, 0.08);
    box-shadow: -8px 0 32px rgba(0, 0, 0, 0.5);
    display: flex;
    flex-direction: column;
    transform: translateX(100%);
    transition: transform 0.3s ease;
}
.ai-slide-panel.open {
    transform: translateX(0);
}
.ai-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    flex-shrink: 0;
    /* Black background to match the #date-display .dd-date pill. */
    background: #000000;
}
.ai-panel-title {
    /* Font size + uppercase casing matched to .dd-date. */
    font-size: 15px;
    font-weight: 600;
    color: #e2e8f0;
    letter-spacing: 0.5px;
    text-transform: uppercase;
}
/* Close button in slide-panel headers — restyled to copy .left-panel-toggle:
   a bare 26×26 transparent ghost button with a subtle white hover, instead of
   the boxed .bc-btn-group look. The wrapper's dark fill + border are dropped. */
.ai-panel-header .bc-btn-group {
    background-color: transparent;
    border: none;
    border-radius: 4px;
    overflow: visible;
}
.ai-panel-header .bc-btn-group__btn {
    width: 26px;
    height: 26px;
    padding: 0;
    background: transparent;
    color: #d1d5db;
    border-radius: 4px;
    line-height: 1;
}
.ai-panel-header .bc-btn-group__btn:hover {
    background-color: rgba(255, 255, 255, 0.12);
    color: #ffffff;
}
.ai-panel-header .bc-btn-group__btn i {
    font-size: 14px;
}
.ai-panel-body {
    flex: 1;
    overflow-y: auto;
    padding: 20px;
    scrollbar-width: thin;
    scrollbar-color: rgba(100,116,139,0.3) transparent;
    background-color: #000000;       /* match .alerts-panel-body */
}
.ai-panel-body::-webkit-scrollbar { width: 4px; }
.ai-panel-body::-webkit-scrollbar-thumb { background: rgba(100,116,139,0.3); border-radius: 2px; }
.ai-panel-body::-webkit-scrollbar-track { background: transparent; }
.ai-panel-footer {
    padding: 12px 20px;
    /* Border now matches the new black footer background so it doesn't read
       as a contrasting strip above the email button. */
    border-top: 1px solid #000000;
    flex-shrink: 0;
    text-align: center;  /* centre the email button */
    background-color: #000000;       /* match .alerts-panel-footer / -body */
}
/* Legal / accuracy disclaimer rendered under every AI-generated
   summary inside the AI Analysis panel. Sits inside #ai-analysis so it
   only shows once the analysis itself is rendered (not during the
   loading spinner). Visually quieter than the analysis body so it
   reads as a footnote, with the "Disclaimer:" lead pulled out of the
   italic. */
.ai-analysis-disclaimer {
    margin-top: 16px;
    padding-top: 12px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    font-size: 11px;
    line-height: 1.5;
    color: #94a3b8;
    font-style: italic;
}
.ai-analysis-disclaimer strong {
    font-style: normal;
    color: #cbd5e1;
}
.ai-loading {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 12px;
    color: #94a3b8;
    padding: 8px 0;
}
.ai-loading .small-spinner {
    width: 16px;
    height: 16px;
    border-width: 2px;
    flex-shrink: 0;
}
#ai-analysis-content {
    font-size: 13px;
    line-height: 1.7;
    color: #cbd5e1;
}
/* Parsed "Assessment=Low/Medium/High Risk" badge — reuses the .dt-risk-badge
   .risk-* styles; this just forces it onto its own line, left-aligned (the
   quick-summary section is otherwise centred), with spacing below. Selector is
   .dt-risk-badge.dt-assessment-badge so it outranks the single-class
   .dt-risk-badge { display: inline-block } rule regardless of source order. */
.dt-risk-badge.dt-assessment-badge {
    display: block;
    width: fit-content;
    margin: 0 0 8px;
}
#ai-analysis-content p {
    margin: 0 0 10px;
}
#ai-analysis-content h1, #ai-analysis-content h2, #ai-analysis-content h3,
#ai-analysis-content h4, #ai-analysis-content h5, #ai-analysis-content h6 {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    color: #e2e8f0;
    margin: 16px 0 8px;
}
#ai-analysis-content h1:first-child, #ai-analysis-content h2:first-child,
#ai-analysis-content h3:first-child {
    margin-top: 0;
}
#ai-analysis-content strong {
    color: #e2e8f0;
}
#ai-analysis-content ul {
    margin: 4px 0 10px;
    padding-left: 18px;
}
#ai-analysis-content li {
    margin-bottom: 4px;
}
#ai-analysis-content .ai-date {
    color: #60a5fa;
    font-weight: 600;
    cursor: pointer;
}
#ai-analysis-content .ai-date:hover {
    text-decoration: underline;
}

/* New-alert footer button — global navy + skew-wave look (the base `button`
   rule), sized compact + full-width to match the buoy popup's .bmp-action.
   overflow:hidden on the base button clips the ::after wave to the radius. */
#btn-new-alert {
    display: block;
    width: 100%;
}
/* Acknowledge = the same blue pill as #btn-new-alert: .blue-pill-button in
   the markup + this block/full-width sizing. No other overrides so the two
   render identically. */
.atc-clear {
    display: block;
    width: 100%;
}
#btn-ai-panel.ai-active {
    background: rgba(96, 165, 250, 0.2);
    border-color: rgba(96, 165, 250, 0.5);
    color: #60a5fa;
}

/* ── Date Display — weather-themed banner ─────────────────────
   The container reads like a compact weather-app card. A `wx-*` class
   picks the scene; CSS-only animations (sun pulse, drifting clouds,
   falling rain, lightning flash, snow drift, fog wipe) sell the mood.
   Randomised on map clicks via a delegated handler in app.js. */
.date-display {

    position: relative;
    overflow: hidden;

    padding: 14px 16px;
    margin-bottom: 12px;
    border-radius: 10px;
    /*box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06),*/
    /*            0 4px 14px rgba(0, 0, 0, 0.35);*/
    /* Column-stack so the date sits on top and the slider (when visible)
       lives directly below it inside the same card. */
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 10px;
    font-size: 22px;
    font-weight: 700;
    letter-spacing: 0.6px;
    color: #f8fafc;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
    /* Text sits above the absolute-positioned decorations. */
    isolation: isolate;
    transition: background 0.5s ease, color 0.5s ease;
}
/* Date pill — same shape language as .event-label (dark slate bg,
   white uppercase text, rounded pill) but bumped to 15px (between
   .event-label's 10px and the date-display's old 22px). Inline-flex
   so the leading FA calendar icon sits inline with the text;
   align-self: flex-start prevents the column-flex parent from
   stretching the pill to full width. */
.date-display .dd-date {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    /* +2px each side from previous (4 12 → 6 14). */
    padding: 6px 14px;
    border-radius: 5px;
    /* Dead-black for the leading 75% of the pill, then linear fade to
       transparent across the trailing 25% on the right. */
    background: #000000;
    color: #ffffff;
    font-size: 15px;
    font-weight: 500;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    line-height: 1.2;
    text-shadow: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.date-display .dd-date i {
    font-size: 12px;
    opacity: 0.85;
    flex-shrink: 0;
}
/* Date text is the shrink target so the "Today" pill + collapse toggle
   never get clipped on narrow panels — the date ellipsizes instead. */
.date-display .dd-date-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Date value — moved out of the pill onto its own line, between the
   "AI INSIGHTS" pill header and the timeline slider. */
.date-display .dd-date-value {
    font-size: 15px;
    font-weight: 600;
    letter-spacing: 0.6px;
    color: #f8fafc;
    text-transform: uppercase;
    line-height: 1.2;
    text-align: center;
}
.date-display .dd-date-value:empty { display: none; }
/* Muted "FOR" prefix on the date value, e.g. "FOR TODAY". */
.date-display .dd-date-value .dd-for { opacity: 0.5; }
/* Contextual "Back to Today" pill — centered underneath the timeline slider
   inside the date-display card; only rendered (display toggled in JS) when
   the scrubber is off today. Styled to match the
   .mode-badge.mode-badge-inline.days-badge label (dark solid fill, light
   uppercase text) so it reads as part of the same pill family. */
.dd-today-snap {
    flex: 0 0 auto;
    align-self: center;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 12px;
    border-radius: 10px;
    border: none;
    background: rgb(12, 17, 30);
    color: #cbd5e1;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    line-height: 1;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s, color 0.15s;
    /* Suppress the global <button> skew/wave fill so the pill stays solid. */
    isolation: auto;
    overflow: visible;
}
.dd-today-snap::after { content: none; display: none; }
.dd-today-snap:hover {
    background: rgb(20, 28, 46);
    color: #e2e8f0;
}
.dd-today-snap i {
    font-size: 10px;
    opacity: 1;
}
/* Slider row lives inside the weather card; keep its own wrap styling
   intact so the thumb positioning calculations don't need to change. */
.date-display .dd-slider-wrap {
    padding: 0 2px;
}
.date-display .dd-slider-wrap .timeline-slider-wrap {
    margin-bottom: 0;
}
/* Keep any ::before/::after decorations behind the text. With `isolation:
   isolate` above, a z-index of -1 stays inside the card's stacking
   context so the date text (normal-flow inline content) paints above. */
.date-display::before,
.date-display::after {
    pointer-events: none;
    z-index: -1;
}
.date-display > * { position: relative; z-index: 1; }

/* Reuse the login-loader wave animation as an ambient background for the
   date banner. Same keyframe (bc_waves), same per-wave colour + duration
   palette, sized down to fill the banner's clipped area. z-index: 0 keeps
   them behind the date text/slider (which sit at z-index: 1 via the
   `.date-display > *` rule above). The outer banner already has
   overflow: hidden + isolation: isolate so the oversized waves clip and
   stay inside their own stacking context. */
.date-display > .bc_wave {
    position: absolute;
    left: -40%;
    width: 360px;
    height: 360px;
    background-color: #002c55;
    border-radius: 40%;
    animation-name: bc_waves;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
    pointer-events: none;
    z-index: 0;
}
.date-display > .bc_wave.one   { animation-duration: 10000ms; opacity: 0.4; }
.date-display > .bc_wave.two   { animation-duration: 12000ms; background-color: #00a3e5; }
.date-display > .bc_wave.three { animation-duration: 15000ms; }


/* ── Event Cards ───────────────────────────────────────────── */
/* TEMP: side-panel event cards hidden while experimenting with an
   alternative event presentation. Leaflet popups (e.g. the bloom-event
   popup that opens on a polygon click) are left untouched so the on-map
   card still renders. Remove this rule to restore the side-panel cards. */
#events-list .event-card,
#dodge-tides-list .event-card { display: none !important; }
.event-card {
    padding: 10px 12px;
    margin-bottom: 6px;
    cursor: pointer;
    /* Shell container only — no border or background on the card div itself
       (just padding + layout). */
    background: transparent;
    border: none;
    transition: all 0.2s ease;
    /* Stacked rows (header, intensity bar, stats, mode row). Absolute-positioned
       wave children in dodge-tide cards are unaffected. `gap` is the primary
       row-spacer; margin-top on children is a belt-and-braces backup for any
       child whose layout mode wouldn't respect gap. */
    display: flex;
    flex-direction: column;
    gap: 14px;
}
/* 3px top margin on every row inside an event-card (stacks with gap to total ~7px). */
.event-card > * {
    margin-top: 3px !important;
}
.event-card > *:first-child {
    margin-top: 0 !important;
}
/* Bloom polygon click opens an event-card inside a Leaflet popup — strip
   the default popup chrome so the card's own styling comes through. */
/* Wrapper re-skinned to mirror the #date-display .glass-panel card. */
.bloom-event-popup .leaflet-popup-content-wrapper,
.bloom-event-popup .leaflet-popup-tip {
    background: rgba(0, 0, 0, 0.28);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    color: #e2e8f0;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}
.bloom-event-popup .leaflet-popup-content {
    margin: 0;
    min-width: 220px;
    /* Slightly more top inset than sides/bottom — without the header row the
       name pill was sitting a touch high against the popup's top edge. */
    padding: 16px 16px 14px;
    /* Leaflet forces its own font on .leaflet-container; restore the app
       font (Oxanium) so the popup matches the rest of the UI. FA icons keep
       their own font via the .fa-* rules. */
    font-family: 'Oxanium', 'Manrope', 'Segoe UI', system-ui, -apple-system, sans-serif;
}

/* ── Bloom popup: replica of the #date-display card ──────────── */
.bloom-popup-card.event-card {
    display: flex;
    flex-direction: column;
    gap: 10px;
    /* No padding on the card itself — the popup content wrapper already
       provides the inset. */
    padding: 0;
}
/* Header — forensic copy of .date-display .dd-date so the popup header is
   identical to the "YOUR AI INSIGHTS MAP" pill (only the content differs). */
.bloom-popup-card .bpc-header {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 6px 14px;
    border-radius: 5px;
    background: #000000;
    color: #ffffff;
    font-size: 15px;
    font-weight: 500;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    line-height: 1.2;
    text-shadow: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Title — copy of .dd-date-text (inherits the header font). */
.bloom-popup-card .bpc-title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Close — copy of .left-panel-toggle (only the icon differs). */
.bloom-popup-card .bpc-close {
    margin-left: auto;
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    padding: 0;
    background: transparent;
    border: none;
    color: inherit;
    cursor: pointer;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, color 0.15s;
    isolation: auto;
    overflow: visible;
    line-height: 1;
}
.bloom-popup-card .bpc-close::after { content: none; display: none; }
.bloom-popup-card .bpc-close:hover { background: rgba(255, 255, 255, 0.12); color: #ffffff; }
.bloom-popup-card .bpc-close i { font-size: 14px; }
/* Name row — bloom name (natural width). */
.bloom-popup-card .bpc-name-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
}
/* Play pill now lives in the header (where the close button was): pushed
   right, uppercase/bold, and ~50% of the base blue-pill size. */
.bloom-popup-card .bpc-header .blue-pill-button {
    margin-left: auto;
    /* Pull tighter to the header's right edge. */
    margin-right: -6px;
    flex: 0 0 auto;
    text-transform: uppercase;
    font-weight: 700;
    font-size: 6px;
    padding: 2px 6px;
    border-radius: 12px;
}
/* Play icon 25% larger than the button text size. */
.bloom-popup-card .bpc-header .blue-pill-button i { font-size: 1.25em; }
/* Bloom name — exact match of the playback-panel .efb-header .event-label
   pill: #3462BB blue pill, light-blue bold text, 10px / 1px tracking,
   rounded, hugging its content (centered in the row). */
.bloom-popup-card .bpc-name {
    flex: 0 1 auto;
    display: inline-block;
    max-width: 100%;
    box-sizing: border-box;
    text-align: left;
    padding: 2px 8px;
    border-radius: 10px;
    background: #3462BB;
    color: #bfdbfe;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.bloom-popup-card .bpc-name i { font-size: 9px; opacity: 0.85; flex-shrink: 0; margin-right: 4px; }
/* Trace row — clickable text that launches per-bloom playback. Replaces the
   old play-icon button that lived in the header. Dotted underline signals
   it's actionable. */
.bloom-popup-card .bpc-trace-row {
    display: flex;
    justify-content: center;
}
.bloom-popup-card .bpc-play-trace {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    cursor: pointer;
    color: #60a5fa;
    font-size: 9px;
    font-weight: 500;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    transition: color 0.15s;
}
.bloom-popup-card .bpc-play-trace i { font-size: 7px; }
/* Dotted underline only on the words, not the leading play icon. */
.bloom-popup-card .bpc-play-trace .bpc-trace-label {
    text-decoration: underline dotted;
    text-underline-offset: 3px;
}
.bloom-popup-card .bpc-play-trace:hover { color: #93c5fd; }
/* Badges row — sits where the timeline slider was. Two equal-width pills. */
.bloom-popup-card .bpc-badges {
    display: flex;
    gap: 8px;
}
/* AGE/SIZE badges — match the aoi-module-badge pill (translucent tint +
   border), but with white text. 50% width each. High specificity so it
   beats the base .mode-badge.mode-badge-inline.days-badge fill. */
/* "Age:" / "Size:" labels — muted like the .dd-for "FOR" prefix. */
.bloom-popup-card .bpc-badges .bpc-label { opacity: 0.5; }
.event-card.bloom-popup-card .bpc-badges .days-badge {
    flex: 1 1 0;
    text-align: center;
    font-size: 11px;
    font-weight: 700;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: 0;
    color: #ffffff;
    text-shadow: 0 0 6px rgb(0 0 0 / 50%);
}
/* Actions row — sits where the "FOR TODAY" value was. */
.bloom-popup-card .bpc-actions {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
}
.bloom-popup-card .bpc-actions .ai-button { margin-top: 5px; font-size: 0.8em; }

/* ── AI button (animated rainbow-glow) ──────────────────────────
   Reusable "AI" call-to-action. Based on Uiverse button-85; keyframes
   are namespaced and the global <button> skew ::after is neutralised so
   it doesn't fight the inner #222 fill. */
.ai-button {
    padding: 0.6em 2em;
    border: none;
    outline: none;
    color: #ffffff;
    background: #111;
    cursor: pointer;
    position: relative;
    z-index: 0;
    border-radius: 10px;
    user-select: none;
    -webkit-user-select: none;
    touch-action: manipulation;
    /* Neutralise global <button> base treatment. */
    isolation: auto;
    overflow: visible;
}
.ai-button:hover { color: #ffffff; }
.ai-button::before {
    content: "";
    background: linear-gradient(
        45deg,
        #22d3ee, #38bdf8, #3b82f6, #2563eb, #1e3a8a,
        #2563eb, #3b82f6, #38bdf8, #22d3ee
    );
    position: absolute;
    top: -2px;
    left: -2px;
    background-size: 400%;
    z-index: -1;
    filter: blur(5px);
    -webkit-filter: blur(5px);
    width: calc(100% + 4px);
    height: calc(100% + 4px);
    animation: ai-button-glow 20s linear infinite;
    transition: opacity 0.3s ease-in-out;
    border-radius: 10px;
}
@keyframes ai-button-glow {
    0%   { background-position: 0 0; }
    50%  { background-position: 400% 0; }
    100% { background-position: 0 0; }
}
.ai-button::after {
    z-index: -1;
    content: "";
    position: absolute;
    width: 100%;
    height: 100%;
    background: #222;
    left: 0;
    top: 0;
    /* Override the global button:after skew so the inner fill stays put. */
    bottom: auto;
    transform: none;
    border-radius: 10px;
}
.ai-button:hover::after { transform: none; }

/* ── button-50 (Polaris-style dark button) ──────────────────────
   Used for the bloom popup "play" control. ::after skew/fill from the
   global <button> rule is neutralised. */
.button-50 {
    font-family: "Inter", -apple-system, BlinkMacSystemFont, "San Francisco", "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
    box-sizing: border-box;
    font-size: .8125rem;
    font-weight: 450;
    -webkit-tap-highlight-color: transparent;
    align-items: center;
    border-radius: .5rem;
    border-style: none;
    cursor: pointer;
    display: inline-flex;
    justify-content: center;
    line-height: 1;
    margin: 0;
    min-height: 1.75rem;
    min-width: 1.75rem;
    padding: .375rem .75rem;
    text-align: center;
    transition: background-color .075s cubic-bezier(.19, .91, .38, 1), box-shadow .075s cubic-bezier(.19, .91, .38, 1);
    user-select: none;
    background: #303030;
    border-color: transparent;
    border-width: 0;
    box-shadow: 0 .1875rem .0625rem -.0625rem rgba(26, 26, 26, .07);
    color: #fff;
    position: relative;
    isolation: auto;
    overflow: visible;
}
.button-50__Content {
    align-items: center;
    display: flex;
    gap: 6px;
    font-size: .8125rem;
    justify-content: center;
    line-height: 1.25rem;
    min-height: .0625rem;
    min-width: .0625rem;
    position: relative;
    font-weight: 650;
    transition: transform 75ms cubic-bezier(.19, .91, .38, 1);
}
.button-50::after {
    border-radius: .25rem;
    bottom: -.0625rem;
    box-shadow: 0 0 0 -.0625rem #005bd3;
    content: "";
    display: block;
    left: -.0625rem;
    pointer-events: none;
    position: absolute;
    right: -.0625rem;
    top: -.0625rem;
    z-index: 1;
    /* Neutralise the global button:after skew/fill. */
    background: transparent;
    transform: none;
    width: auto;
    height: auto;
}
.button-50:hover::after { transform: none; }
.button-50::before {
    background-color: initial;
    background-image: linear-gradient(rgba(255, 255, 255, .07) 80%, rgba(255, 255, 255, .15));
    border-radius: .5625rem;
    bottom: 0;
    box-shadow: 0 .125rem 0 0 rgba(255, 255, 255, .2) inset, .125rem 0 0 0 rgba(255, 255, 255, .2) inset, -.125rem 0 0 0 rgba(255, 255, 255, .2) inset, 0 -.0625rem 0 .0625rem #000 inset, 0 .0625rem 0 0 #000 inset;
    content: "";
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
    transition: opacity 75ms cubic-bezier(.19, .91, .38, 1);
}
.button-50:hover {
    background: #1a1a1a;
    border-color: transparent;
    color: #e3e3e3;
}
.button-50:active {
    background: #1a1a1a;
    box-shadow: 0 .1875rem 0 0 #000 inset;
    color: #ccc;
}
.button-50:active .button-50__Content {
    transform: translateY(.0625rem);
}

/* ── Blue pill button ───────────────────────────────────────────
   The canonical "blue pill button" (Uiverse button-43): blue gradient,
   rounded 25px pill. Gradient syntax modernised; global <button>::after
   skew suppressed. Use the .text span pattern for the label. */
.blue-pill-button {
    -webkit-font-smoothing: antialiased;
    /* Buttons don't inherit font-family by default — inherit so the pill
       matches its surrounding text (e.g. the .event-label / app font). */
    font-family: inherit;
    font-size: 12px;
    display: inline-block;
    padding: 5px 15px;
    background: #3b88d8;
    background: linear-gradient(to bottom, #52a8e8 0%, #377ad0 100%);
    border-top: 1px solid #4081af;
    border-right: 1px solid #2e69a3;
    border-bottom: 1px solid #20559a;
    border-left: 1px solid #2e69a3;
    box-shadow: inset 0 1px 0 0 #72b9eb, 0 1px 2px 0 rgba(0, 0, 0, .3);
    text-shadow: 0 -1px 1px #3275bc;
    -webkit-background-clip: padding-box;
    background-clip: padding-box;
    color: #eeeeee;
    cursor: pointer;
    border-radius: 25px;
    isolation: auto;
    overflow: visible;
}
.blue-pill-button::after { content: none; display: none; }
.blue-pill-button:hover {
    background: #2a81d7;
    background: linear-gradient(to bottom, #3e9ee5 0%, #206bcb 100%);
    border-top: 1px solid #2a73a6;
    border-right: 1px solid #165899;
    border-bottom: 1px solid #07428f;
    border-left: 1px solid #165899;
    box-shadow: inset 0 1px 0 0 #62b1e9;
    text-shadow: 0 -1px 1px #1d62ab;
    color: #eeeeee;
}
.blue-pill-button:active {
    background: #3282d3;
    border: 1px solid #154c8c;
    border-bottom: 1px solid #0e408e;
    box-shadow: inset 0 0 6px 3px #1657b5, 0 1px 0 0 rgba(255, 255, 255, .1);
    text-shadow: 0 -1px 1px #2361a4;
}
.bloom-event-popup .event-card {
    margin: 0;
    background: transparent;
    border: none;
    cursor: default;
}
.bloom-event-popup .event-card:hover {
    background: transparent;
    border: none;
}
/* Popup close "×" button — lifted 10–15 px outside the card, circular,
   soft red (styled after the click-recorder cancel). */
.bloom-event-popup .leaflet-popup-close-button {
    top: -14px;
    right: -14px;
    width: 24px;
    height: 24px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(252, 165, 165, 0.9);
    color: #7f1d1d;
    border: 1px solid rgba(239, 68, 68, 0.7);
    border-radius: 50%;
    font-size: 14px;
    font-weight: 700;
    text-decoration: none;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
    transition: background 0.15s, transform 0.15s;
}
.bloom-event-popup .leaflet-popup-close-button:hover {
    background: rgba(239, 68, 68, 0.9);
    color: #fff;
    transform: scale(1.08);
}
/* Let the close button escape the wrapper's clipping. */
.bloom-event-popup .leaflet-popup-content-wrapper { overflow: visible; }

/* Playback popup — opens at start, lives through the run, morphs to a
   Replay + Close state when the bloom's lifespan ends. */
.event-finished-popup .leaflet-popup-content {
    min-width: 220px;
    margin: 0;
}
/* ── Bloom Playback Panel ─────────────────────────────────────
   Fixed top-center floating panel that replaces the old map-attached
   Leaflet popup for active bloom playback. Vertically aligned with
   the side panels (.side-panel { top: 100px; }). */
.bloom-playback-panel {
    position: fixed;
    top: 100px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1000;
    width: 380px;
    /* Padding sits on the panel itself; the header + day-row are direct
       children. The switchers are absolute children so the panel's
       padding box matches the visible HUD exactly (drives the mouse-
       proximity hit-test). */
    padding: 14px 18px 12px;
    /* Glass-buoy dark backdrop — same look as .glass-panel-buoy used
       on the buoy cards. */
    background: rgba(0, 0, 0, 0.58);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
}
/* Healthy spacing between the header row (bloom name + DAY counter) and
   the day-dots row beneath it. */
.bloom-playback-panel .efb-day-row {
    margin-top: 14px;
}
/* Top switcher anchored 10px above the banner; bottom switcher 10px
   below. Both transition transform/opacity so the collapse-expand is
   smooth. */
.bloom-playback-panel #animation-speed-switcher {
    position: absolute;
    left: 0;
    right: 0;
    bottom: calc(100% + 10px);
    transition: transform 0.3s ease, opacity 0.25s ease;
}
.bloom-playback-panel #playback-control-switcher {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(100% + 10px);
    transition: transform 0.3s ease, opacity 0.25s ease;
}
/* Collapsed state — switchers slide back into the banner's vertical
   space and fade out. Default for an active playback. The .is-finished
   state (set when the bloom reaches its last day) keeps them expanded
   regardless of mouse position. */
.bloom-playback-panel.is-collapsed #animation-speed-switcher {
    transform: translateY(calc(100% + 10px));
    opacity: 0;
    pointer-events: none;
}
.bloom-playback-panel.is-collapsed #playback-control-switcher {
    transform: translateY(calc(-100% - 10px));
    opacity: 0;
    pointer-events: none;
}
/* Override the default banner row alignments now that the panel is
   centered: bloom name on its own row, days pill on the right of it,
   and a giant "DAY X OF Y" headline centered underneath. */
.bloom-playback-panel .efb-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
}
.bloom-playback-panel .efb-header .event-label {
    /* Match .mode-badge.mode-badge-inline.days-badge pill styling so the
       bloom name reads as a peer of the "3 AREAS MONITORED" badge. */
    flex: 0 1 auto;
    text-align: left;
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    background: #3462BB;
    color: #ffffff;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.bloom-playback-panel .efb-header .days-badge {
    flex: 0 0 auto;
}
/* Bloom-name pin icon — matches .dt-tide-event-label in the dodge-tide
   cards so the two locations read alike. */
.bloom-playback-panel .efb-header .event-label i {
    margin-right: 4px;
    opacity: 0.85;
}
/* Days-badge pill — bloom playback "DAY X OF Y" header counter.
   Pushed all the way to the right of the header (margin-left: auto)
   with the X digit animating in via a slide-up flip on each tick. */
.bloom-playback-panel .efb-header .days-badge {
    background: #ffffff;
    color: #000000;
}
.bloom-playback-panel .efb-header .efb-day-counter {
    margin-left: auto;
    font-variant-numeric: tabular-nums;
    overflow: hidden;
}
.bloom-playback-panel .efb-header .bp-close {
    margin-left: 8px;
    flex: 0 0 auto;
}
.bloom-playback-panel .efb-header .efb-day-counter .efb-day-x {
    display: inline-block;
    min-width: 1.1em;
    text-align: center;
}
.bloom-playback-panel .efb-header .efb-day-counter .efb-day-x.flip-in {
    animation: efb-day-counter-flip 0.35s ease;
}
@keyframes efb-day-counter-flip {
    0%   { transform: translateY(70%);  opacity: 0; }
    60%  { transform: translateY(0);     opacity: 1; }
    100% { transform: translateY(0);     opacity: 1; }
}
.bloom-playback-panel .efb-header .event-label {
    color: #bfdbfe;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
}
.bloom-playback-panel .efb-day-row {
    justify-content: center;
}
.bloom-playback-panel .efb-day-large {
    font-size: 28px;
    font-weight: 800;
    letter-spacing: 1.5px;
    color: #f1f5f9;
    text-align: center;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}
.bloom-playback-panel .efb-day-large .efb-day-x {
    display: inline-block;
    min-width: 1.1em;
    text-align: center;
    color: #fbbf24;
}
.bloom-playback-panel .efb-actions {
    justify-content: center;
}
/* Hover keeps the same base colour; only opacity shifts to fully opaque so
   the card stands out from the glass panel without changing hue. */
.event-card:hover {
    background: transparent;
}
.event-card.focused {
    background: transparent;
}
.event-card.focused:hover {
    background: transparent;
}

/* Intensity section hidden for now — kept in DOM so we can bring it back
   without re-plumbing. Remove this rule to restore. */
#intensity-section { display: none !important; }

.event-card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
.event-label {
    /* Pill — same shape language as .dt-tide-event-label so bloom and
       dodge-tide cards share a consistent header chip. */
    display: inline-block;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    padding: 2px 8px;
    border-radius: 10px;
    color: #ffffff;
    background: #1a1a1a;
    text-transform: uppercase;
    animation: none;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
/* All remaining text in an event-card shares the same font + a lighter,
   more legible colour. Applied to everything inside .event-card except the
   title (.event-label). */
.event-card .event-meta,
.event-card .event-stats,
.event-card .event-stats span {
    font-family: inherit;
    font-size: 10px;
    font-weight: 500;
    color: #cbd5e1;
    letter-spacing: 0.2px;
}
#events-list {
    margin-bottom: 20px;
}
.intensity-bar-bg {
    height: 2.5px;
    border-radius: 2px;
    background: rgba(255, 255, 255, 0.06);
    overflow: hidden;
}
.intensity-bar-fill {
    height: 100%;
    border-radius: 3px;
    transition: width 0.4s ease;
}
.event-stats {
    display: flex;
    align-items: center;
    gap: 8px;
}
/* p50 + p99 stay left-aligned; the right-hand group (AI thumb + play
   thumb) is pushed to the far right. When both thumbs are present, only
   the leftmost (AI) consumes the auto-margin so play sits flush against
   it via the row's gap. */
.event-stats .event-ai-thumb,
.event-stats .event-thumb {
    margin-left: auto;
}
.event-stats .event-ai-thumb + .event-thumb {
    margin-left: 0;
}
.event-mode-row {
    display: flex;
    justify-content: flex-start;
    align-items: center;
    gap: 8px;
}
/* Inline per-event clone of the #timeline-thumb — same gold disc + FA icon,
   but strips the absolute-positioning that the main thumb uses and shrinks
   to a compact 17 px that sits next to the mode pill. Shared by the
   play/pause thumb (.event-thumb) and the sibling AI thumb
   (.event-ai-thumb). */
.timeline-thumb.event-thumb,
.timeline-thumb.event-ai-thumb {
    position: static;
    top: auto;
    left: auto;
    transform: none;
    pointer-events: auto;
    cursor: pointer;
    width: 26px;
    height: 26px;
    border-width: 1px;
}
.timeline-thumb.event-thumb i {
    font-size: 11px;
    margin-left: 1px;
}
/* AI thumb renders the literal "AI" text instead of an FA glyph; tune
   the type so the two letters sit centred and read at the same visual
   weight as the play icon. Selector is prefixed with .event-card
   .event-stats to outrank the surrounding `.event-card .event-stats
   span` rule (which would otherwise paint the text in the muted
   slate-grey of the stats badges). */
.event-card .event-stats .timeline-thumb.event-ai-thumb {
    /* Render as a text label (pill), not the round gold icon disc — the
       full "HAB AI ANALYSIS" text needs a rectangular, auto-width chip. */
    width: auto;
    height: auto;
    border-radius: 6px;
    padding: 4px 10px;
    background: linear-gradient(180deg, #fcd34d, #f59e0b);
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 0.5px;
    color: #000;
    white-space: nowrap;
}
.timeline-thumb.event-thumb:hover,
.timeline-thumb.event-ai-thumb:hover {
    box-shadow: 0 0 14px rgba(251, 191, 36, 0.7), 0 1px 6px rgba(0, 0, 0, 0.3);
}
/* Days-tracked pill — uses the shared .mode-badge.mode-badge-inline
   geometry (font, padding, border-radius) but fills with the event-card's
   own background colour at full opacity. */
.mode-badge.mode-badge-inline.days-badge {
    background: rgb(12, 17, 30);
    color: #cbd5e1;
}
/* Collapsed (non-focused) cards show only the header + intensity bar; stats
   and mode-row unhide when the card gains `.focused`. */
.event-card:not(.focused) .event-stats,
.event-card:not(.focused) .event-mode-row,
.event-card:not(.focused) .intensity-bar-bg {
    display: none;
}
/* While a bloom's lifespan is being played back via the map popup, hide
   every event card in the side-panel list — the popup owns the scene.
   JS toggles this class on/off; popup close event also removes it. */
#events-list.playback-active .event-card {
    display: none;
}
/* Inline mode pill — smaller than the old top-of-panel badge so it fits
   inside a card without dominating. */
.mode-badge.mode-badge-inline {
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    padding: 2px 8px;
    border-radius: 10px;
    animation: none;
}
.mode-badge.mode-badge-inline.mode-observation {
    background: rgba(29, 78, 216, 0.35);
    color: #bfdbfe;
}
.mode-badge.mode-badge-inline.mode-forecast {
    background: rgba(220, 38, 38, 0.3);
    color: #fecaca;
}
/* ── Dodge Tide Cards ──────────────────────────────────────── */
/* Dodge-tide card detail row — pill, same shape language as .days-badge.
   inline-flex so the pill hugs its content; align-self: flex-start stops
   the parent flex column from stretching it to full width. */
.dodge-tide-card .dodge-tide-detail {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    align-self: center;
    padding: 2px 8px;
    border-radius: 10px;
    background: transparent;
    color: #cbd5e1;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    text-transform: uppercase;
}

/* ── Dodge-tide card wave animation ─────────────────────────────
   Carbon-copy of the buoy-modal wave effect (buoy-back-waves /
   buoy-water-gradient / buoy-front-wave), rendered directly inside each
   .event-card.dodge-tide-card. The palette mirrors the compound-risk
   colours from the card's risk pill; the water level (how far up the card
   the tide fills) decreases as risk increases — a higher-risk dodge tide
   is a *lower* tide, so the water recedes. */

/* Card + "View tidal analysis" action become positioning contexts for the
   absolute-positioned wave/water layers; overflow: hidden clips to the
   rounded corners. */
.event-card.dodge-tide-card,
.dt-panel-action.risk-healthy {
    position: relative;
    overflow: hidden;
}
/* Lift the text content above the wave layers with position: relative +
   z-index: 1 so labels/badges never get obscured by the surface. */
.event-card.dodge-tide-card > .event-card-header,
.event-card.dodge-tide-card > .dodge-tide-detail,
.dt-panel-action.risk-healthy > .dt-panel-action-link,
.dt-panel-action.risk-healthy > .dt-panel-action-row {
    position: relative;
    z-index: 1;
}

/* --dt-water-level = height of the water layer, measured from card bottom.
   Lower value = less water (= higher-risk dodge tide). risk-healthy (used
   by the singleton "View tidal analysis" card when no risks are active)
   sits at a calm default water level. */
.event-card.dodge-tide-card                 { --dt-water-level: 42%; }
.event-card.dodge-tide-card.risk-low        { --dt-water-level: 42%; }
.event-card.dodge-tide-card.risk-medium     { --dt-water-level: 42%; }
.event-card.dodge-tide-card.risk-high       { --dt-water-level: 42%; }
.event-card.dodge-tide-card.risk-critical   { --dt-water-level: 42%; }
.dt-panel-action.risk-healthy               { --dt-water-level: 42%; }

/* Singleton "Healthy Tides Forecasted" card — near-opaque blue wash
   (alpha 0.9) so it reads as a solid calm-state surface. At-risk cards
   live under .event-card.dodge-tide-card.risk-* and keep their own fills. */
.dt-panel-action.risk-healthy {
    min-height: 56px;
    padding: 12px 12px 14px;
    border-radius: 8px;
    background: rgb(0 51 109 / 90%);
    border: 1px solid rgba(96, 165, 250, 0.25);
    cursor: pointer;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
}
/* Second row: just holds the AREAS MONITORED pill, centred; pill sizes
   to its own content rather than filling the row. */
.dt-panel-action.risk-healthy .dt-panel-action-row {
    display: flex;
    align-items: center;
    justify-content: center;
    align-self: stretch;
}
/* Match the bloom-popup name pill's padding + font size. */
.dt-panel-action.risk-healthy .dt-panel-action-row .days-badge {
    padding: 2px 8px;
    font-size: 10px;
}
/* Label type-only — no background, no border; just the uppercase blue text. */
.dt-panel-action.risk-healthy > .dt-panel-action-link {
    display: inline-block;
    padding: 2px 8px;
    background: transparent;
    border: none;
    color: #bfdbfe;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
}
.dt-panel-action.risk-healthy:hover > .dt-panel-action-link {
    color: #dbeafe;
}

/* Water body sits flush with the card bottom, height = --dt-water-level. */
.event-card.dodge-tide-card .dt-water-gradient,
.dt-panel-action.risk-healthy .dt-water-gradient {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: var(--dt-water-level);
    pointer-events: none;
    z-index: 0;
    border-radius: 0 0 8px 8px;
    opacity: 0.5;
}
/* Waves float on the waterline — their bottom edge meets the top of the
   gradient. Negative margin overlaps slightly so the surface reads as one
   continuous sheet of animated water. opacity: 0.7 dials the whole stack
   down by 30% across all risk levels without touching the colour values. */
.event-card.dodge-tide-card .dt-wave-back,
.event-card.dodge-tide-card .dt-wave-front,
.dt-panel-action.risk-healthy .dt-wave-back,
.dt-panel-action.risk-healthy .dt-wave-front {
    position: absolute;
    left: 0;
    right: 0;
    width: 100%;
    height: 22px;
    bottom: 0;
    margin-bottom: -12px;
    pointer-events: none;
    z-index: 0;
    opacity: 0.45;
}
/* Healthy-tides card wants a taller, more solid wave strip anchored flush
   to the bottom — explicit overrides for height/bottom/opacity per spec. */
.dt-panel-action.risk-healthy .dt-wave-back,
.dt-panel-action.risk-healthy .dt-wave-front {
    position: absolute;
    left: 0;
    right: 0;
    width: 100%;
    height: 40px;
    bottom: 0;
    margin-bottom: -12px;
    pointer-events: none;
    z-index: 0;
    opacity: 0.8;
}

.event-card.dodge-tide-card .dt-wave-back use,
.event-card.dodge-tide-card .dt-wave-front use,
.dt-panel-action.risk-healthy .dt-wave-back use,
.dt-panel-action.risk-healthy .dt-wave-front use {
    animation: dt-wave-move 12s linear infinite;
}
.event-card.dodge-tide-card .dt-wave-back use:nth-of-type(1),
.dt-panel-action.risk-healthy .dt-wave-back use:nth-of-type(1) { animation-delay: -2s; animation-duration: 15s; }
.event-card.dodge-tide-card .dt-wave-back use:nth-of-type(2),
.dt-panel-action.risk-healthy .dt-wave-back use:nth-of-type(2) { animation-delay: -4s; animation-duration: 18s; }
.event-card.dodge-tide-card .dt-wave-front use:nth-of-type(1),
.dt-panel-action.risk-healthy .dt-wave-front use:nth-of-type(1) { animation-delay: -12s; animation-duration: 9s; }

@keyframes dt-wave-move {
    0%   { transform: translate(-90px, 0); }
    100% { transform: translate(85px, 0); }
}

/* Risk-keyed palette — analogous to the buoy telemetry hues but drawn
   from the compound-risk colour set (#c2410c / #9a3412 / #991b1b / #7f1d1d). */

/* LOW — orange */
.event-card.dodge-tide-card.risk-low .dt-wave-back use:nth-of-type(1) { fill: rgba(234, 88, 12, 0.80); }
.event-card.dodge-tide-card.risk-low .dt-wave-back use:nth-of-type(2) { fill: rgba(194, 65, 12, 0.90); }
.event-card.dodge-tide-card.risk-low .dt-wave-front use              { fill: rgba(251, 146, 60, 0.55); }
.event-card.dodge-tide-card.risk-low .dt-water-gradient {
    background: linear-gradient(180deg,
        rgba(194, 65, 12, 0.10)  0%,
        rgba(154, 52, 18, 0.42)  50%,
        rgba(124, 45, 18, 0.65) 100%);
}

/* MEDIUM — amber (mirrors the buoy telemetry-warning palette). */
.event-card.dodge-tide-card.risk-medium .dt-wave-back use:nth-of-type(1) { fill: rgba(202, 138, 4, 0.80); }
.event-card.dodge-tide-card.risk-medium .dt-wave-back use:nth-of-type(2) { fill: rgba(161, 98, 7, 0.90); }
.event-card.dodge-tide-card.risk-medium .dt-wave-front use              { fill: rgba(234, 179, 8, 0.55); }
.event-card.dodge-tide-card.risk-medium .dt-water-gradient {
    background: linear-gradient(180deg,
        rgba(202, 138, 4, 0.15)  0%,
        rgba(161, 98, 7, 0.40)  50%,
        rgba(113, 63, 18, 0.60) 100%);
}

/* HIGH — red */
.event-card.dodge-tide-card.risk-high .dt-wave-back use:nth-of-type(1) { fill: rgba(220, 38, 38, 0.80); }
.event-card.dodge-tide-card.risk-high .dt-wave-back use:nth-of-type(2) { fill: rgba(185, 28, 28, 0.90); }
.event-card.dodge-tide-card.risk-high .dt-wave-front use              { fill: rgba(239, 68, 68, 0.55); }
.event-card.dodge-tide-card.risk-high .dt-water-gradient {
    background: linear-gradient(180deg,
        rgba(185, 28, 28, 0.14)  0%,
        rgba(153, 27, 27, 0.46)  50%,
        rgba(127, 29, 29, 0.72) 100%);
}

/* CRITICAL — deep red */
.event-card.dodge-tide-card.risk-critical .dt-wave-back use:nth-of-type(1) { fill: rgba(185, 28, 28, 0.80); }
.event-card.dodge-tide-card.risk-critical .dt-wave-back use:nth-of-type(2) { fill: rgba(127, 29, 29, 0.90); }
.event-card.dodge-tide-card.risk-critical .dt-wave-front use              { fill: rgba(220, 38, 38, 0.55); }
.event-card.dodge-tide-card.risk-critical .dt-water-gradient {
    background: linear-gradient(180deg,
        rgba(127, 29, 29, 0.18)  0%,
        rgba(100, 20, 20, 0.50)  50%,
        rgba(69, 10, 10, 0.80)  100%);
}

/* HEALTHY — blue (no active risks). Mirrors the buoy healthy palette so
   the "View tidal analysis" card reads as a calm, safe baseline. Same
   blue palette also forced onto dodge-tide event cards during the demo
   (see the riskClass override in updateDodgeTidesPanel). */
.dt-panel-action.risk-healthy .dt-wave-back use:nth-of-type(1),
.event-card.dodge-tide-card.risk-healthy .dt-wave-back use:nth-of-type(1) { fill: rgba(52, 97, 193, 0.80); }
.dt-panel-action.risk-healthy .dt-wave-back use:nth-of-type(2),
.event-card.dodge-tide-card.risk-healthy .dt-wave-back use:nth-of-type(2) { fill: rgba(45, 85, 170, 0.90); }
.dt-panel-action.risk-healthy .dt-wave-front use,
.event-card.dodge-tide-card.risk-healthy .dt-wave-front use              { fill: rgba(69, 121, 226, 0.55); }
.dt-panel-action.risk-healthy .dt-water-gradient,
.event-card.dodge-tide-card.risk-healthy .dt-water-gradient {
    background: linear-gradient(180deg,
        rgba(52, 97, 193, 0.10)  0%,
        rgba(45, 85, 170, 0.42)  50%,
        rgba(37, 33, 33, 0.55)  100%);
}
/* Water level for the healthy-forced dodge-tide card — calm mid-height. */
.event-card.dodge-tide-card.risk-healthy { --dt-water-level: 42%; }
/* Risk pill on dodge-tide cards — shares geometry with .mode-badge.mode-badge-inline
   (font, padding, border-radius, letter-spacing). Background/colour set inline
   per-risk-level at render time. */
.dodge-tide-risk {
    /* Same pill as .dt-tide-event-label so the two header pills read as a
       matched set — only the text differs. */
    display: inline-block;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    padding: 2px 8px;
    border-radius: 10px;
    color: #1a1a1a;
    background: #60a5fa;
    text-transform: uppercase;
    animation: none;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.dodge-tide-detail {
    font-size: 10px;
    color: #fff;
    margin-top: 2px;
}
.dt-panel-action {
    padding: 8px 0 4px;
    text-align: center;
}
.dt-panel-action-link {
    font-size: 11px;
    color: #60a5fa;
    cursor: pointer;
    transition: color 0.15s;
}
.dt-panel-action-link:hover {
    color: #93c5fd;
}
.dt-panel-action-link i {
    margin-right: 4px;
}
.dt-panel-action-hint {
    font-size: 11px;
    color: #64748b;
    font-style: italic;
}
/* AOI on-map popup: clickable, monospace lat/lng line under the area name. */
.aoi-item-coords {
    display: block;
    font-size: 10px;
    color: #94a3b8;
    cursor: pointer;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    margin-top: 2px;
    white-space: nowrap;
    transition: color 0.15s;
}
.aoi-item-coords:hover {
    color: #cbd5e1;
}
.aoi-item-coords i {
    margin-right: 4px;
    opacity: 0.85;
}
/* ── BC Button Group ──────────────────────────────────────────
   Reusable horizontal toolbar — flush buttons divided by a thin
   line, single rounded outer border, hover tint per cell. Used
   in modal headers as the right-side controls cluster. Items
   can be <button> (interactive) or <span> with the --text
   modifier (read-only label, no hover, default cursor). */
.bc-btn-group {
    display: inline-flex;
    overflow: hidden;
    background-color: #111827;
    border: 1px solid #374151;
    border-radius: 8px;
}
.bc-btn-group__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 6px 14px;
    background: transparent;
    color: #d1d5db;
    font-weight: 500;
    font-size: 12px;
    line-height: 1;
    border: none;
    cursor: pointer;
    transition: background-color 0.2s, color 0.2s;
}
.bc-btn-group__btn + .bc-btn-group__btn {
    border-left: 1px solid #374151;
}
.bc-btn-group__btn:hover {
    background-color: #1f2937;
    color: #ffffff;
}
.bc-btn-group__btn i {
    font-size: 14px;
}
.bc-btn-group__btn--text {
    cursor: default;
    color: #94a3b8;
    font-variant-numeric: tabular-nums;
}
.bc-btn-group__btn--text:hover {
    background-color: transparent;
    color: #94a3b8;
}
.bc-btn-group__btn--danger {
    color: #f87171;
}
.bc-btn-group__btn--danger:hover {
    background-color: rgba(239, 68, 68, 0.2);
    color: #fca5a5;
}

/* AOI delete two-step confirm — first click swaps the trash icon for
   a question mark and reveals the "Sure?" label; second click commits.
   Auto-reverts after 2s via JS. Smooth crossfade between states. */
#btn-aoi-delete .text {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
#btn-aoi-delete .adc-icon-default,
#btn-aoi-delete .adc-confirm-label {
    transition: opacity 0.2s ease, transform 0.2s ease, max-width 0.25s ease;
}
#btn-aoi-delete .adc-confirm-label {
    overflow: hidden;
    max-width: 0;
    opacity: 0;
    white-space: nowrap;
}
/* Confirm state — armed, awaiting second click. Trash icon hides; the
   "Sure?" label slides out to the side as the only visible content. */
#btn-aoi-delete.aoi-delete-confirm {
    background-color: rgba(239, 68, 68, 0.25);
    color: #fecaca;
}
#btn-aoi-delete.aoi-delete-confirm .adc-icon-default {
    display: none;
}
#btn-aoi-delete.aoi-delete-confirm .adc-confirm-label {
    max-width: 64px;
    opacity: 1;
}
@media (min-width: 640px) {
    .bc-btn-group__btn { padding: 6px 18px; }
    .bc-btn-group__btn i { font-size: 15px; }
}

/* ── Dodge Tide Modal ──────────────────────────────────────── */
.dodge-tide-modal-content {
    position: relative;
    width: 780px;
    max-width: 95vw;
    max-height: 90vh;
    overflow-y: auto;
    overflow-x: hidden;
    z-index: 1051;
    transition: width 0.3s ease, max-width 0.3s ease, max-height 0.3s ease;
}
/* Fullscreen toggle — mirrors the buoy modal's .telemetry-fullscreen so
   the dodge-tide chart can take over the screen for closer inspection. */
.dodge-tide-modal-content.dt-fullscreen {
    width: 98vw;
    max-width: 98vw;
    height: 96vh;
    max-height: 96vh;
}
.dodge-tide-modal-content::-webkit-scrollbar { width: 4px; }
.dodge-tide-modal-content::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 2px; }
/* Date navigator row inside the dodge-tide modal — sits between
   the modal-header and the tabs. Prev arrow + clickable date pill
   + next arrow, all centered. The date pill houses an invisible
   native date picker so clicking it opens the system calendar. */
/* Wrapper that stacks the floating date-nav above the modal-content so
   the date controls hover just above the modal frame. The .modal-overlay
   centres this stack vertically; the date-nav inherits centering via
   align-items below. */
.dt-modal-stack {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    max-height: 96vh;
    /* Lift the whole stack above the .modal-backdrop (positioned, z-index
       auto). Without this the static date-nav row paints behind the
       backdrop and is dimmed/obscured. */
    position: relative;
    z-index: 1;
}
.dt-modal-stack > .dodge-tide-modal-content {
    /* Stack item — keep the modal's existing width/height behaviour but
       let the flex column manage vertical layout. */
    flex: 0 1 auto;
}
.dt-date-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 0;
    background: transparent;
    border: none;
    margin: 0;
}
.dt-date-nav-arrow {
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.10);
    color: #cbd5e1;
    border-radius: 50%;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}
.dt-date-nav-arrow:hover:not([disabled]) {
    background: rgba(255, 255, 255, 0.14);
    color: #ffffff;
}
.dt-date-nav-arrow[disabled] {
    opacity: 0.3;
    cursor: default;
}
.dt-date-nav-arrow i {
    font-size: 13px;
}
/* Styled to match the left-panel date pill (.dd-date): black pill, white
   uppercase text, with a leading calendar icon. */
.dt-date-nav-date {
    position: relative;
    min-width: 220px;
    padding: 6px 14px;
    background: #000000;
    border: none;
    color: #ffffff;
    border-radius: 5px;
    font-size: 15px;
    font-weight: 500;
    letter-spacing: 0.6px;
    line-height: 1.2;
    text-transform: uppercase;
    cursor: pointer;
    transition: background 0.15s;
}
.dt-date-nav-date:hover {
    background: #1a1a1a;
}
/* Calendar icon + date text centred inside the pill. */
.dt-date-nav-date .text {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
}
.dt-date-nav-date .text i {
    font-size: 12px;
    opacity: 0.85;
}
/* Hidden native date input — laid over the button so the OS picker
   anchors here. Pointer-events: none so clicks always hit the button
   (which then calls showPicker). */
.dt-date-picker-input {
    position: absolute;
    inset: 0;
    opacity: 0;
    pointer-events: none;
    border: none;
    background: transparent;
    width: 100%;
    height: 100%;
}
.dt-section {
    padding: 12px 0;
    border-bottom: 1px solid rgba(255,255,255,0.06);
}
.dt-section:last-child { border-bottom: none; }
.dt-section-title {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    color: #64748b;
    margin-bottom: 10px;
}
/* Dodge-tide AI summary section (top of the modal). */
.dt-ai-section {
    text-align: center;
}
.dt-ai-section .ai-button[disabled] {
    cursor: default;
}
/* Deep-analysis CTA pulled to the right edge of the modal. */
.dt-deep-analysis-section {
    text-align: right;
}
.dt-deep-analysis-btn {
    margin-top: 4px;
}
.dt-ai-summary-text {
    font-size: 13px;
    line-height: 1.5;
    color: #cbd5e1;
    text-align: left;
}
.dt-ai-summary-text.dt-ai-error {
    color: #64748b;
    font-style: italic;
}
/* Collapsible section header — title left, +/- toggle pushed to the right. */
.dt-section-title--toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    cursor: pointer;
}
/* When collapsed, drop the header's bottom margin so the minimised section
   sits tight, and hide its body. */
.dt-section.dt-collapsed .dt-section-title--toggle { margin-bottom: 0; }
.dt-section.dt-collapsed .dt-section-body { display: none; }
.dt-section-toggle {
    flex: 0 0 auto;
    margin-left: auto;
    width: 22px;
    height: 22px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.05);
    color: #cbd5e1;
    font-size: 11px;
    cursor: pointer;
    /* Suppress the global <button> skew/wave fill. */
    isolation: auto;
    overflow: visible;
}
.dt-section-toggle::after { content: none; display: none; }
.dt-section-toggle:hover {
    background: rgba(255, 255, 255, 0.12);
    color: #ffffff;
}
/* Header */
.dt-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}
.dt-header-info { flex: 1; }
.dt-aoi-name {
    font-size: 16px;
    font-weight: 700;
    color: #e2e8f0;
}
.dt-date {
    font-size: 11px;
    color: #64748b;
    margin-top: 2px;
}
/* "Tide Event" pill on the dodge-tide event-card header — same shape
   language as .dt-risk-badge; only colours differ. Background uses the
   modal-title accent (#60a5fa); white text. */
.dt-tide-event-label {
    display: inline-block;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    padding: 2px 8px;
    border-radius: 10px;
    color: #ffffff;
    background: #1a1a1a;
    text-transform: uppercase;
    animation: none;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

/* Modal risk badges — same mode-badge-inline geometry, colours per risk-level. */
.dt-risk-badge {
    display: inline-block;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    padding: 2px 8px;
    border-radius: 10px;
    color: #fff;
    text-transform: uppercase;
    animation: none;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.dt-risk-badge.risk-low { background: #c2410c; box-shadow: 0 0 12px rgba(194,65,12,0.4); }
.dt-risk-badge.risk-medium { background: #a16207; box-shadow: 0 0 12px rgba(234, 179, 8, 0.4); }
.dt-risk-badge.risk-high { background: #991b1b; box-shadow: 0 0 12px rgba(153,27,27,0.4); }
.dt-risk-badge.risk-critical { background: #7f1d1d; box-shadow: 0 0 16px rgba(127,29,29,0.5); }
.dt-risk-badge.risk-none { background: #334155; box-shadow: none; }
/* Risk Windows */
.dt-window-bar {
    position: relative;
    height: 32px;
    background: rgba(255,255,255,0.03);
    border-radius: 6px;
    margin-bottom: 6px;
    overflow: hidden;
    border: 1px solid rgba(255,255,255,0.06);
}
.dt-window-fill {
    position: absolute;
    top: 0; bottom: 0;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 9px;
    font-weight: 600;
    color: #fff;
    letter-spacing: 0.3px;
    min-width: 60px;
    text-shadow: 0 1px 2px rgba(0,0,0,0.5);
}
.dt-window-label {
    display: flex;
    justify-content: space-between;
    font-size: 9px;
    color: #94a3b8;
    margin-bottom: 4px;
}
.dt-no-windows {
    font-size: 11px;
    color: #475569;
    font-style: italic;
    padding: 8px 0;
}
/* Chart containers */
.dt-combined-chart-wrap {
    position: relative;
    height: 200px;
    margin-top: 8px;
}
/* Fullscreen mode — let the chart take advantage of the extra
   vertical room. 96vh modal − ~250px of chrome (header, tabs,
   section title, wind arrows, legend, padding) leaves a healthy
   chart area; the min-height keeps it sensible if the modal ends
   up shorter than expected. Width unchanged — the chart already
   filled horizontally. */
.dodge-tide-modal-content.dt-fullscreen .dt-combined-chart-wrap {
    height: calc(96vh - 250px);
    min-height: 220px;
}
/* Fullscreen mode — scale up the Ocean Profile heatmap so it
   fills the extra vertical room the way the combined chart does.
   Row heights, cell heights, and label font-sizes all enlarge. */
.dodge-tide-modal-content.dt-fullscreen .ocean-depth-row {
    height: 44px;
}
.dodge-tide-modal-content.dt-fullscreen .ocean-heatmap-cell {
    height: 44px;
    font-size: 14px;
}
.dodge-tide-modal-content.dt-fullscreen .ocean-depth-label {
    font-size: 14px;
}
.dodge-tide-modal-content.dt-fullscreen .ocean-profile-header-titles span {
    font-size: 14px;
}
.dodge-tide-modal-content.dt-fullscreen .ocean-thermocline-row {
    height: 28px;
}
.dodge-tide-modal-content.dt-fullscreen .ocean-thermocline-label {
    font-size: 13px;
}
.dodge-tide-modal-content.dt-fullscreen .ocean-legend-item {
    font-size: 13px;
}
/* Wind direction arrows row */
.dt-wind-arrows {
    position: relative;
    height: 22px;
    margin-top: 4px;
}
.dt-wind-arrow {
    position: absolute;
    width: 18px;
    height: 18px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    font-weight: 900;
    color: #94a3b8;
    transition: transform 0.2s;
    transform-origin: center center;
}
/* ── Combined Chart Legend ────────────────────────────────── */
.dt-chart-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 8px;
    padding-top: 6px;
    border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.dt-chart-legend-item {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 9px;
    color: #94a3b8;
}
.dt-chart-legend-line {
    width: 14px;
    height: 3px;
    border-radius: 1px;
}
.dt-chart-legend-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
}
.dt-chart-legend-dash {
    width: 14px;
    height: 0;
    border-top: 2px dashed rgba(148, 163, 184, 0.5);
}
.dt-chart-legend-bar {
    width: 10px;
    height: 10px;
    border-radius: 2px;
}

/* ── Ocean Profile Heatmap ───────────────────────────────── */
.ocean-profile-header {
    display: flex;
    align-items: center;
    margin-bottom: 4px;
}
.ocean-profile-header-spacer {
    width: 50px;
    flex-shrink: 0;
}
.ocean-profile-header-titles {
    display: flex;
    flex: 1;
    gap: 3px;
}
.ocean-profile-header-titles span {
    flex: 1;
    text-align: center;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.5px;
}
.ocean-depth-row {
    display: flex;
    align-items: center;
    height: 22px;
}
.ocean-depth-label {
    width: 50px;
    text-align: right;
    padding-right: 10px;
    font-size: 10px;
    color: #64748b;
    flex-shrink: 0;
}
.ocean-heatmap-cols {
    display: flex;
    flex: 1;
    gap: 2px;
}
.ocean-heatmap-cell {
    flex: 1;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 9px;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.9);
}
.ocean-thermocline-row {
    display: flex;
    align-items: center;
    height: 18px;
    margin: 1px 0;
}
.ocean-thermocline-spacer {
    width: 50px;
    flex-shrink: 0;
}
.ocean-thermocline-line-area {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 8px;
}
.ocean-thermocline-dashed {
    flex: 1;
    height: 0;
    border-top: 2px dashed #f97316;
}
.ocean-thermocline-label {
    font-size: 9px;
    font-weight: 700;
    color: #f97316;
    white-space: nowrap;
}
.ocean-legend-row {
    display: flex;
    gap: 16px;
    margin-top: 10px;
    padding-top: 8px;
    border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.ocean-legend-item {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 10px;
    color: #94a3b8;
}
.ocean-legend-dot {
    width: 8px;
    height: 8px;
    border-radius: 2px;
}
.ocean-legend-line {
    width: 16px;
    height: 0;
    border-top: 2px dashed #f97316;
}

/* ── N² Stratification Chart ─────────────────────────────── */
.n2-row {
    display: flex;
    align-items: center;
    height: 28px;
}
.n2-row.n2-highlight {
    background: rgba(249, 115, 22, 0.08);
    border-radius: 4px;
}
.n2-depth {
    width: 70px;
    text-align: right;
    padding-right: 10px;
    font-size: 10px;
    color: #64748b;
    flex-shrink: 0;
}
.n2-depth.n2-highlight {
    color: #f97316;
    font-weight: 700;
}
.n2-bar-area {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 8px;
}
.n2-bar-track {
    flex: 1;
    height: 12px;
    background: rgba(255, 255, 255, 0.04);
    border-radius: 3px;
    overflow: hidden;
}
.n2-bar-fill {
    height: 100%;
    border-radius: 3px;
}
.n2-value {
    font-size: 9px;
    color: #94a3b8;
    width: 50px;
    text-align: right;
    flex-shrink: 0;
}
.n2-value.n2-highlight {
    color: #f97316;
    font-weight: 700;
}
.n2-desc {
    font-size: 9px;
    width: 100px;
    flex-shrink: 0;
}
.n2-desc-strong { color: #f97316; font-weight: 700; }
.n2-desc-moderate { color: #facc15; font-weight: 600; }
.n2-desc-weak { color: #64748b; }

/* Stat tiles grid */
.dt-stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 8px;
    margin-top: 8px;
}
.dt-stat-tile {
    padding: 10px;
    border-radius: 8px;
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.06);
    text-align: center;
}
.dt-stat-value {
    font-size: 18px;
    font-weight: 700;
    color: #e2e8f0;
}
.dt-stat-label {
    font-size: 9px;
    color: #64748b;
    margin-top: 2px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.dt-stat-tile.stratified-yes { border-color: rgba(220,38,38,0.3); background: rgba(220,38,38,0.06); }
.dt-stat-tile.stratified-no { border-color: rgba(34,197,94,0.3); background: rgba(34,197,94,0.06); }
/* Compound risk cards */
.dt-compound-card {
    padding: 10px 12px;
    border-radius: 8px;
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.06);
    margin-bottom: 6px;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
}
/* Card header row — risk badge on the left, date/time pills pushed right. */
.dt-compound-head {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
}
.dt-compound-when {
    margin-left: auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 6px;
}
/* Plain-English summary paragraph generated by
   buildCompoundRiskCommentary(). Sits beside the risk badge inside
   .dt-compound-card. */
.dt-compound-commentary {
    font-size: 12px;
    line-height: 1.5;
    color: #cbd5e1;
    flex: 1 1 100%;
    margin-bottom: 6px;
}

/* ── Buoys Panel ──────────────────────────────────────────── */
#buoys-panel {
    top: 100px;

}
#buoys-panel .panel-title {
    font-size: 12px;
    font-weight: 600;
    color: #e2e8f0;
    letter-spacing: 0.5px;
    text-transform: uppercase;
}
#buoys-list {
    margin-top: 8px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    background: transparent;
}
.buoy-list-item {
    position: relative;
    cursor: pointer;
    border-radius: 8px;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.buoy-list-item:hover {
    transform: translateY(-2px);
}
/* Three micro-stat cards (PV V / Battery V / Load V) anchored to the
   bottom of each .buoy-list-item. Field set is hardcoded in
   BUOY_PANEL_MICROSTATS for now; values refresh live from the
   telemetry WS via _updateBuoyListMicrostats(). */
.buoy-microstats {
    position: absolute;
    bottom: 8px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 4px;
    z-index: 2;
    pointer-events: none;
}
/* Mirror the .buoy-list-name pill styling so the three side-panel
   micro-stats read as small health-coloured chips. The telemetry-*
   variants below paint background + foreground per state — setBuoyStatus
   swaps the class on each chip whenever the buoy's status changes. */
.buoy-microstat {
    padding: 2px 8px;
    border-radius: 5px;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 1px;
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    /* Cap the chip's width so a long/unexpected value can't push the row
       past the buoy card. Inner value text clips with an ellipsis. */
    max-width: 80px;
    overflow: hidden;
    transition: background 0.3s, color 0.3s;
}
.buoy-microstat i {
    font-size: 13px;
    opacity: 0.9;
    flex-shrink: 0;
}
.buoy-microstat-value {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
.buoy-microstat.telemetry-unknown {
    background: rgba(148, 163, 184, 0.18);
    color: #94a3b8;
}
.buoy-microstat.telemetry-healthy {
    background: rgb(0 51 109 / 90%);
    color: #bfdbfe;
}
.buoy-microstat.telemetry-warning {
    background: rgba(234, 179, 8, 0.22);
    color: #eab308;
}
.buoy-microstat.telemetry-critical {
    background: rgba(239, 68, 68, 0.22);
    color: #ef4444;
}
.buoy-list-name {
    /* Match .buoy-device-health from the buoy modal — compact health pill */
    position: absolute;
    left: 50%;
    top: 6px;
    transform: translateX(-50%);
    z-index: 100;
    padding: 1px 6px;
    border-radius: 4px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 1px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    pointer-events: none;
    transition: background 0.3s, color 0.3s;
}
.buoy-list-name.telemetry-unknown {
    background: rgba(148, 163, 184, 0.18);
    color: #94a3b8;
}
.buoy-list-name.telemetry-healthy {
    background: rgb(0 51 109 / 90%);
    color: #bfdbfe;
}
.buoy-list-name.telemetry-warning {
    background: rgba(234, 179, 8, 0.22);
    color: #eab308;
}
.buoy-list-name.telemetry-critical {
    background: rgba(239, 68, 68, 0.22);
    color: #ef4444;
}
.buoy-list-item .buoy-image-col {
    flex: 0 0 auto;
    width: 100%;
}
.buoy-list-item .buoy-image-col img {
    width: 100%;
}
.buoy-list-item.alarm-high {
    box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.45);
}
.buoy-list-item.alarm-critical {
    box-shadow: 0 0 0 2px rgba(220, 38, 38, 0.55);
    animation: buoy-alarm-pulse 1.8s ease-in-out infinite;
}
@keyframes buoy-alarm-pulse {
    0%, 100% { box-shadow: 0 0 0 2px rgba(220, 38, 38, 0.55); }
    50% { box-shadow: 0 0 0 4px rgba(220, 38, 38, 0.35); }
}

/* ── Layer Buttons ─────────────────────────────────────────── */
.layer-btn-group {
    display: flex;
    flex-direction: row;
    gap: 4px;
}

/* Suppress the browser's default focus outline on Leaflet interactive
   paths. Without this, clicking an AOI circle (or any clickable vector)
   draws a rectangular focus ring around the path's bounding box —
   which on a circle looks like a stray square next to the shape.
   The shape itself is still keyboard-focusable for accessibility, just
   without the visual rectangle. */
.leaflet-interactive:focus,
.leaflet-interactive:focus-visible {
    outline: none;
}

/* AOI logo overlay — sits behind the shape on a dedicated pane and is
   clipped per-shape to the AOI's outline so the brand mark only shows
   inside the geofence boundary. Pointer events are disabled at the pane
   level; class-level rule is belt-and-suspenders. `object-fit: contain`
   preserves the logo's aspect ratio inside the (square) bounding box —
   without it the image would be stretched to fill the box, distorting
   the mark. The transparent letterbox space around the logo doesn't
   interfere with the clip-path. */
.aoi-logo-overlay {
    pointer-events: none;
    object-fit: contain;
}
.aoi-logo-circle {
    clip-path: circle(90%);
    opacity: 0.5;
}
/* Triangle clip matches buildAoiShape's geometry: vertices computed at
   angles -90°, 30°, 150° from centre, all at distance r. In the
   bounding-box image this maps to apex bottom-centre, base top-left and
   top-right at 25% from the top. */
.aoi-logo-triangle {
    clip-path: polygon(50% 100%, 93.3% 25%, 6.7% 25%);
}
/* Squares fill the rect — no clip needed. */

/* ── Bottom-left sliding-pill map-layer switcher ───────────────
   Class names namespaced under .map-switcher__* so they don't
   clash with other tab/indicator/label patterns in the app.
   --map-tab-w sets the per-tab width — change once and both the
   labels and the indicator follow. */
.map-switcher {
    --map-tab-w: 90px;
    --map-tab-h: 28px;
    /* Positioning context for the absolutely-positioned __indicator
       child. Variants (the bottom-left fixed pill, the in-panel
       switchers) override `position` as needed. */
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    padding: 2px;
    background-color: #dbeafe;
    border-radius: 9px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
    user-select: none;
}
/* Bottom-left map-layer switcher — pinned to the viewport corner. */
#map-layer-switcher {
    position: fixed;
    bottom: 12px;
    left: 12px;
    z-index: 1500;
}
/* Admin entry point — injected by admin.js for users with the
   administration module. Sits just above the bottom-left map layer
   switcher as a low-contrast disc so it's discoverable without taking
   up a slot in the user-bar. */
#btn-admin {
    position: fixed;
    bottom: 56px;
    left: 12px;
    z-index: 1500;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: rgba(8, 12, 24, 0.55);
    border: 1px solid rgba(148, 163, 184, 0.22);
    color: #94a3b8;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    backdrop-filter: blur(4px);
    transition: background 0.15s ease, color 0.15s ease, transform 0.15s ease;
}
#btn-admin:hover {
    background: rgba(8, 12, 24, 0.85);
    color: #e2e8f0;
    transform: scale(1.05);
}
#btn-admin i {
    font-size: 12px;
}
.map-switcher__indicator {
    width: var(--map-tab-w);
    height: var(--map-tab-h);
    background: #ffffff;
    position: absolute;
    top: 2px;
    left: 2px;
    z-index: 1;
    border: 0.5px solid rgba(0, 0, 0, 0.04);
    box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.12), 0px 3px 1px rgba(0, 0, 0, 0.04);
    border-radius: 7px;
    transition: left 0.2s ease-out;
    pointer-events: none;
}
.map-switcher__tab {
    width: var(--map-tab-w);
    height: var(--map-tab-h);
    position: absolute;
    z-index: 3;
    outline: none;
    opacity: 0;
    margin: 0;
    cursor: pointer;
}
.map-switcher__label {
    width: var(--map-tab-w);
    height: var(--map-tab-h);
    position: relative;
    z-index: 4;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    margin: 0;
    color: #1a1a1a;
    font-size: 0.75rem;
    opacity: 0.6;
    cursor: pointer;
    transition: opacity 0.2s ease-out, font-weight 0.2s ease-out;
}
.map-switcher__tab:checked + .map-switcher__label {
    opacity: 1;
    font-weight: 600;
}
.map-switcher__tab--1:checked ~ .map-switcher__indicator { left: 2px; }
.map-switcher__tab--2:checked ~ .map-switcher__indicator { left: calc(var(--map-tab-w) + 2px); }
.map-switcher__tab--3:checked ~ .map-switcher__indicator { left: calc(var(--map-tab-w) * 2 + 2px); }
.map-switcher__tab--4:checked ~ .map-switcher__indicator { left: calc(var(--map-tab-w) * 3 + 2px); }
.map-switcher__tab--5:checked ~ .map-switcher__indicator { left: calc(var(--map-tab-w) * 4 + 2px); }

/* Dark palette variant — compose as `.map-switcher.map-switcher--dark`.
   Reuses the structural rules above and only swaps the colour tokens:
   pale-blue tray → translucent slate, white indicator → light slate,
   near-black labels → light slate-200. */
.map-switcher--dark {
    background-color: #969696E5;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.45);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid rgba(255, 255, 255, 0.18);
}
.map-switcher--dark .map-switcher__indicator {
    background: rgba(15, 23, 42, 0.92);
    border: 0.5px solid rgba(255, 255, 255, 0.18);
    box-shadow: 0 3px 8px rgba(0, 0, 0, 0.35), 0 1px 2px rgba(0, 0, 0, 0.25);
}
.map-switcher--dark .map-switcher__label {
    color: #334155;
    opacity: 0.7;
}
.map-switcher--dark .map-switcher__tab:checked + .map-switcher__label {
    color: #f8fafc;
    opacity: 1;
    font-weight: 600;
}

/* Animation-speed switcher — same widget, but lives inline inside
   the bloom playback panel (not bottom-left fixed). Tabs auto-size
   to share the container width, so the labels "Fast Forward",
   "Normal Speed", "Slow Motion" all fit. */
.animation-speed-switcher {
    position: static;
    width: 100%;
    box-sizing: border-box;
}
.animation-speed-switcher .map-switcher__tab,
.animation-speed-switcher .map-switcher__label,
.animation-speed-switcher .map-switcher__indicator {
    width: calc((100% - 4px) / 3);
}
.animation-speed-switcher .map-switcher__tab--1:checked ~ .map-switcher__indicator { left: 2px; }
.animation-speed-switcher .map-switcher__tab--2:checked ~ .map-switcher__indicator { left: calc((100% - 4px) / 3 + 2px); }
.animation-speed-switcher .map-switcher__tab--3:checked ~ .map-switcher__indicator { left: calc((100% - 4px) / 3 * 2 + 2px); }

/* Playback-control switcher — same widget, 3-tab variant
   (Replay / Play / Pause). Lives below the playback banner. Slightly
   thicker tray padding than the base so the indicator has visible
   breathing room top/bottom. */
.playback-control-switcher {
    position: static;
    width: 100%;
    box-sizing: border-box;
    padding: 4px;
}
.playback-control-switcher .map-switcher__indicator {
    top: 4px;
}
.playback-control-switcher .map-switcher__tab,
.playback-control-switcher .map-switcher__label,
.playback-control-switcher .map-switcher__indicator {
    width: calc((100% - 8px) / 3);
}
.playback-control-switcher .map-switcher__tab--1:checked ~ .map-switcher__indicator { left: 4px; }
.playback-control-switcher .map-switcher__tab--2:checked ~ .map-switcher__indicator { left: calc((100% - 8px) / 3 + 4px); }
.playback-control-switcher .map-switcher__tab--3:checked ~ .map-switcher__indicator { left: calc((100% - 8px) / 3 * 2 + 4px); }

/* Day-progress dot row — one dot per day. Past dots solid-but-dim,
   the current dot glows bright, future dots stay muted/grayscale.
   Wraps if a long-running bloom won't fit in one line. */
.efb-day-dots {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 4px;
    width: 100%;
    padding: 4px 0;
}
.efb-day-dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: rgba(148, 163, 184, 0.25);
    transition: background 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
}
.efb-day-dot-past {
    background: #3b82f6;
}
.efb-day-dot-current {
    background: #fbbf24;
    box-shadow: 0 0 8px 2px rgba(251, 191, 36, 0.85),
                0 0 16px 4px rgba(251, 191, 36, 0.45);
    transform: scale(1.4);
}
.efb-day-dot-future {
    background: rgba(148, 163, 184, 0.25);
}
.layer-btn {
    padding: 7px 14px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.03);
    color: #94a3b8;
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s ease;
    text-align: left;

}
.layer-btn:hover {
    background: rgba(255, 255, 255, 0.07);
    color: #e2e8f0;
}
.layer-btn.active {
    background: rgba(37, 99, 235, 0.2);
    border-color: rgba(37, 99, 235, 0.5);
    color: #60a5fa;
}

/* ── Toggle Switches ───────────────────────────────────────── */
.toggle-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 6px 0;
    cursor: pointer;
    font-size: 12px;
    color: #94a3b8;
}
.toggle-row input[type="checkbox"] {
    display: none;
}
.toggle-switch {
    position: relative;
    width: 36px;
    height: 20px;
    background: rgba(255,255,255,0.1);
    border-radius: 10px;
    transition: background 0.2s;
    flex-shrink: 0;
}
.toggle-switch::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #64748b;
    transition: all 0.2s;
}
.toggle-row input:checked + .toggle-switch {
    background: rgba(37, 99, 235, 0.5);
}
.toggle-row input:checked + .toggle-switch::after {
    left: 18px;
    background: #60a5fa;
}

/* ── Intensity Legend ──────────────────────────────────────── */
.legend-gradient {
    height: 10px;
    border-radius: 5px;
    background: linear-gradient(to right, #00ff00, #adff2f, #ffff00, #ffa500, #ff4500, #ff0000, #8b0000);
    margin-bottom: 4px;
}
.legend-labels {
    display: flex;
    justify-content: space-between;
    font-size: 10px;
    color: #64748b;
}

/* ── Timeline Panel (above events panel) ───────────────────── */
.timeline-panel-left {
    display: none;
    position: fixed;
    top: 100px;
    left: 16px;
    width: 270px;
    z-index: 1000;
    padding: 0;
}

/* ── Timeline Slider ───────────────────────────────────────── */
.timeline-slider-wrap {
    position: relative;
    height: 28px;
    margin-bottom: 8px;
}
.timeline-slider {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 6px;
    border-radius: 3px;
    outline: none;
    cursor: pointer;
    background: #023267;
    border: 1px solid #3462BB;
    box-sizing: border-box;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    margin: 0;
}
/* Hide native thumbs — custom overlay used instead */
.timeline-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: transparent;
    border: none;
    cursor: grab;
}
.timeline-slider::-webkit-slider-thumb:active {
    cursor: grabbing;
}
.timeline-slider::-moz-range-thumb {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: transparent;
    border: none;
    cursor: grab;
}
.timeline-slider::-moz-range-thumb:active {
    cursor: grabbing;
}
.timeline-slider::-moz-range-track {
    height: 6px;
    border-radius: 3px;
    background: transparent;
}
/* Custom FA icon thumb overlay */
.timeline-thumb {
    position: absolute;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: radial-gradient(circle at 40% 35%, #fde68a, #fbbf24 50%, #d97706);
    border: 2px solid rgba(0,0,0,0.25);
    box-shadow: 0 0 12px rgba(251, 191, 36, 0.5), 0 2px 6px rgba(0,0,0,0.3);
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    transition: box-shadow 0.1s ease;
    z-index: 1;
}
.timeline-thumb i {
    font-size: 10px;
    color: #422006;
    margin-left: 2px;
}
.timeline-thumb.playing i {
    margin-left: 0;
}
/* Simpler thumb for the main timeline slider — half the size, solid dark
   fill in the same palette as .days-badge. Scoped by id so the cloned
   .event-thumb buttons in the events panel keep their gold flourish. */
#timeline-thumb {
    width: 14px;
    height: 14px;
    background: rgb(12, 17, 30);
    border: 1px solid #3462BB;
    box-shadow: none;
}
.timeline-slider-wrap:hover .timeline-thumb {
    box-shadow: 0 0 18px rgba(251, 191, 36, 0.7), 0 2px 8px rgba(0,0,0,0.3);
}
.timeline-thumb.pulse-hint {
    animation: slider-thumb-pulse 2s ease-in-out infinite;
}
@keyframes slider-thumb-pulse {
    0%   { box-shadow: 0 0 12px rgba(251, 191, 36, 0.5), 0 2px 6px rgba(0,0,0,0.3); transform: translate(-50%, -50%) scale(1); }
    50%  { box-shadow: 0 0 28px rgba(251, 191, 36, 0.9), 0 0 50px rgba(251, 191, 36, 0.3); transform: translate(-50%, -50%) scale(1.3); }
    100% { box-shadow: 0 0 12px rgba(251, 191, 36, 0.5), 0 2px 6px rgba(0,0,0,0.3); transform: translate(-50%, -50%) scale(1); }
}

#timeline-date-label {
    display: inline-block;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.5px;
    color: #f7f7f7;
    background: rgba(22, 22, 22, 0.2);
    border: 1px solid rgba(11, 11, 11, 0.2);
    border-radius: 10px;
    padding: 3px 10px;
    margin-top: 10px;
    text-align:center;
    width: 100%;
}

/* ── Loading Overlay ───────────────────────────────────────── */
#loading-overlay {
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    background: #080c18 url('/static/back.jpg') center / cover no-repeat;
    z-index: 2000;
    display: flex;
    align-items: center;
    justify-content: center;
}
#loading-content {
    text-align: center;
    max-width: 360px;
}

.loading-logo {
    height: 144px;
    z-index: -1;
    margin-top: 70px;
    transform: rotate(-45deg);
      margin-bottom: auto;
    opacity: 1;
}
/* Lift the logo into the "positioned descendants" paint band so it sits
   above the absolutely-positioned wave layers inside .bc-wave-loader. */
.bc-wave-loader .loading-logo {
    position: relative;
    z-index: 1;
}
.loading-spinner {
    width: 40px;
    height: 40px;

    border: 3px solid rgba(255,255,255,0.08);
    border-top-color: #60a5fa;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
    margin: 0 auto 20px;
}
.small-spinner {
    width: 20px;
    height: 20px;
    border-width: 2px;
    margin: 0 0 0 0;
}
@keyframes spin {
    to { transform: rotate(360deg); }
}
/* Match the .days-badge pill look (composed from .mode-badge +
   .mode-badge-inline + .days-badge so the whole cascade is mirrored here). */
#loading-text {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    animation: none;
    background: rgb(12, 17, 30);
    color: #cbd5e1;
    margin-top: 20px;
    min-height: 20px;
}

/* ── Loading motif — wave-filled BC logo ─────────────────────
   The Blue Carbon logo PNG is used as an alpha mask: only its non-
   transparent pixels let the three rotating .bc_wave layers through,
   so the visible silhouette is the brand mark filled with animated
   waves. The orb-shape rotation + organic border-radius are dropped
   — the logo's outline defines the shape now. The inline `top:` on
   each .bc_wave still drives waterline progress (70% = 0% complete,
   10% = 100% complete). updateLoadingProgress() writes the top. */
.bc-wave-loader {
    position: relative;
    width: 180px;
    height: 180px;
    margin: 0 auto;
    opacity:1;
    overflow: hidden;
    -webkit-mask-image: url('bluecarbon-w.png');
            mask-image: url('bluecarbon-w.png');
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
    -webkit-mask-size: contain;
            mask-size: contain;
}
/* The img inside the wave-loader is now redundant — the mask above
   already produces the logo silhouette. Hide it so the masked waves
   read as a clean shape. */
.bc-wave-loader .loading-logo { display: none; }
.wave-shadow {
  box-shadow: rgba(14, 63, 126, 0.04) 0px 0px 0px 1px, rgba(42, 51, 69, 0.04) 0px 1px 1px -0.5px, rgba(42, 51, 70, 0.04) 0px 3px 3px -1.5px, rgba(42, 51, 70, 0.04) 0px 6px 6px -3px, rgba(14, 63, 126, 0.04) 0px 12px 12px -6px, rgba(14, 63, 126, 0.04) 0px 24px 24px -12px;
}

.third.box {
    width: 150px;
    height: 150px;
    box-shadow:
      1px 2px 2px hsl(220deg 60% 50% / 0.2),
      2px 4px 4px hsl(220deg 60% 50% / 0.2),
      4px 8px 8px hsl(220deg 60% 50% / 0.2),
      8px 16px 16px hsl(220deg 60% 50% / 0.2),
      16px 32px 32px hsl(220deg 60% 50% / 0.2);
  }
.bc-wave-loader .bc_wave {
    position: absolute;
    top:  -10%;
    left: -190%;
    width: 750px;
    height: 750px;
    background-color: #002c55;
    border-radius: 40%;
    animation-name: bc_waves;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
    /* Every JS write to `top` (via setWaveLoaderProgress) now eases
       in instead of snapping. ease-out gives the rise a "water
       settling" feel; bump the duration if you want it slower. */
    transition: top 500ms ease-out;
}
/* Stacking: wave one paints UNDER the logo, waves two + three paint OVER
   it. Logo is at z-index 1 via the .bc-wave-loader .loading-logo rule, so
   the logo is sandwiched between the back-most wave and the front pair. */
.bc-wave-loader .bc_wave.one   { animation-duration: 10000ms; opacity: 0.4; z-index: 0; }
.bc-wave-loader .bc_wave.two   { animation-duration: 12000ms; background-color: #00a3e5; opacity:0.5; z-index: 2; }
.bc-wave-loader .bc_wave.three { animation-duration: 15000ms; z-index: 2; opacity: 0.9 }
@keyframes bc_waves {
    0%   { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

/* ── Error Overlay ─────────────────────────────────────────── */
#error-overlay {
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    background: rgba(8, 12, 24, 0.96);
    z-index: 2000;
    display: flex;
    align-items: center;
    justify-content: center;
}
#error-content {
    text-align: center;
    max-width: 400px;
}
.error-logo {
    display: block;
    height: 48px;
    margin: 0 auto 24px;
    filter: drop-shadow(0 2px 8px rgba(0,0,0,0.6));
}
#error-text {
    font-size: 15px;
    color: #94a3b8;
    margin-bottom: 24px;
    line-height: 1.6;
}
.error-actions {
    display: flex;
    gap: 12px;
    justify-content: center;
}

/* ── User Bar ──────────────────────────────────────────────── */
#user-bar {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 1001;
    display: flex;

    gap: 10px;


    padding: 6px 14px;

}
#user-name {
    font-size: 12px;
    color: #94a3b8;
    font-weight: 500;
    max-width: 180px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── Buttons ───────────────────────────────────────────────── */
.btn-glass {
    display: inline-block;
    padding: 6px 16px;
    border-radius: 6px;
    border: none;
    background: #2563eb;
    color: #fff;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s;
    margin-top: 8px;
}
.btn-glass:hover { background: #1d4ed8; }
.btn-glass-outline {
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.15);
    color: #94a3b8;
}
.btn-glass-outline:hover {
    background: rgba(255,255,255,0.05);
    color: #e2e8f0;
}
.btn-glass-danger {
    display: inline-block;
    padding: 6px 16px;
    border-radius: 6px;
    background: transparent;
    border: 1px solid rgba(239, 68, 68, 0.4);
    color: #f87171;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s;
}
.btn-glass-danger:hover {
    background: rgba(239, 68, 68, 0.12);
    border-color: rgba(239, 68, 68, 0.6);
    color: #fca5a5;
}

/* ── Small Loader ──────────────────────────────────────────── */
#small-loader {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 1500;
}
.small-loader-inner {
    display: flex;
    align-items: center;
    gap: 10px;
    background: rgba(12, 17, 30, 0.88);
    backdrop-filter: blur(10px);
    padding: 12px 20px;
    border-radius: 8px;
    border: 1px solid rgba(255,255,255,0.08);
    font-size: 13px;
    color: #94a3b8;
}

/* ── Toast ─────────────────────────────────────────────────── */
/* Container is just a hold for the DOM nodes; toasts themselves are
   position:fixed so they always land dead-centre regardless of where
   the container sits. */
#toast-container {
    position: fixed;
    bottom: 100px;
    right: 16px;
    z-index: 1800;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
/* Every toast: dead-centre on the screen, large legible text, soft
   scale-in / scale-out. Used for both quick status messages and
   longer instructional prompts. */
.toast-msg {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    padding: 24px 36px;
    font-size: 18px;
    line-height: 1.4;
    text-align: center;
    color: #e2e8f0;
    background: rgba(12, 17, 30, 0.95);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 12px;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
    z-index: 9999;
    max-width: min(560px, calc(100vw - 64px));
    backdrop-filter: blur(10px);
    animation: toast-in 0.3s ease, toast-out 0.3s ease 2.7s forwards;
}
/* Smaller, muted secondary line (e.g. "Press ESC to cancel"). */
.toast-msg-sub {
    display: block;
    margin-top: 8px;
    font-size: 13px;
    color: #94a3b8;
    letter-spacing: 0.4px;
}
/* Persistent bottom-centre toast — shown while a bloom playback runs.
   Manual show/hide (no auto-out animation), smaller padding/font than
   the dead-centre toast since it's an ambient hint, not a prompt. */
.toast-msg.toast-bottom {
    top: auto;
    bottom: 90px;
    left: 50%;
    transform: translateX(-50%);
    padding: 12px 22px;
    font-size: 13px;
    animation: toast-bottom-in 0.3s ease;
}
@keyframes toast-bottom-in {
    from { opacity: 0; transform: translate(-50%, 8px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
}
/* Slim corner toast — bottom-right of the screen. Used for save-
   confirmation hints that fire often (every toggle/slider change),
   so they need to be unobtrusive. Auto-out animation matches the
   1.8s lifetime in showToastMini. */
.toast-msg.toast-mini {
    position: fixed;
    top: auto;
    left: auto;
    transform: none;
    bottom: 24px;
    right: 24px;
    padding: 8px 14px;
    font-size: 12px;
    line-height: 1.3;
    border-radius: 8px;
    background: rgba(15, 23, 42, 0.92);
    border: 1px solid rgba(96, 165, 250, 0.35);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
    z-index: 9999;
    max-width: min(320px, calc(100vw - 64px));
    animation: toast-mini-in 0.2s ease, toast-mini-out 0.25s ease 1.55s forwards;
}
@keyframes toast-mini-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes toast-mini-out {
    from { opacity: 1; transform: translateY(0); }
    to   { opacity: 0; transform: translateY(8px); }
}

.toast-msg.toast-bottom kbd {
    display: inline-block;
    padding: 2px 8px;
    margin: 0 4px;
    background: rgba(255, 255, 255, 0.12);
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 4px;
    font-family: inherit;
    font-size: 12px;
    color: #f1f5f9;
}
@keyframes toast-in {
    from { opacity: 0; transform: translate(-50%, -50%) scale(0.92); }
    to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
@keyframes toast-out {
    from { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    to   { opacity: 0; transform: translate(-50%, -50%) scale(0.96); }
}

/* ── Mobile Responsive ────────────────────────────────────── */
@media (max-width: 768px) {
    #logo-container {
        left: 50%;
        transform: translateX(-50%);
    }
    #logo-container img {
        height: 36px;
    }

    /* Hide AI panel on mobile (alerts panel shares the same class but
       must remain accessible — id-scoped instead of class-wide). */
    #ai-panel {
        display: none !important;
    }

    /* Hide user-bar entry points that aren't useful on mobile. */
    #btn-admin,
    #btn-tips,
    #btn-ai-panel {
        display: none !important;
    }

    /* Hide the right-panel buoys list on mobile — too narrow to read
       and AOIs / alerts cover the use case there. */
    .right-panel-buoys {
        display: none !important;
    }

    /* Make panels responsive on mobile */
    .side-panel,
    .timeline-panel-left,
    #buoys-panel {
        width: calc(100vw - 32px);
        max-width: 190px;
    }


    /* Buoy modal: stack image above status table */
    .buoy-info-header {
        flex-direction: column;
        gap: 12px;
    }
    .buoy-modal-image-wrap {
        flex: 0 0 auto;
    }
    .buoy-image-col {
        flex: none;
    }
    .buoy-image-col img {
        width: 100%;
        max-height: 180px;
    }
    .buoy-status-label {
        min-width: 90px;
    }

    #user-bar {
        top: 20px;
        right: auto;
        left: 50%;
        transform: translateX(-50%);
        padding: 4px 10px;
    }
    #user-name {
        max-width: 120px;
        font-size: 11px;
    }
}

/* ── User Bar Icon Buttons ─────────────────────────────────── */
.user-bar-icon {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(255, 255, 255, 0.06);
    color: #94a3b8;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-size: 13px;
    transition: all 0.2s;
}
.user-bar-icon:hover {
    background: rgba(255, 255, 255, 0.12);
    color: #e2e8f0;
    border-color: rgba(255, 255, 255, 0.2);
}

/* ── Tooltips ─────────────────────────────────────────────── */
/* Pill — matches the dodge-tide-card detail row's shape language. */
.app-tooltip {
    position: fixed;
    z-index: 3000;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 8px;
    border-radius: 10px;
    background: rgb(12, 17, 30);
    color: #cbd5e1;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    white-space: nowrap;
    pointer-events: none;
}

/* ── Tips & Tricks Modal ──────────────────────────────────────
   Two-column "library" layout:
     ┌ header ──────────────────────┐
     │ rail (240px) │ detail (flex) │
     └ footer ──────────────────────┘
   Rail groups tips by module ("General" first); detail mirrors the
   active rail item. Footer keeps the startup toggle and prev/next
   visually separate. Mobile (<640px) collapses to a single column
   with the rail scrolling horizontally above the detail.
   ─────────────────────────────────────────────────────────── */

/* Compound .modal-content.tips-modal-content for higher specificity than the
   base .modal-content rule below (otherwise that 340px width wins on source
   order and squashes the modal to mostly-rail). */
.modal-content.tips-modal-content {
    width: 920px;
    max-width: calc(100vw - 32px);
    max-height: 80vh;
    display: flex;
    flex-direction: column;
    text-align: left;
}

.tips-layout {
    display: flex;
    gap: 16px;
    flex: 1 1 auto;
    min-height: 0; /* lets inner panes scroll instead of growing the modal */
    margin-top: 4px;
}

/* Left rail — categorised list of all (filtered) tips */
.tips-rail {
    flex: 0 0 220px;
    overflow-y: auto;
    border-right: 1px solid rgba(255, 255, 255, 0.06);
    padding-right: 8px;
    padding-bottom: 4px;
}
.tips-rail::-webkit-scrollbar { width: 4px; }
.tips-rail::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 2px; }

/* Accordion section header — clickable; toggles its items. Chevron points
   down when expanded, right when collapsed. */
.tips-rail-section-label {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    font-size: 10px;
    letter-spacing: 1.2px;
    text-transform: uppercase;
    color: #60a5fa;
    font-weight: 700;
    padding: 8px 10px;
    margin-bottom: 4px;
    /* Faded-blue band so each module section header stands out in the rail. */
    background: rgba(96, 165, 250, 0.12);
    border-radius: 6px;
    cursor: pointer;
    user-select: none;
}
.tips-rail-section:first-child .tips-rail-section-label { margin-top: 2px; }
.tips-rail-section-label:hover { color: #93c5fd; background: rgba(96, 165, 250, 0.18); }
.tips-rail-section-chevron {
    font-size: 9px;
    transition: transform 0.2s ease;
}
/* Collapsed: hide the items, point the chevron right. */
.tips-rail-section.collapsed .tips-rail-section-items { display: none; }
.tips-rail-section.collapsed .tips-rail-section-chevron { transform: rotate(-90deg); }

.tips-rail-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border-radius: 8px;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
    color: #94a3b8;
    font-size: 12px;
    line-height: 1.3;
    position: relative;
}
.tips-rail-item:hover {
    background: rgba(255, 255, 255, 0.04);
    color: #e2e8f0;
}
.tips-rail-item.is-active {
    /* No background fill — the left-accent stripe + lighter text mark the
       selection (the blue fill clashed with the section header band). */
    color: #e2e8f0;
}
/* Blue left-accent stripe on active rail item (analogous to settings tabs) */
.tips-rail-item.is-active::before {
    content: '';
    position: absolute;
    left: 0;
    top: 6px;
    bottom: 6px;
    width: 3px;
    border-radius: 0 2px 2px 0;
    background: #60a5fa;
}
.tips-rail-item i {
    flex-shrink: 0;
    width: 16px;
    font-size: 14px;
    color: inherit;
    text-align: center;
}
.tips-rail-item-title {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* "NEW" pill — shown on rail items the current user has never opened.
   Drives the "what's new since last visit" affordance. Removed inline
   when the user opens that tip (see _markTipViewed in app.js). */
.tips-rail-new-pill {
    flex-shrink: 0;
    background: rgba(251, 191, 36, 0.18);  /* warm amber to stand out from the blue accent */
    color: #fbbf24;
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.6px;
    padding: 2px 6px;
    border-radius: 8px;
    margin-left: auto;
}

/* Settings entry in the rail — same row look as a tip item, gear-led. */
.tips-rail-settings-item i { color: #94a3b8; }
.tips-rail-settings-item.is-active i { color: #60a5fa; }

/* Settings sections rendered as accordions in the detail pane, reusing the
   AOI module-section look (.aoi-module-section / .aoi-module-header /
   .aoi-module-chevron / .aoi-module-body) — a pure accordion, no enable
   toggle. The first group opens by default; collapsed groups hide their body. */
.ss-acc.collapsed .ss-acc-body { display: none; }
#tips-detail-settings .settings-section { margin-bottom: 0; }

/* Right detail pane — icon + badge + title at top, body text below */
.tips-detail {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 0 4px 12px 4px;
    overflow-y: auto;
}
.tips-detail::-webkit-scrollbar { width: 4px; }
.tips-detail::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 2px; }

/* Detail header — styled to match the #date-display .dd-date pill: a dark
   bar, flex row, small leading inline icon, uppercase 15px/500 text. (Pure
   styling — no dd-date behaviour.) */
.tips-detail-head {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 10px 14px;
    border-radius: 5px;
    background: #000000;
    color: #ffffff;
}
/* #tips-icon's class is replaced wholesale by renderTip (FA classes), so it
   must be targeted by id, not a static class. Leading inline icon. */
.tips-detail-head #tips-icon {
    font-size: 12px;
    opacity: 0.85;
    flex-shrink: 0;
    color: #ffffff;
}
.tips-detail-badge {
    display: inline-block;
    /* Sits inside the dark .tips-detail-head pill, pushed to the right edge
       (the title's flex:1 takes the slack). flex-shrink:0 keeps it intact.
       Styled to match the .event-label chip (dark #1a1a1a pill, white 10px). */
    flex-shrink: 0;
    background: #1a1a1a;
    color: #ffffff;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    padding: 2px 8px;
    border-radius: 10px;
    white-space: nowrap;
}
.tips-detail-title {
    flex: 1;
    min-width: 0;
    font-size: 15px;
    font-weight: 500;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: #ffffff;
    margin: 0;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
}
.tips-detail-text {
    font-size: 14px;
    line-height: 1.7;
    color: #cbd5e1;
    margin: 10px 0 0;
    /* Honour newlines in the source body — \n in the tip's `body` field
       renders as a line break (equivalent to a <br>), while long lines still
       wrap normally. */
    white-space: pre-line;
}
/* Body row — text + optional image side by side. With no image the text is
   the only flex child and fills the row; with an image present the text takes
   ~half and the image the other half, top-aligned. */
.tips-detail-body-row {
    display: flex;
    gap: 16px;
    align-items: flex-start;
}
.tips-detail-body-row > .tips-detail-text {
    flex: 1 1 0;
    min-width: 0;
}
.tips-detail-image {
    flex: 0 0 50%;
    max-width: 50%;
    height: auto;            /* width fills its half; height auto = keep ratio */
    align-self: flex-start;  /* aligned to the top of the row */
    margin-top: 10px;        /* line up with the text's top margin */
    border-radius: 8px;
    display: block;
}

/* Footer — startup toggle (left) + prev/next arrows (right) */
.tips-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-top: 12px;
    margin-top: 8px;
    border-top: 1px solid rgba(255, 255, 255, 0.06);
    gap: 16px;
    flex-shrink: 0;
}
.tips-startup-toggle {
    margin: 0;
    padding: 0;
    gap: 10px;
    /* Reverse the label order so the switch sits before the text */
    flex-direction: row;
    justify-content: flex-start;
}

.tips-foot-actions {
    display: flex;
    gap: 8px;
}
.tips-arrow {
    width: 32px;
    height: 32px;
    padding: 0;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.10);
    color: #cbd5e1;
    border-radius: 50%;
    font-size: 12px;
    backdrop-filter: none;
    transition: background 0.15s, color 0.15s;
}
.tips-arrow:hover:not([disabled]) {
    background: rgba(255, 255, 255, 0.14);
    color: #ffffff;
}
.tips-arrow[disabled] {
    opacity: 0.3;
    cursor: default;
}
.tips-arrow i { font-size: 12px; }

/* Mobile: collapse rail to a horizontal scroller above the detail */
@media (max-width: 640px) {
    .tips-modal-content {
        width: 100%;
        max-width: 100%;
    }
    .tips-layout {
        flex-direction: column;
        gap: 12px;
    }
    .tips-rail {
        flex: 0 0 auto;
        max-height: 32vh;
        border-right: none;
        border-bottom: 1px solid rgba(255, 255, 255, 0.06);
        padding-right: 0;
        padding-bottom: 8px;
    }
    .tips-detail-title { font-size: 14px; }
    .tips-detail-text { font-size: 13px; }
    /* On narrow viewports the body text + image stack vertically — text takes
       the full row, image sits underneath at full width (no more 50/50 split). */
    .tips-detail-body-row { flex-direction: column; }
    .tips-detail-body-row > .tips-detail-image {
        flex: 0 0 auto;
        max-width: 100%;
        width: 100%;
        margin-top: 0;          /* row's gap handles separation in column mode */
    }
    .tips-footer { flex-wrap: wrap; gap: 12px; }
}

/* ── Modal Overlay ────────────────────────────────────────── */
.modal-overlay {
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    z-index: 2000;
    display: flex;
    align-items: center;
    justify-content: center;
}
/* Dodge-tide loading overlay — centers the loading card on screen with NO
   backdrop (transparent, clicks pass through to the map). Shown while the
   first AI summary loads; the modal stays hidden until it returns. */
.dt-loading-overlay {
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    z-index: 2001;            /* above the modal layer, below the AI panel */
    display: flex;            /* toggled to flex / none in JS */
    align-items: center;
    justify-content: center;
    pointer-events: none;     /* no backdrop — don't block the map */
}
/* Glassy card matching the bloom map-popup look. */
.dt-loading-card {
    pointer-events: auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 22px 26px;
    background: rgba(0, 0, 0, 0.28);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}
/* On-map AOI popup — replaces the old right-pane carousel. Single div
   appended to the Leaflet map container; JS positions it via top/left
   from map.latLngToContainerPoint() and the translate(-50%,-50%) below
   centres it dead-over the AOI's centroid. opacity transition drives
   the fade-on-swap between AOIs. */
.aoi-popup {
    position: absolute;
    transform: translate(-50%, -50%);
    z-index: 600;
    pointer-events: auto;
    transition: opacity 160ms ease-out;
    width: 240px;
}
.aoi-popup .aoi-item {
    /* Mirror the previous carousel card visual: dark calm-blue card with
       the wave layers behind a centred headline + module-pill sub-row. */
    --dt-water-level: 42%;
    --aoi-bleed-right: 0;
    position: relative;
    overflow: hidden;
    min-height: 56px;
    padding: 12px 12px 14px;
    border-radius: 8px;
    background: rgb(0 51 109 / 90%);
    border: 1px solid rgba(96, 165, 250, 0.25);
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
}
/* Close button in the top-right corner of the AOI popup — mirrors the
   look of Leaflet's bloom-event popup close affordance. Stays inside the
   card bounds since .aoi-item has overflow: hidden. */
.aoi-popup .aoi-popup-close {
    position: absolute;
    top: 4px;
    right: 4px;
    z-index: 2;
    width: 18px;
    height: 18px;
    padding: 0;
    border: none;
    border-radius: 4px;
    background: transparent;
    color: rgba(191, 219, 254, 0.7);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    transition: color 0.15s ease, background 0.15s ease;
}
.aoi-popup .aoi-popup-close:hover {
    color: #ffffff;
    background: rgba(255, 255, 255, 0.12);
}
.aoi-popup .aoi-popup-close .text {
    font-size: 14px;
    font-weight: 700;
    line-height: 1;
}
.aoi-popup .aoi-item > .dt-panel-action-link {
    display: inline-block;
    padding: 2px 8px;
    background: transparent;
    border: none;
    color: #bfdbfe;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
    position: relative;
    z-index: 1;
    cursor: pointer;
    /* Dotted underline signals the name opens the AOI edit modal —
       same affordance as chartable measurement labels in the buoy modal. */
    text-decoration: underline dotted rgba(191, 219, 254, 0.55);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    transition: color 0.15s ease, text-decoration-color 0.15s ease;
}
.aoi-popup .aoi-item > .dt-panel-action-link:hover {
    color: #ffffff;
    text-decoration-color: #ffffff;
}
.aoi-popup .aoi-item > .dt-panel-action-row {
    display: flex;
    align-items: center;
    justify-content: center;
    align-self: stretch;
    gap: 4px;
    position: relative;
    z-index: 1;
}
.aoi-popup .aoi-item > .dt-panel-action-row > .mode-badge {
    flex: 1 1 0;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Module badges (excluding the inert "No alerts today" placeholder) are
   clickable shortcuts — they re-fire the matching side-panel event card's
   click action. Subtle hover lift to signal interactivity. */
.aoi-popup .aoi-item .aoi-module-badge {
    cursor: pointer;
    transition: filter 0.15s ease, transform 0.15s ease;
}
.aoi-popup .aoi-item .aoi-module-badge:hover {
    filter: brightness(1.2);
    transform: translateY(-1px);
}
.aoi-popup .aoi-item .dt-water-gradient {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: var(--dt-water-level);
    pointer-events: none;
    z-index: 0;
    border-radius: 0 0 8px 8px;
    background: linear-gradient(180deg,
        rgba(52, 97, 193, 0.10)  0%,
        rgba(45, 85, 170, 0.42)  50%,
        rgba(37, 33, 33, 0.55)  100%);
    opacity: 0.5;
}
.aoi-popup .aoi-item .dt-wave-back,
.aoi-popup .aoi-item .dt-wave-front {
    position: absolute;
    left: 0;
    right: 0;
    width: 100%;
    height: 22px;
    bottom: 0;
    margin-bottom: -12px;
    pointer-events: none;
    z-index: 0;
    opacity: 0.45;
}
.aoi-popup .aoi-item .dt-wave-back use:nth-of-type(1) { fill: rgba(52, 97, 193, 0.80); }
.aoi-popup .aoi-item .dt-wave-back use:nth-of-type(2) { fill: rgba(45, 85, 170, 0.90); }
.aoi-popup .aoi-item .dt-wave-front use { fill: rgba(69, 121, 226, 0.55); }
.aoi-popup .aoi-item .dt-wave-back use,
.aoi-popup .aoi-item .dt-wave-front use {
    animation: dt-wave-move 12s linear infinite;
}
.aoi-popup .aoi-item .dt-wave-back use:nth-of-type(1) { animation-delay: -2s; animation-duration: 15s; }
.aoi-popup .aoi-item .dt-wave-back use:nth-of-type(2) { animation-delay: -4s; animation-duration: 18s; }
.aoi-popup .aoi-item .dt-wave-front use:nth-of-type(1) { animation-delay: -12s; animation-duration: 9s; }

/* On-map name label hung below an AOI's south edge. */
.aoi-name-label {
    background: transparent !important;
    border: none !important;
    width: auto !important;
    height: auto !important;
    pointer-events: none;
}
.aoi-name-label > span {
    display: inline-block;
    transform: translate(-50%, 6px);
    color: #bfdbfe;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85), 0 0 2px rgba(0, 0, 0, 0.7);
    white-space: nowrap;
}
.modal-backdrop {
    position: absolute;
    top: 0; left: 0; right: 0; bottom: 0;
    background: rgba(0, 0, 0, 0.5);
    /* Pin the dim layer at the bottom of the overlay's stacking context so
       modal content always sits above it. Content sits higher via
       .modal-content (z-index 1051) or .dt-modal-stack (z-index 1). Without
       an explicit level here, a non-positioned content wrapper paints behind
       this absolutely-positioned backdrop. */
    z-index: 0;
}
.modal-content {
    position: relative;
    width: 340px;
    max-height: 80vh;
    overflow-y: auto;
    z-index: 1051;
}
.modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 16px;
}
.modal-title {
    font-weight: 600;
    letter-spacing: 1.5px;
     color: #60a5fa;
    font-size: 1rem;
    margin: 0;
}
/* Buoy name + nearest-landmass location share the header's left side,
   pipe-delimited. Location is a quieter, smaller subtitle. */
.modal-title-wrap {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    min-width: 0;
}
/* Styled like .mode-badge.mode-badge-inline.days-badge — a dark pill with
   a leading location-pin icon. */
/* Buoy-modal location + lat/lng — matched to .buoy-status-label (default sans
   font, 12px, #64748b) so the header reads with the status rows below. The
   coords line stays click-to-copy. */
.modal-title-location,
.buoy-modal-coords {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    color: #64748b;
    white-space: nowrap;
}
.modal-title-location {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
}
.buoy-modal-coords {
    cursor: pointer;
    transition: color 0.15s;
}
.buoy-modal-coords:hover { color: #94a3b8; }
.modal-title-location i,
.buoy-modal-coords i { font-size: 0.85em; opacity: 0.85; flex-shrink: 0; margin-right: 4px; }
.modal-title-location:empty,
.buoy-modal-coords:empty { display: none; }
.modal-close {
    background: none;
    border: none;
    color: #64748b;
    font-size: 20px;
    cursor: pointer;
    padding: 0;
    line-height: 1;
    transition: color 0.2s;
}
.modal-close:hover {
    color: #e2e8f0;
}

/* ── AOI List Items ───────────────────────────────────────── */
:root {
    --aoi-bleed-left: 0px;
    --aoi-bleed-right: 30px;
}
.aoi-item {
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    padding-left: calc(var(--aoi-bleed-left) + 12px);
    padding-right: calc(var(--aoi-bleed-right) + 12px);
    margin-bottom: 6px;
    border-radius: 8px;
    cursor: pointer;
    /* Was a hover-only translucent lift; now the always-on background. */
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.12);
    transition: all 0.2s ease;
}
.aoi-color-swatch {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: var(--aoi-bleed-left);
    border-radius: 0;
    flex-shrink: 0;
}
.aoi-item-info {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
}
/* AOI name pill — mirrors .dt-tide-event-label (dark pill, white text). */
.aoi-item-name {
    display: inline-block;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    padding: 2px 8px;
    border-radius: 10px;
    color: #ffffff;
    background: #1a1a1a;
    text-transform: uppercase;
    animation: none;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
/* Module tags — mirror .dodge-tide-risk (blue pill, dark text). */
.aoi-item-modules {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    max-width: 100%;
}
.aoi-module-tag {
    display: inline-block;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    padding: 2px 8px;
    border-radius: 10px;
    color: #1a1a1a;
    background: #60a5fa;
    text-transform: uppercase;
    animation: none;
    white-space: nowrap;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.aoi-item-actions {
    position: absolute;
    right: 0;
    top: 0;
    bottom: 0;
    width: var(--aoi-bleed-right);
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
    gap: 0;
}
.aoi-item-btn {
    flex: 1;
    width: 100%;
    height: auto;
    min-height: 0;
    border: none;
    border-radius: 0;
    padding: 0;
    font-size: 12px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: filter 0.15s ease, background-color 0.15s ease;
}
/* Defeat the element-level Uiverse `button:after` skewed overlay so
   these compact split-buttons stay flat. */
.aoi-item-btn::after { display: none !important; }

.aoi-item-btn.edit {
    background: #475569;        /* slate-600 — neutral grey */
    color: #e2e8f0;
}
.aoi-item-btn.edit:hover {
    background: #64748b;        /* slate-500 */
    color: #fff;
}
.aoi-item-btn.delete {
    background: rgba(220, 38, 38, 0.2);
    color: #fff;
}
.aoi-item-btn.delete:hover {
    background: rgba(239, 68, 68, 0.35);
    color: #fff;
}
.aoi-empty {
    text-align: center;
    padding: 20px 0;
    font-size: 12px;
    color: #64748b;
}

/* ── AOI Form ─────────────────────────────────────────────── */
.aoi-form-group {
    margin-bottom: 14px;
}
.aoi-form-label {
    display: block;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    color: #64748b;
    margin-bottom: 6px;
}
.aoi-form-input {
    width: 100%;
    padding: 8px 12px;
    border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(255, 255, 255, 0.04);
    color: #e2e8f0;
    font-size: 13px;
    outline: none;
    transition: border-color 0.2s;
}
.aoi-form-input:focus {
    border-color: rgba(96, 165, 250, 0.5);
}
.aoi-form-range {
    width: 100%;
    accent-color: #60a5fa;
}
.aoi-form-select {
    width: 100%;
    padding: 8px 12px;
    border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(255, 255, 255, 0.04);
    color: #e2e8f0;
    font-size: 13px;
    outline: none;
    cursor: pointer;
    transition: border-color 0.2s;
}
.aoi-form-select:focus {
    border-color: rgba(96, 165, 250, 0.5);
}
.aoi-form-select option {
    background: #1a1f2e;
    color: #e2e8f0;
}
.aoi-form-color {
    width: 40px;
    height: 28px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 4px;
    background: none;
    cursor: pointer;
    padding: 1px;
}
/* ── AOI Module Options ── */
.aoi-module-section {
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 8px;
    margin-top: 10px;
    overflow: hidden;
}
.aoi-module-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 12px;
    background: rgba(255,255,255,0.03);
    cursor: pointer;
}
.aoi-module-header.aoi-module-enabled {
    background: rgba(96, 165, 250, 0.16);
}
.aoi-module-header.aoi-module-disabled {
    background: rgba(239, 68, 68, 0.16);
}
.aoi-module-chevron {
    font-size: 10px;
    color: #64748b;
    transition: transform 0.2s;
}
.aoi-module-chevron.open {
    transform: rotate(90deg);
}
.aoi-module-name {
    font-size: 12px;
    font-weight: 600;
    color: #e2e8f0;
}
/* When .aoi-module-name is a direct child of the section (no longer
   wrapped in the old accordion .aoi-module-header), give it a header
   strip look so it reads as a section title without the chevron. */
.aoi-module-section > .aoi-module-name {
    padding: 8px 12px;
    background: rgba(255, 255, 255, 0.03);
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.aoi-module-body {
    padding: 8px 12px 12px;
    border-top: 1px solid rgba(255,255,255,0.06);
}
.aoi-module-body .aoi-form-group {
    margin-bottom: 8px;
}
.aoi-module-body .aoi-form-group:last-child {
    margin-bottom: 0;
}
/* Shared row for Shape + Color — Shape stretches, Color hugs its content. */
.aoi-module-body .aoi-form-row {
    display: flex;
    align-items: flex-end;
    gap: 12px;
    margin-bottom: 8px;
}
.aoi-module-body .aoi-form-row .aoi-form-group {
    margin-bottom: 0;
}
.aoi-module-body .aoi-form-row .aoi-form-group-shape {
    flex: 1 1 auto;
    min-width: 0;
}
.aoi-module-body .aoi-form-row .aoi-form-group-color {
    flex: 0 0 auto;
}

.aoi-form-input.error {
    border-color: rgba(239, 68, 68, 0.6);
    animation: shake 0.3s ease;
}
@keyframes shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-4px); }
    75% { transform: translateX(4px); }
}

/* AOI placement-method chooser modal */
.aoi-method-content {
    max-width: 360px;
}
.aoi-method-prompt {
    font-size: 13px;
    color: #94a3b8;
    margin: 4px 0 14px;
}
/* View toggle: chooser shown by default, coords revealed via .show-coords */
.aoi-method-content .aoi-method-view--coords {
    display: none;
}
.aoi-method-content.show-coords .aoi-method-view--chooser {
    display: none;
}
.aoi-method-content.show-coords .aoi-method-view--coords {
    display: block;
}
/* Entry-format field groups: DD shows by default; entry-ddm/entry-dms swap it. */
.aoi-method-content .aoi-entry-ddm,
.aoi-method-content .aoi-entry-dms { display: none; }
.aoi-method-content.entry-ddm .aoi-entry-dd,
.aoi-method-content.entry-dms .aoi-entry-dd { display: none; }
.aoi-method-content.entry-ddm .aoi-entry-ddm { display: block; }
.aoi-method-content.entry-dms .aoi-entry-dms { display: block; }

/* One inline row of component boxes + a hemisphere select per axis. */
.aoi-coord-row {
    display: flex;
    align-items: center;
    gap: 6px;
}
.aoi-coord-row .aoi-form-input { width: auto; }
.aoi-coord-row .aoi-coord-deg { flex: 1 1 0; min-width: 0; }
.aoi-coord-row .aoi-coord-min,
.aoi-coord-row .aoi-coord-sec { flex: 1 1 0; min-width: 0; }
.aoi-coord-row .aoi-coord-hemi { width: 58px; flex: 0 0 auto; padding: 8px 6px; }
.aoi-coord-unit {
    color: #64748b;
    font-size: 13px;
    margin-left: -2px;
}
.aoi-method-option {
    display: block;
    width: 100%;
    margin-bottom: 10px;
    padding: 14px 16px;
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(255, 255, 255, 0.04);
    color: #e2e8f0;
    font-size: 14px;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.2s, background 0.2s;
}
.aoi-method-option:last-child {
    margin-bottom: 0;
}
.aoi-method-option:hover {
    border-color: rgba(96, 165, 250, 0.5);
    background: rgba(96, 165, 250, 0.08);
}
.aoi-method-option .text i {
    margin-right: 10px;
    color: #60a5fa;
}
/* These custom option buttons don't use the animated-button second span */
.aoi-method-option > span:last-child {
    display: none;
}

.aoi-name-loading {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: #94a3b8;
    padding: 8px 0;
}
.aoi-name-spinner {
    display: inline-block;
    width: 14px;
    height: 14px;
    border: 2px solid rgba(96, 165, 250, 0.3);
    border-top-color: #60a5fa;
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
}

/* ── Placement Mode ───────────────────────────────────────── */
.placement-mode .leaflet-container {
    cursor: crosshair !important;
}

/* Live lat/lng readout shown above the AOI placement ghost (see
   enterPlacementMode). Dark pill, monospace coords, arrow hidden. */
.leaflet-tooltip.aoi-placement-coords {
    background: rgba(15, 23, 42, 0.92);
    color: #e2e8f0;
    border: 1px solid rgba(59, 130, 246, 0.65);
    border-radius: 6px;
    padding: 3px 8px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.2px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
    white-space: nowrap;
    pointer-events: none;
}
.leaflet-tooltip.aoi-placement-coords::before {
    display: none;
}

/* ── AOI Marker Icon ──────────────────────────────────────── */
.aoi-marker-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.4));
}

/* ── Buoy Map Markers ─────────────────────────────────────── */
.buoy-marker-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.5));
    transition: transform 0.15s ease;
    overflow: visible;
    position: relative;
}
.buoy-marker-icon:hover {
    transform: scale(1.2);
}
.buoy-marker-icon img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    border-radius: 4px;
    position: relative;
    z-index: 100;
}

/* ── Buoy Pulse Rings ────────────────────────────────────── */
.buoy-pulse-ring {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 36px;
    height: 36px;
    margin-top: -18px;
    margin-left: -18px;
    border-radius: 50%;
    border: 2px solid currentColor;
    opacity: 0;
    z-index: 1;
    animation: buoy-pulse 2.8s ease-out infinite;
    pointer-events: none;
}
.buoy-pulse-ring.ring-2 { animation-delay: 0.9s; }
.buoy-pulse-ring.ring-3 { animation-delay: 1.8s; }
.pulse-unknown  { color: #94a3b8; }
.pulse-healthy  { color: #bfdbfe; }
.pulse-warning     { color: #eab308; }
.pulse-critical { color: #ef4444; }

/* Alarm-blaring pulse — fixed size, hard on/off blink. 300 ms on, 300 ms off. */
.buoy-pulse-ring.pulse-warning,
.buoy-pulse-ring.pulse-critical {
    border-width: 3px;
    transform: scale(1.6);
    animation-name: buoy-pulse-blink;
    animation-duration: 0.6s;
    animation-timing-function: steps(1, end);
    animation-delay: 0s !important;
}
.buoy-pulse-ring.pulse-warning {
    /* Fill the ring's centre with the warning hue at 50 % alpha so the
       blink reads as a filled disc rather than a hollow outline. */
    background-color: rgba(234, 179, 8, 0.5);
}
.buoy-pulse-ring.pulse-critical {
    background-color: rgba(239, 68, 68, 0.5);
    filter: drop-shadow(0 0 5px currentColor);
}
/* Only one visible ring in alarm mode — extras would just overlay identically */
.buoy-pulse-ring.pulse-warning.ring-2,
.buoy-pulse-ring.pulse-warning.ring-3,
.buoy-pulse-ring.pulse-critical.ring-2,
.buoy-pulse-ring.pulse-critical.ring-3 {
    display: none;
}
@keyframes buoy-pulse-blink {
    0%, 49.9%  { opacity: 1; }
    50%, 100%  { opacity: 0; }
}
/* Dodge tide risk pulse colors */
.pulse-low         { color: #fb923c; }
.pulse-medium      { color: #eab308; }
.pulse-high-dt     { color: #dc2626; }
.pulse-critical-dt { color: #991b1b; }
/* AOI pulse rings — same animation as buoy rings */
.aoi-pulse-ring {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 36px;
    height: 36px;
    margin-top: -18px;
    margin-left: -18px;
    border-radius: 50%;
    border: 2px solid currentColor;
    opacity: 0;
    z-index: 0;
    animation: buoy-pulse 2.8s ease-out infinite;
    pointer-events: none;
}
.aoi-pulse-ring.ring-2 { animation-delay: 0.9s; }
.aoi-pulse-ring.ring-3 { animation-delay: 1.8s; }
.aoi-marker-icon { position: relative; overflow: visible; }
.aoi-pulse-center { position: relative; overflow: visible; }
@keyframes buoy-pulse {
    0% {
        transform: scale(1);
        opacity: 0.6;
    }
    100% {
        transform: scale(2.8);
        opacity: 0;
    }
}

/* ── Buoy Modal ──────────────────────────────────────────── */
.buoy-modal-content {
    position: relative;
    width: 580px;
    max-width: 95vw;
    max-height: 85vh;
    overflow: hidden;
    z-index: 1051;
    transition: width 0.3s ease, max-width 0.3s ease, max-height 0.3s ease;
}
.buoy-modal-content.telemetry-fullscreen {
    width: 98vw;
    max-width: 98vw;
    height: 96vh;
    max-height: 96vh;
}
/* While the chart is open, hide everything else in the buoy modal so the
   chart reads as its own modal — the buoy carousel + header reappear when
   the chart closes (telemetry-fullscreen class is removed). */
.buoy-modal-content.telemetry-fullscreen > *:not(.telemetry-chart-overlay) {
    display: none;
}
.buoy-carousel-wrapper {
    position: relative;
    overflow: hidden;
}
.buoy-carousel-track {
    display: flex;
    transition: transform 0.35s ease;
}
.buoy-slide {
    min-width: 100%;
    overflow-y: auto;
    max-height: calc(85vh - 70px);
    padding: 0 4px;
}

/* Navigation arrows — positioned outside the modal */
.buoy-nav-arrow {
    position: fixed;
    top: 50%;
    transform: translateY(-50%);
    /* z-index lower than .modal-overlay (2000) so a fullscreen
       modal-content covers the arrows. In non-fullscreen mode the
       overlay is transparent and the arrows still read fine over
       the dimmed backdrop. */
    z-index: 1900;
    background: rgba(12, 17, 30, 0.85);
    border: 1px solid rgba(255,255,255,0.1);
    color: #e2e8f0;
    font-size: 28px;
    line-height: 1;
    width: 40px;
    height: 52px;
    /* Centre the chevron glyph both axes inside the button. */
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border-radius: 6px;
    cursor: pointer;
    backdrop-filter: blur(8px);
    transition: background 0.2s;
}
.buoy-nav-arrow:hover { background: rgba(30, 40, 60, 0.95); }
/* Hide the side-arrows whenever the modal-content is in fullscreen
   so they don't appear over the maximised chart view. */
#buoy-modal:has(.buoy-modal-content.telemetry-fullscreen) .buoy-nav-arrow {
    display: none;
}
.buoy-nav-prev { left: max(8px, calc(50% - 342px)); }
.buoy-nav-next { right: max(8px, calc(50% - 342px)); }

/* Modal buoy nav arrows borrow the lighter, rounder look of the in-modal
   date arrows (.dt-date-nav-arrow) rather than the heavier dark-glass
   chrome of the base .buoy-nav-arrow rule. Positioning (position: fixed;
   top: 50%; transform: …; left/right offset) still comes from
   .buoy-nav-arrow and .buoy-nav-prev / .buoy-nav-next so the buttons stay
   flanking the modal edges. */
#buoy-nav-prev,
#buoy-nav-next {
    width: 32px;
    height: 32px;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.10);
    color: #cbd5e1;
    border-radius: 50%;
    font-size: 13px;
    backdrop-filter: none;
    transition: background 0.15s, color 0.15s;
}
#buoy-nav-prev:hover:not([disabled]),
#buoy-nav-next:hover:not([disabled]) {
    background: rgba(255, 255, 255, 0.14);
    color: #ffffff;
}
#buoy-nav-prev[disabled],
#buoy-nav-next[disabled] {
    opacity: 0.3;
    cursor: default;
}
#buoy-nav-prev i,
#buoy-nav-next i {
    font-size: 13px;
}

/* Info header row — 50:50 split on large screens; stacked at <=768px via the
   media query below. Image-col stays at its intrinsic 160px and is centered
   inside the wider wrap so the visual stays compact even when the wrap is
   half the modal width. */
.buoy-info-header {
    display: flex;
    gap: 16px;
    margin-bottom: 16px;
}
.buoy-modal-image-wrap {
    position: relative;
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    justify-content: center;
}
.buoy-modal-image-wrap .buoy-image-col { flex: 1 1 auto; width: 100%; }
/* Health pill at the bottom of the modal's image column, same styling
   as list-item pills. */
.buoy-modal-image-wrap > .buoy-list-name {
    position: absolute;
    left: 50%;
    top: 13px;
    transform: translateX(-50%);
    z-index: 100;
}
/* Hide the buoy-name overlay in the MODAL image column only — the modal title
   already names the buoy, so the on-image pill is redundant here. The same
   .buoy-image-name overlay is kept on the side-panel list cards (where it's the
   only place the name shows). buildBuoyImageCol() is shared by both contexts,
   so this is scoped by the modal wrapper rather than removed from the markup.
   The overlay is position:absolute + pointer-events:none, so display:none can't
   affect the modal image layout. */
.buoy-modal-image-wrap .buoy-image-name { display: none; }
.buoy-image-col {
    flex: 0 0 160px;
    position: relative;
    overflow: hidden;
    border-radius: 8px;
}
/* Buoy name dead-centered over the image column. Visual treatment mirrors
   .dt-tide-event-label (dark pill, white uppercase text) at 20% opacity so
   the label is unmistakably present but recedes against the water imagery.
   z-index keeps it above the waves + water-gradient layers (3 / 4). */
.buoy-image-name {
    position: absolute;
    top: 75%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 10;
    display: inline-block;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.6px;
    padding: 2px 8px;
    border-radius: 10px;
    color: #ffffff;
    background: #1a1a1a;
    text-transform: uppercase;
    animation: none;
    max-width: calc(100% - 16px);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
    opacity: 0.8;
    pointer-events: none;
}
.buoy-image-col img {
    width: 160px;
    height: auto;
    max-height: 200px;
    object-fit: contain;
    border-radius: 8px;
    display: block;
    position: relative;
    z-index: 2;
    margin: 20px auto 0;
    animation: buoy-bob 4.5s ease-in-out infinite;
}
/* Semi-transparent water body — sits ON TOP of the image to submerge it,
   extends from the bottom of the back-waves SVG (110px) down to container bottom.
   Gradient fades from near-transparent at the surface to deeper blue near the bottom,
   matching the back-wave palette so the water feels continuous. */
.buoy-water-gradient {
    position: absolute;
    left: 0;
    right: 0;
    top: 110px;
    bottom: 0;
    background: linear-gradient(180deg,        rgba(52, 97, 193, 1)  0%,rgba(52, 97, 193, 0.1)  50%,        rgba(45, 85, 170, 0.1)  45%,        rgba(37, 33,  33, 0.34)  100%);
    z-index: 3;
    pointer-events: none;
    border-radius: 0 0 8px 8px;
}
/* Parallax wave layers — back waves sit behind the image,
   front wave sits in front with transparency.
   Both SVGs are positioned on the waterline (95px from top). */
.buoy-back-waves,
.buoy-front-wave {
    position: absolute;
    left: 0;
    right: 0;
    top: 80px;
    width: 100%;
    height: 30px;
    pointer-events: none;
}
.buoy-back-waves { z-index: 1; }
.buoy-front-wave { z-index: 5; }
.buoy-back-waves use,
.buoy-front-wave use {
    animation: buoy-wave-move 12s linear infinite;
}
.buoy-back-waves use:nth-child(1) { animation-delay: -2s; animation-duration: 15s; }
.buoy-back-waves use:nth-child(2) { animation-delay: -4s; animation-duration: 18s; }
.buoy-front-wave use:nth-child(1) { animation-delay: -12s; animation-duration: 9s; }
@keyframes buoy-wave-move {
    0%   { transform: translate(-90px, 0); }
    100% { transform: translate(85px, 0); }
}
@keyframes buoy-bob {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-3px); }
}
.buoy-status-col {
    flex: 1 1 0;
    min-width: 0;
    border-radius: 8px;
}
.buoy-device-health {
    margin-left: 4px;
    padding: 1px 6px;
    border-radius: 4px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 1px;
    transition: background 0.3s, color 0.3s;
}
.buoy-device-health[data-health="unknown"] {
    background: rgba(148, 163, 184, 0.18);
    color: #94a3b8;
}
.buoy-device-health[data-health="healthy"] {
    background: rgb(0 51 109 / 90%);
    color: #bfdbfe;
}
.buoy-device-health[data-health="warning"] {
    background: rgba(234, 179, 8, 0.22);
    color: #eab308;
}
.buoy-device-health[data-health="critical"] {
    background: rgba(239, 68, 68, 0.22);
    color: #ef4444;
    animation: critical-pulse 1.5s ease-in-out infinite;
}
.buoy-device-type {
    font-size: 11px;
    font-weight: 600;
    /* Names shown as written (no upper-casing) — Class Model Series, built by
       the backend's display_pretty fragment. */
    letter-spacing: 1.2px;
    color: #60a5fa;
    margin-bottom: 10px;
}
/* Series' lowercase i's are <em>-wrapped in display_pretty; render italic
   explicitly (keep the label's weight so the em doesn't read as a separate
   style). */
.buoy-device-type em {
    font-style: italic;
    font-weight: inherit;
}
.buoy-status-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 0;
    font-size: 12px;
    color: #cbd5e1;
    border-bottom: 1px solid rgba(255,255,255,0.04);
}
.buoy-status-row:last-child { border-bottom: none; }
/* Rows for chartable measurements keep the slight horizontal inset
   (so the dotted-underline label aligns with the rest of the column)
   but the row itself is no longer the clickable target — the label
   inside is. See .buoy-status-row--chartable .buoy-status-label below. */
.buoy-status-row.buoy-status-row--chartable {
    border-radius: 4px;
    padding-left: 4px;
    padding-right: 4px;
    margin-left: -4px;
    margin-right: -4px;
}
.buoy-status-row.buoy-status-row--chartable .buoy-status-label {
    cursor: pointer;
    /* Dotted underline signals the label opens the measurement popup
       (same affordance as .buoy-measurement-name inside the tile grid). */
    text-decoration: underline dotted rgba(203, 213, 225, 0.5);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    transition: color 0.15s ease, text-decoration-color 0.15s ease;
}
.buoy-status-row.buoy-status-row--chartable .buoy-status-label:hover {
    color: #ffffff;
    text-decoration-color: #ffffff;
}
.buoy-status-icon {
    flex: 0 0 16px;
    width: 16px;
    font-size: 13px;
    text-align: center;
    color: #64748b;
}
.buoy-status-label {
    color: #64748b;
    flex: 0 0 auto;
    min-width: 110px;
}
.buoy-status-value {
    /* Back to white (2026-07-21) — estimated readings are marked by the
       tile's wand icon + Estimated Read popup, not by text colour. */
    color: #e2e8f0;
    font-weight: 500;
    transition: opacity 0.4s ease;
}
.buoy-status-value.telemetry-stale {
    /* Steady dim only — the old stale-pulse opacity animation read as
       annoying flicker on cache-seeded values that live packets rarely
       re-confirm. The class lifecycle is unchanged. */
    opacity: 0.4;
}
.buoy-status-row {
    transition: background 0.4s;
    border-radius: 4px;
    padding: 2px 4px;
    margin: 0 -4px;
}
.buoy-status-row.telemetry-updated {
    background: rgba(96, 165, 250, 0.12);
}

/* ── Buoy Health Status (WS liveness-driven) ──────────────────
   Applied to .buoy-slide based on WS payload recency:
   - healthy:  recent WS message
   - high:     no message in 30 s (fetched last-known values from BQ)
   - critical: no message in 5 min
*/
.buoy-slide.telemetry-warning .buoy-device-type {
    color: #eab308;
}
.buoy-slide.telemetry-warning .buoy-status-col {
    padding-left: 10px;
    margin-left: 0;
}
.buoy-slide.telemetry-warning .buoy-status-value[data-status="last_transmission"] {
    color: #eab308;
    font-weight: 600;
}

.buoy-slide.telemetry-critical .buoy-device-type {
    color: #ef4444;
    animation: critical-pulse 1.5s ease-in-out infinite;
}
.buoy-slide.telemetry-critical .buoy-status-col {
    padding-left: 10px;
    margin-left: 0;
}
.buoy-slide.telemetry-critical .buoy-status-value[data-status="last_transmission"] {
    color: #ef4444;
    font-weight: 700;
    animation: critical-pulse 1.5s ease-in-out infinite;
}
/* Measurement-grid colors are intentionally NOT tinted by telemetry health —
   data values stay the same regardless of alert state so the reading itself
   isn't editorialised by the alert system. Wave/water gradients still react
   to telemetry status below; only the numeric data points stay neutral. */
@keyframes critical-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.55; }
}

/* Wave + water gradient hues follow the telemetry health status.
   CSS `fill` overrides the inline SVG `fill` attribute, and the water
   gradient rule fully replaces the default blue stack. */

/* UNKNOWN — greyscale (default on WS open, before any payload) */
.buoy-slide.telemetry-unknown .buoy-back-waves use:nth-of-type(1) { fill: rgba(100, 116, 139, 0.80); }
.buoy-slide.telemetry-unknown .buoy-back-waves use:nth-of-type(2) { fill: rgba(71, 85, 105, 0.90); }
.buoy-slide.telemetry-unknown .buoy-front-wave use                { fill: rgba(148, 163, 184, 0.55); }
.buoy-slide.telemetry-unknown .buoy-water-gradient {
    background: linear-gradient(180deg,
        rgba(100, 116, 139, 0.15)  0%,
        rgba(71, 85, 105, 0.40)   45%,
        rgba(51, 65, 85, 0.60)   100%);
}

/* HIGH — amber (no message for 30s) */
.buoy-slide.telemetry-warning .buoy-back-waves use:nth-of-type(1) { fill: rgba(202, 138, 4, 0.80); }
.buoy-slide.telemetry-warning .buoy-back-waves use:nth-of-type(2) { fill: rgba(161, 98, 7, 0.90); }
.buoy-slide.telemetry-warning .buoy-front-wave use                { fill: rgba(234, 179, 8, 0.55); }
.buoy-slide.telemetry-warning .buoy-water-gradient {
    background: linear-gradient(180deg,
        rgba(202, 138, 4, 0.15)  0%,
        rgba(161, 98, 7, 0.40)  45%,
        rgba(113, 63, 18, 0.60) 100%);
}

/* CRITICAL — red (no message for 5 min) */
.buoy-slide.telemetry-critical .buoy-back-waves use:nth-of-type(1) { fill: rgba(220, 38, 38, 0.80); }
.buoy-slide.telemetry-critical .buoy-back-waves use:nth-of-type(2) { fill: rgba(185, 28, 28, 0.90); }
.buoy-slide.telemetry-critical .buoy-front-wave use                { fill: rgba(239, 68, 68, 0.55); }
.buoy-slide.telemetry-critical .buoy-water-gradient {
    background: linear-gradient(180deg,
        rgba(220, 38, 38, 0.15)  0%,
        rgba(185, 28, 28, 0.40)  45%,
        rgba(127, 29, 29, 0.60) 100%);
}

/* Buoy list items — mirror the same status-driven hues on the mini image col */
.buoy-list-item.telemetry-unknown .buoy-back-waves use:nth-of-type(1) { fill: rgba(100, 116, 139, 0.80); }
.buoy-list-item.telemetry-unknown .buoy-back-waves use:nth-of-type(2) { fill: rgba(71, 85, 105, 0.90); }
.buoy-list-item.telemetry-unknown .buoy-front-wave use                { fill: rgba(148, 163, 184, 0.55); }
.buoy-list-item.telemetry-unknown .buoy-water-gradient {
    background: linear-gradient(180deg,
        rgba(100, 116, 139, 0.15)  0%,
        rgba(71, 85, 105, 0.40)   45%,
        rgba(51, 65, 85, 0.60)   100%);
}
.buoy-list-item.telemetry-warning .buoy-back-waves use:nth-of-type(1) { fill: rgba(202, 138, 4, 0.80); }
.buoy-list-item.telemetry-warning .buoy-back-waves use:nth-of-type(2) { fill: rgba(161, 98, 7, 0.90); }
.buoy-list-item.telemetry-warning .buoy-front-wave use                { fill: rgba(234, 179, 8, 0.55); }
.buoy-list-item.telemetry-warning .buoy-water-gradient {
    background: linear-gradient(180deg,
        rgba(202, 138, 4, 0.15)  0%,
        rgba(161, 98, 7, 0.40)  45%,
        rgba(113, 63, 18, 0.60) 100%);
}
.buoy-list-item.telemetry-critical .buoy-back-waves use:nth-of-type(1) { fill: rgba(220, 38, 38, 0.80); }
.buoy-list-item.telemetry-critical .buoy-back-waves use:nth-of-type(2) { fill: rgba(185, 28, 28, 0.90); }
.buoy-list-item.telemetry-critical .buoy-front-wave use                { fill: rgba(239, 68, 68, 0.55); }
.buoy-list-item.telemetry-critical .buoy-water-gradient {
    background: linear-gradient(180deg,
        rgba(220, 38, 38, 0.15)  0%,
        rgba(185, 28, 28, 0.40)  45%,
        rgba(127, 29, 29, 0.60) 100%);
}

/* Measurement group tabs */
.buoy-group-tabs {
    display: flex;
    gap: 4px;
    margin-bottom: 12px;
    border-bottom: 1px solid rgba(255,255,255,0.06);
    padding-bottom: 0;
}
.buoy-group-tab {
    padding: 6px 14px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: #64748b;
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    cursor: pointer;
    transition: color 0.2s, border-color 0.2s;
    margin-bottom: -1px;
}
.buoy-group-tab:hover { color: #94a3b8; }
.buoy-group-tab.active {
    color: #60a5fa;
    border-bottom-color: #60a5fa;
}

/* Sensor Guide panel */
.sensor-guide {
    display: flex;
    gap: 18px;
    padding: 4px 0;
    min-height: 240px;
}
/* Alerts tab layout — carved out from the (now exclusive) Sensor Guide
   tab. The v2 Alarms tab uses .alerts-guide--flat (single column); the
   list grows with content and scrolls inside .alarms-list-scroll up to
   max-height. */
.alerts-guide {
    display: flex;
    gap: 18px;
    padding: 4px 0;
    max-height: calc(85vh - 240px);
}
/* Flat (v2) Alarms tab — single column, no 240px min-height void. */
.alerts-guide--flat {
    flex-direction: column;
    gap: 12px;
}
/* Left column wraps the category sidebar plus the shared current-reading
   tile that lives below it. */
.sensor-guide-left {
    flex: 0 0 200px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-width: 0;
}
.sensor-guide-left .sensor-guide-sidebar {
    flex: 0 0 auto;
}
.sensor-guide-left .sg-current-shared {
    flex: 0 0 auto;
}
.sensor-guide-sidebar {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.sensor-guide-pill {
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.06);
    color: #94a3b8;
    padding: 8px;
    border-radius: 8px;
    font-size: 12px;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
    transition: background 0.2s, border-color 0.2s, color 0.2s;
}
.sensor-guide-pill:hover {
    background: rgba(255,255,255,0.06);
    color: #e2e8f0;
}
.sensor-guide-pill.active {
    background: rgba(56,189,248,0.15);
    border-color: rgba(56,189,248,0.4);
    color: #38bdf8;
}
.sensor-guide-content {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.sensor-guide-empty {
    color: #64748b;
    font-style: italic;
    font-size: 13px;
    padding: 20px;
    text-align: center;
}
.sensor-guide-select-wrap { position: relative; }
.sensor-guide-select {
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.06);
    border-radius: 8px;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    cursor: pointer;
    user-select: none;
    transition: background 0.2s, border-color 0.2s;
}
.sensor-guide-select:hover { border-color: rgba(255,255,255,0.12); }
.sensor-guide-select-wrap.open .sensor-guide-select {
    border-color: rgba(56,189,248,0.4);
    background: rgba(255,255,255,0.05);
}
.sensor-guide-select-icon {
    color: #64748b;
    font-size: 14px;
    flex: 0 0 auto;
}
.sensor-guide-select-name {
    flex: 1;
    font-size: 13px;
    font-weight: 500;
    color: #e2e8f0;
}
.sensor-guide-select-uom {
    font-size: 11px;
    color: #64748b;
}
.sensor-guide-chevron {
    color: #64748b;
    font-size: 11px;
    transition: transform 0.25s ease;
}
.sensor-guide-select-wrap.open .sensor-guide-chevron { transform: rotate(180deg); }
.sensor-guide-options {
    display: none;
}
/* Backdrop: full-viewport dim layer that holds the centered options list */
.sensor-guide-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    z-index: 111080;
    display: flex;
    align-items: center;
    justify-content: center;
    animation: sensor-guide-fade 0.18s ease-out;
}
.sensor-guide-backdrop .sensor-guide-options {
    display: block;
    position: relative;
    width: min(440px, 92vw);
    max-height: min(70vh, 520px);
    overflow-y: auto;
    background: #1e293b;
    border: 1px solid rgba(255,255,255,0.12);
    border-radius: 10px;
    box-shadow: 0 16px 48px rgba(0,0,0,0.55);
    animation: sensor-guide-pop 0.18s ease-out;
}
@keyframes sensor-guide-fade {
    from { opacity: 0; }
    to { opacity: 1; }
}
@keyframes sensor-guide-pop {
    from { opacity: 0; transform: scale(0.95); }
    to { opacity: 1; transform: scale(1); }
}
.sensor-guide-option {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 14px;
    cursor: pointer;
    transition: background 0.15s;
    border-bottom: 1px solid rgba(255,255,255,0.04);
}
.sensor-guide-option:last-child { border-bottom: none; }
.sensor-guide-option:hover { background: rgba(255,255,255,0.05); }
.sensor-guide-option.selected {
    background: rgba(56,189,248,0.08);
    color: #38bdf8;
}
.sensor-guide-option-icon {
    color: #64748b;
    font-size: 13px;
    flex: 0 0 auto;
}
.sensor-guide-option.selected .sensor-guide-option-icon { color: #38bdf8; }
.sensor-guide-option-name {
    flex: 1;
    font-size: 12px;
    font-weight: 500;
}
.sensor-guide-option-uom {
    font-size: 11px;
    color: #64748b;
}
.sensor-guide-desc {
    font-size: 12px;
    line-height: 1.55;
    color: #cbd5e1;
    padding: 14px;
    background: rgba(255,255,255,0.02);
    border: 1px solid rgba(255,255,255,0.04);
    border-radius: 8px;
    margin-top: 10px;
}
.sensor-guide-actions {
    display: grid;
    grid-template-columns: 1fr;
    gap: 10px;
    margin-top: 10px;
}
.sensor-guide-action {
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.06);
    border-radius: 8px;
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    transition: background 0.2s, border-color 0.2s;
}
.sensor-guide-action.sg-alarms { cursor: pointer; }
.sensor-guide-action.sg-alarms:hover {
    background: rgba(255,255,255,0.06);
    border-color: rgba(56,189,248,0.35);
}
.sg-action-label {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    color: #94a3b8;
    font-weight: 600;
}
.sg-action-value-row {
    display: flex;
    align-items: center;
    gap: 8px;
}
.sg-action-icon {
    color: #64748b;
    font-size: 15px;
}
.sg-action-value {
    font-size: 18px;
    font-weight: 600;
    color: #e2e8f0;
    font-variant-numeric: tabular-nums;
}
.sg-action-value .buoy-measurement-uom {
    font-size: 11px;
    color: #64748b;
    margin-left: 4px;
}
/* "Add {Sensor} Alert" — pinned to the right of the value row. Visual is a
   compact pill that picks up the same blue accent the modal uses for primary
   actions; the global <button> skew animation is suppressed since this is
   a control button, not a hero CTA. */
.sg-add-alert-btn {
    margin-left: auto;
    padding: 7px 14px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    border-radius: 999px;
    background: rgba(56, 189, 248, 0.15);
    border: 1px solid rgba(56, 189, 248, 0.4);
    color: #38bdf8;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
    isolation: auto;
    overflow: visible;
}
.sg-add-alert-btn:hover {
    background: rgba(56, 189, 248, 0.25);
    border-color: rgba(56, 189, 248, 0.6);
    color: #7dd3fc;
}
.sg-add-alert-btn::after { content: none; display: none; }
.sg-alarm-label {
    font-size: 11px;
    color: #94a3b8;
    font-weight: 400;
}


.ac-btn-cancel, .ac-btn-save {
    padding: 8px 16px;
    font-size: 13px;
    font-weight: 500;
    border-radius: 6px;
    cursor: pointer;
    border: 1px solid rgba(255,255,255,0.1);
}
.ac-btn-cancel { background: transparent; color: #cbd5e1; }
.ac-btn-save {
    background: #0ea5e9;
    color: #fff;
    border-color: #0ea5e9;
}
.ac-btn-save:disabled { opacity: 0.5; cursor: not-allowed; }

.ac-row {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.ac-row label {
    font-size: 11px;
    text-transform: uppercase;
    color: #94a3b8;
    font-weight: 600;
    letter-spacing: 0.5px;
}
.ac-input, .ac-textarea, .ac-select {
    background: #1e293b;
    border: 1px solid rgba(255,255,255,0.1);
    color: #e2e8f0;
    padding: 8px 10px;
    border-radius: 6px;
    font-size: 13px;
}
.ac-textarea { resize: vertical; min-height: 48px; }
.ac-section-title {
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    color: #94a3b8;
    font-weight: 700;
    margin-top: 6px;
}
.ac-severity-row { display: flex; gap: 6px; flex-wrap: wrap; }
.ac-severity-btn {
    --sev: #38bdf8;
    background: transparent;
    border: 1px solid rgba(255,255,255,0.15);
    color: #cbd5e1;
    padding: 6px 12px;
    border-radius: 16px;
    font-size: 12px;
    cursor: pointer;
    transition: all 0.15s;
}
.ac-severity-btn.active {
    background: color-mix(in srgb, var(--sev) 18%, transparent);
    border-color: var(--sev);
    color: var(--sev);
}
.ac-logic-row { display: flex; gap: 16px; }
.ac-radio { display: flex; align-items: center; gap: 6px; font-size: 13px; color: #cbd5e1; cursor: pointer; }

.ac-rules { display: flex; flex-direction: column; gap: 10px; }
.ac-rule {
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 8px;
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.ac-rule-remove {
    background: none;
    border: none;
    color: #ef4444;
    font-size: 18px;
    cursor: pointer;
    padding: 0 6px;
}
.ac-range-row {
    display: grid;
    grid-template-columns: auto 1fr auto 1fr;
    gap: 8px;
    align-items: center;
}
.ac-range-row label { margin: 0; }
.ac-duration { display: flex; gap: 8px; }
.ac-duration .ac-input { flex: 1; }
.ac-duration .ac-select { flex: 0 0 110px; }
.ac-uom-hint {
    display: inline-block;
    margin-left: 8px;
    color: #64748b;
    font-size: 12px;
}
.ac-add-rule {
    background: transparent;
    border: 1px dashed rgba(255,255,255,0.2);
    color: #94a3b8;
    padding: 8px 14px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 12px;
}
.ac-add-rule:hover { border-color: #38bdf8; color: #38bdf8; }

.ac-validation:empty { display: none; }
.ac-validation { display: flex; flex-direction: column; gap: 4px; }
.ac-err, .ac-warn { font-size: 12px; padding: 6px 10px; border-radius: 4px; }
.ac-err { color: #fca5a5; background: rgba(239,68,68,0.1); border: 1px solid rgba(239,68,68,0.3); }
.ac-warn { color: #fcd34d; background: rgba(234,179,8,0.08); border: 1px solid rgba(234,179,8,0.25); }

.ac-notifications {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}
.ac-notif-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: rgba(56,189,248,0.12);
    border: 1px solid rgba(56,189,248,0.3);
    color: #bae6fd;
    padding: 4px 10px;
    border-radius: 14px;
    font-size: 12px;
}
.ac-notif-pill.ac-pill-self {
    background: rgba(34,197,94,0.12);
    border-color: rgba(34,197,94,0.3);
    color: #bbf7d0;
}
.ac-notif-pill-x { cursor: pointer; font-size: 14px; padding-left: 2px; }
.ac-add-recipient {
    background: transparent;
    border: 1px dashed rgba(255,255,255,0.2);
    color: #94a3b8;
    padding: 4px 10px;
    border-radius: 14px;
    font-size: 12px;
    cursor: pointer;
}
.ac-add-recipient:hover { border-color: #38bdf8; color: #38bdf8; }
.ac-contact-picker {
    position: absolute;
    background: #1e293b;
    border: 1px solid rgba(255,255,255,0.15);
    border-radius: 8px;
    padding: 6px;
    margin-top: 6px;
    min-width: 280px;
    max-height: 260px;
    overflow-y: auto;
    z-index: 10;
    box-shadow: 0 8px 24px rgba(0,0,0,0.4);
}
.ac-contact-row {
    padding: 6px 10px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 12px;
    color: #cbd5e1;
}
.ac-contact-row:hover { background: rgba(255,255,255,0.06); }
.ac-contact-row em { color: #64748b; font-style: normal; font-size: 11px; }
.ac-contact-custom {
    display: flex;
    gap: 6px;
    padding: 6px;
    border-top: 1px solid rgba(255,255,255,0.08);
    margin-top: 4px;
}
.ac-contact-custom input {
    flex: 1;
    background: #0f172a;
    border: 1px solid rgba(255,255,255,0.1);
    color: #e2e8f0;
    padding: 5px 8px;
    border-radius: 4px;
    font-size: 12px;
}
.ac-contact-custom button {
    background: #0ea5e9;
    color: #fff;
    border: none;
    padding: 4px 12px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 12px;
}

.ac-advanced {
    border-top: 1px solid rgba(255,255,255,0.06);
    padding-top: 10px;
    margin-top: 6px;
}
.ac-advanced summary {
    cursor: pointer;
    font-size: 12px;
    color: #94a3b8;
    padding: 4px 0;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

/* Wizard tabs */
.ac-wizard-tabs {
    display: flex;
    gap: 4px;
    padding: 12px 18px;
    background: rgba(255,255,255,0.02);
    border-bottom: 1px solid rgba(255,255,255,0.08);
}
.ac-wizard-tab {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px;
    border-radius: 6px;
    font-size: 11px;
    color: #64748b;
    opacity: 0.55;
    background: rgba(255,255,255,0.02);
    border: 1px solid transparent;
}
.ac-wizard-tab.done { opacity: 1; color: #38bdf8; }
.ac-wizard-tab.active { opacity: 1; color: #e2e8f0; background: rgba(56,189,248,0.12); border-color: rgba(56,189,248,0.4); }
.ac-wizard-num {
    width: 18px; height: 18px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 9px;
    background: rgba(255,255,255,0.08);
    font-size: 10px;
    font-weight: 700;
}
.ac-wizard-tab.active .ac-wizard-num,
.ac-wizard-tab.done .ac-wizard-num {
    background: #38bdf8; color: #0f172a;
}
.ac-wizard-label { font-weight: 500; letter-spacing: 0.3px; }

.ac-step-title {
    font-size: 14px;
    font-weight: 600;
    color: #e2e8f0;
    margin-bottom: 8px;
}
.ac-empty {
    padding: 20px;
    text-align: center;
    color: #64748b;
    font-style: italic;
    font-size: 13px;
}

/* Faux select (centered-popup style) */
.ac-fsel-wrap { position: relative; }
.ac-fsel {
    background: #1e293b;
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: 6px;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    cursor: pointer;
    user-select: none;
    transition: border-color 0.2s;
    min-height: 36px;
}
.ac-fsel:hover { border-color: rgba(56,189,248,0.4); }
.ac-fsel-icon { color: #64748b; font-size: 14px; flex: 0 0 auto; }
.ac-fsel-icon:empty { display: none; }
.ac-fsel-label { flex: 1; font-size: 13px; color: #e2e8f0; }
.ac-fsel-chev { color: #64748b; font-size: 11px; }

.ac-fsel-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.5);
    z-index: 100000;
    display: flex;
    align-items: center;
    justify-content: center;
    animation: sensor-guide-fade 0.15s ease-out;
}
.ac-fsel-popup {
    width: min(420px, 92vw);
    max-height: min(60vh, 420px);
    overflow-y: auto;
    background: #1e293b;
    border: 1px solid rgba(255,255,255,0.12);
    border-radius: 10px;
    box-shadow: 0 16px 48px rgba(0,0,0,0.55);
    animation: sensor-guide-pop 0.15s ease-out;
}
.ac-fsel-popup-title {
    padding: 12px 16px;
    font-size: 12px;
    font-weight: 600;
    color: #94a3b8;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    border-bottom: 1px solid rgba(255,255,255,0.08);
}
.ac-fsel-opt {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 16px;
    cursor: pointer;
    border-bottom: 1px solid rgba(255,255,255,0.04);
}
.ac-fsel-opt:last-child { border-bottom: none; }
.ac-fsel-opt:hover { background: rgba(255,255,255,0.05); }
.ac-fsel-opt-icon { color: #64748b; font-size: 13px; flex: 0 0 auto; }
.ac-fsel-opt-label { flex: 1; font-size: 13px; font-weight: 500; color: #e2e8f0; }
.ac-fsel-sub { font-size: 11px; color: #64748b; }

.ac-rule-preview {
    margin-top: 12px;
    padding: 10px 14px;
    background: rgba(56,189,248,0.08);
    border: 1px dashed rgba(56,189,248,0.3);
    border-radius: 6px;
    color: #bae6fd;
    font-size: 12px;
}
.ac-rule-preview em { color: #64748b; font-style: normal; margin-right: 6px; }

.ac-rules-list { display: flex; flex-direction: column; gap: 6px; }
.ac-rule-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 6px;
}
.ac-rule-row-label { flex: 1; font-size: 13px; color: #e2e8f0; }
.ac-rule-row-actions { display: flex; gap: 6px; }

.ac-contacts { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }
.ac-contact-pill {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 12px;
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 8px;
}
.ac-contact-name {
    flex: 1;
    font-size: 13px;
    color: #e2e8f0;
    display: flex;
    align-items: center;
    gap: 8px;
}
.ac-contact-name i { color: #64748b; }
.ac-toggle {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    cursor: pointer;
    user-select: none;
}
.ac-toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
.ac-toggle-track {
    width: 30px;
    height: 16px;
    border-radius: 8px;
    background: rgba(255,255,255,0.1);
    border: 1px solid rgba(255,255,255,0.12);
    position: relative;
    transition: background 0.2s, border-color 0.2s;
}
.ac-toggle-track::after {
    content: '';
    position: absolute;
    width: 12px;
    height: 12px;
    background: #64748b;
    border-radius: 6px;
    top: 1px;
    left: 2px;
    transition: transform 0.2s, background 0.2s;
}
.ac-toggle input:checked + .ac-toggle-track {
    background: rgba(56,189,248,0.25);
    border-color: rgba(56,189,248,0.5);
}
.ac-toggle input:checked + .ac-toggle-track::after {
    transform: translateX(13px);
    background: #38bdf8;
}
.ac-toggle.disabled { opacity: 0.4; cursor: not-allowed; }
.ac-toggle-icon { color: #64748b; font-size: 11px; }
.ac-toggle input:checked ~ .ac-toggle-icon { color: #38bdf8; }
.ac-contact-remove {
    background: none;
    border: none;
    color: #94a3b8;
    font-size: 16px;
    cursor: pointer;
    padding: 0 4px;
}
.ac-add-contact {
    background: transparent;
    border: 1px dashed rgba(255,255,255,0.2);
    color: #94a3b8;
    padding: 6px 14px;
    border-radius: 6px;
    font-size: 12px;
    cursor: pointer;
}
.ac-add-contact:hover { border-color: #38bdf8; color: #38bdf8; }

.ac-webhook-row {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 16px;
    padding: 12px 14px;
    background: rgba(255,255,255,0.02);
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 8px;
}
.ac-webhook-row strong { color: #e2e8f0; font-size: 13px; font-weight: 600; display: flex; align-items: center; gap: 6px; }
.ac-webhook-desc { font-size: 11px; color: #94a3b8; margin-top: 2px; }
.ac-toggle-big .ac-toggle-track { width: 40px; height: 22px; border-radius: 11px; }
.ac-toggle-big .ac-toggle-track::after { width: 16px; height: 16px; border-radius: 8px; top: 2px; left: 3px; }
.ac-toggle-big input:checked + .ac-toggle-track::after { transform: translateX(18px); }

.ac-saved-hero { text-align: center; padding: 40px 20px 20px; }
.ac-saved-icon { font-size: 48px; color: #22c55e; display: block; margin-bottom: 12px; }
.ac-saved-title { font-size: 20px; font-weight: 700; color: #e2e8f0; margin-bottom: 4px; }
.ac-saved-sub { font-size: 13px; color: #94a3b8; }
.ac-webhook-display {
    margin-top: 20px;
    padding: 14px;
    background: rgba(56,189,248,0.08);
    border: 1px solid rgba(56,189,248,0.3);
    border-radius: 8px;
}
.ac-webhook-display label {
    font-size: 11px;
    color: #94a3b8;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    font-weight: 600;
}
.ac-webhook-url { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.ac-webhook-url code {
    flex: 1;
    padding: 8px 10px;
    background: #0f172a;
    border-radius: 4px;
    font-size: 12px;
    color: #bae6fd;
    overflow-x: auto;
    white-space: nowrap;
}
.ac-webhook-copy {
    background: #0ea5e9;
    color: #fff;
    border: none;
    padding: 6px 12px;
    border-radius: 4px;
    font-size: 11px;
    cursor: pointer;
}
.ac-webhook-note { font-size: 11px; color: #94a3b8; margin-top: 6px; }

/* ── Alarms tab inside buoy modal ─────────────────────────── */
.alarms-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.alarm-card {
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 8px;
    padding: 10px 14px;
}
.alarm-card-head {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 4px;
}
.alarm-card-name {
    font-size: 13px;
    font-weight: 600;
    color: #e2e8f0;
    flex: 1 1 auto;
}
/* Spinner state on mute / unmute while the server processes the
   request — button is disabled, dimmed, can't be re-triggered. */
.alarm-mute.alarm-pending,
.alarm-unmute.alarm-pending {
    opacity: 0.7;
    cursor: wait;
    pointer-events: none;
}
/* Alarm-card action buttons wear the rule-editor pill look:
   edit = blue pill, mute/unmute = grey (grayscale) pill, delete = red pill.
   .blue-pill-button supplies the blue gradient base; the red + grayscale
   variants below replicate the #alert-config-modal-scoped looks for these
   buttons, which live OUTSIDE the modal. Sized down for the icon cluster. */
.alarm-card-actions .blue-pill-button {
    padding: 4px 10px;
    font-size: 11px;
    line-height: 1;
    border-radius: 999px;
}
.alarm-card-actions .blue-pill-button i { font-size: 12px; }
.alarm-card-actions .blue-pill-button .text {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
/* Delete → red pill (mirror of #alert-config-modal .ac-rule-delete). */
.alarm-card-actions .alarm-delete.blue-pill-button {
    background: linear-gradient(to bottom, #ef5350 0%, #c62828 100%);
    border-top-color: #e57373;
    border-right-color: #b71c1c;
    border-bottom-color: #9b1c1c;
    border-left-color: #b71c1c;
}
.alarm-card-actions .alarm-delete.blue-pill-button:hover {
    background: linear-gradient(to bottom, #f56b68 0%, #d32f2f 100%);
}
/* Armed ("Sure?") — brighten the red so the confirm state reads against the
   already-red resting pill. (The flat .alarm-delete-confirm background-color
   wash is for the old group button; the gradient pill needs its own delta.) */
.alarm-card-actions .alarm-delete.blue-pill-button.alarm-delete-confirm {
    background: linear-gradient(to bottom, #ff7b78 0%, #e53935 100%);
}
/* Mute / unmute → grey pill (mirror of #alert-config-modal .ac-btn-cancel). */
.alarm-card-actions .alarm-mute.blue-pill-button,
.alarm-card-actions .alarm-unmute.blue-pill-button {
    filter: grayscale(1);
}
.alarm-card-actions .alarm-mute.blue-pill-button:hover,
.alarm-card-actions .alarm-unmute.blue-pill-button:hover {
    filter: grayscale(0.85);
}

/* Two-step delete confirm on alarm cards — same crossfade morph as
   #btn-aoi-delete. First click swaps the trash icon for a "Sure?"
   label and applies a red wash; second click within 2s commits.
   Auto-reverts after 2s via the JS handler in app.js. */
.alarm-delete .text {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.alarm-delete .adc-icon-default,
.alarm-delete .adc-confirm-label {
    transition: opacity 0.2s ease, max-width 0.25s ease;
}
.alarm-delete .adc-confirm-label {
    overflow: hidden;
    max-width: 0;
    opacity: 0;
    white-space: nowrap;
    font-size: 11px;
    letter-spacing: 0.4px;
}
.alarm-delete.alarm-delete-confirm {
    background-color: rgba(239, 68, 68, 0.25);
    color: #fecaca;
}
.alarm-delete.alarm-delete-confirm .adc-icon-default { display: none; }
.alarm-delete.alarm-delete-confirm .adc-confirm-label {
    max-width: 64px;
    opacity: 1;
}

/* ── Alarms tab v2 — flat list, card, hero ─────── */

/* Flat list — sized to content, scrolls inside its own wrapper. No
   240px min-height, no stretch-to-fill. (.alarms-list is defined above.) */
.alarms-list-wrap {
    min-height: 0;
    overflow: hidden;
}
.alarms-list-scroll {
    max-height: calc(85vh - 360px);
    overflow-y: auto;
    scrollbar-width: thin;
}
/* Alarms pager — small icon buttons; override the global Uiverse button base. */
.alarms-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin: 10px 0 4px;
}
.alarms-pager-btn {
    width: 28px;
    height: 28px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(255,255,255,0.06);
    border: 1px solid rgba(255,255,255,0.12);
    border-radius: 6px;
    color: #cbd5e1;
    font-size: 12px;
    cursor: pointer;
}
.alarms-pager-btn::after { content: none; }
.alarms-pager-btn:hover:not(:disabled) { background: rgba(255,255,255,0.12); color: #fff; }
.alarms-pager-btn:disabled { opacity: 0.35; cursor: default; }
.alarms-pager-info {
    font-size: 12px;
    color: #94a3b8;
    min-width: 46px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* Alert Settings tab fills its (carousel-stretched) buoy slide so the Add
   Alert button sits at the very bottom instead of floating under a short
   list. Scoped to .buoy-slide--fill — toggled on only while the alarms tab
   is active — so the other tabs keep their natural top-aligned block layout.
   The active panel is shown via inline display:block (jQuery .show()), so we
   fill via a height:100% chain through the tab body rather than flexing the
   panel itself. NB the pin relies on .buoy-slide being a stretched flex item
   of the content-sized carousel track — that definite height is what flex:1 /
   height:100% resolve against. If the carousel stops stretching slides the
   chain falls back to auto height and the button just stops pinning (no
   breakage). */
.buoy-slide--fill {
    display: flex;
    flex-direction: column;
}
.buoy-slide--fill .buoy-group-panel[data-group-idx="alarms"] {
    flex: 1 1 auto;
    min-height: 0;
}
.buoy-slide--fill .alarms-tab-body {
    height: 100%;
    display: flex;
    flex-direction: column;
    min-height: 0;
}
.buoy-slide--fill .alerts-guide--flat {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
}
.buoy-slide--fill .alarms-add {
    margin-top: auto;
}

/* alarm-card v2 — stripe + body, name-dominant, non-reflowing actions. */
.alarm-card--v2 {
    position: relative;
    display: flex;
    align-items: stretch;
    gap: 0;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 8px;
    padding: 0;
    overflow: hidden;
    transition: background 0.15s ease, border-color 0.15s ease;
}
.alarm-card--v2:hover {
    background: rgba(255, 255, 255, 0.05);
    border-color: rgba(255, 255, 255, 0.14);
}
.alarm-card--v2 .alarm-card-stripe {
    flex: 0 0 8px;
    align-self: stretch;
    /* colour set inline per severity */
}
.alarm-card--v2 .alarm-card-body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 10px 12px 10px 14px;
}
.alarm-card--v2 .alarm-card-head {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: nowrap;
    margin-bottom: 0;
}
.alarm-card--v2 .alarm-card-name {
    display: flex;
    align-items: center;
    gap: 0;
    flex: 1 1 auto;
    min-width: 0;
    font-size: 13px;
    font-weight: 600;
    color: #f1f5f9;
}
/* Only the name TEXT truncates; the nested Fleet-wide chip is a flex sibling
   that keeps its slot, so it is never clipped by the name's overflow. */
.alarm-card--v2 .alarm-card-name-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Tinted severity chip + status chips. Read-only labels (severity is not
   navigation) — keep the default cursor so they never look clickable. */
.alarm-card--v2 .alarm-card-chip {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.3px;
    text-transform: uppercase;
    padding: 2px 8px;
    border-radius: 999px;
    color: var(--sev);
    background: color-mix(in srgb, var(--sev) 18%, transparent);
    border: 1px solid color-mix(in srgb, var(--sev) 40%, transparent);
    cursor: default;
}
/* The Muted chip nested in the name sits 5px clear of the name text (the
   Fleet-wide indicator now rides the rule line as plain text). */
.alarm-card--v2 .alarm-card-name .alarm-card-chip {
    flex: 0 0 auto;
    margin-left: 5px;
}
.alarm-card-chip--off {
    color: #94a3b8;
    background: rgba(148, 163, 184, 0.14);
    border-color: rgba(148, 163, 184, 0.25);
}
.alarm-card-chip--muted {
    color: #cbd5e1;
    background: rgba(148, 163, 184, 0.16);
    border-color: rgba(148, 163, 184, 0.28);
    text-transform: none;
    letter-spacing: 0;
    filter: grayscale(1);
}
/* Line 2 — plain-English rule line + glyph. */
.alarm-card--v2 .alarm-card-line {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: #94a3b8;
    min-width: 0;
}
.alarm-card--v2 .alarm-card-glyph {
    flex: 0 0 auto;
    color: var(--sev);
    opacity: 0.85;
    font-size: 12px;
}
.alarm-card--v2 .alarm-card-line-text {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Recipient count on the right of the rule line — same grey + size as the
   line text (both inherit #94a3b8 / 12px from .alarm-card-line). */
.alarm-card--v2 .alarm-card-recipients {
    flex: 0 0 auto;
    white-space: nowrap;
}
.alarm-card-line-more {
    color: #64748b;
    font-weight: 600;
}

/* Disabled rows: dimmed. */
.alarm-card--v2.alarm-card--disabled {
    opacity: 0.6;
}

/* Hover-reveal action cluster in a FIXED-WIDTH reserved slot so the row
   never reflows at rest. Three pill buttons (~34px) + gaps ≈ 116px. The
   armed delete ("Sure?") expands past the slot and overlays the hover-only,
   already-ellipsised name to its left — acceptable, not clipped. */
.alarm-card--v2 .alarm-card-actions {
    flex: 0 0 auto;
    margin-left: auto;
    display: flex;
    gap: 4px;
    width: 116px;
    justify-content: flex-end;
    opacity: 0;
    transition: opacity 0.15s ease;
}
.alarm-card--v2:hover .alarm-card-actions,
.alarm-card--v2:focus-within .alarm-card-actions {
    opacity: 1;
}
/* Keep the two-step delete morph from reflowing: it expands within the
   reserved slot, not the row. (The shared .alarm-delete confirm rules
   above still apply — they're untouched.) */

/* Severe mute lock — disabled + struck-through. */
.alarm-mute--locked {
    opacity: 0.4;
    cursor: not-allowed;
    text-decoration: line-through;
    pointer-events: auto; /* keep tooltip on hover even while disabled */
}
.alarm-mute--locked .text { text-decoration: line-through; }

/* ── Calm hero empty state ───────────────────────────────────── */
.alarms-hero {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 10px;
    padding: 36px 24px;
}
.alarms-hero-icon {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 26px;
    color: #38bdf8;
    background: rgba(56, 189, 248, 0.12);
    border: 1px solid rgba(56, 189, 248, 0.25);
    margin-bottom: 4px;
}
.alarms-hero-title {
    font-size: 16px;
    font-weight: 700;
    color: #e2e8f0;
}
.alarms-hero-sub {
    font-size: 13px;
    color: #94a3b8;
    max-width: 320px;
    line-height: 1.5;
}
.alarms-hero-cta {
    margin-top: 6px;
}
.alarms-hero-cta i { margin-right: 6px; }
.alarms-add i { margin-right: 6px; }

/* ── Alerts slide-out panel + bell badge ──────────────────── */
.user-bar-icon { position: relative; }
.user-bar-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    background: #ef4444;
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    padding: 2px 5px;
    border-radius: 10px;
    min-width: 16px;
    text-align: center;
    line-height: 1;
    box-shadow: 0 0 0 2px #0f172a;
}
/* Bell badge — count of Current-tab (active) alerts, in a circle coloured to
   the highest current severity (background set inline by updateBellBadge). */
#btn-alerts { position: relative; }
#btn-alerts .alerts-badge {
    position: absolute;
    top: -3px;
    right: -3px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    box-sizing: border-box;
    border-radius: 999px;
    background: #f59e0b;
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
    box-shadow: 0 0 0 2px rgba(15, 23, 42, 0.55);
    pointer-events: none;
}
/* History tab — right-aligned "Clear history" blue-pill button. */
.alerts-history-bar { display: flex; justify-content: flex-end; margin-bottom: 8px; }
.alerts-clear-history i { margin-right: 6px; }

#alerts-panel {
    position: fixed;
    top: 0;
    right: -380px;
    width: 380px;
    height: 100vh;
    max-width: 95vw;
    background: #0f172a;
    border-left: 1px solid rgba(255,255,255,0.1);
    transition: right 0.25s ease;
    z-index: 1100;
    display: flex;
    flex-direction: column;
}
#alerts-panel.open { right: 0; }
/* Tabs styled to match the buoy modal's group tabs (.buoy-group-tab):
   left-aligned natural-width tabs, dim inactive / blue active + underline. */
.alerts-panel-tabs {
    display: flex;
    gap: 4px;
    padding: 0 12px;
    border-bottom: 1px solid rgba(255,255,255,0.06);
    background-color: #000000;
}
.alerts-tab {
    padding: 6px 14px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: #64748b;
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    cursor: pointer;
    transition: color 0.2s, border-color 0.2s;
    margin-bottom: -1px;
}
/* Suppress the inherited global button skew-wave so it can't paint over the
   tab label on hover — these flat tabs have no .text span to lift the text
   above the z-index:1 ::after overlay. */
.alerts-tab::after { content: none; }
.alerts-tab:hover { color: #ffffff; }
.alerts-tab.active { color: #60a5fa; border-bottom-color: #60a5fa; }
.alerts-panel-body { flex: 1; overflow-y: auto; padding: 12px; background-color: #000000; }
.alerts-panel-footer { padding: 10px 12px; border-top: 1px solid rgba(255,255,255,0.08); background-color: #000000; }
.alerts-empty {
    text-align: center;
    color: #64748b;
    font-style: italic;
    padding: 20px;
    font-size: 13px;
}
.alert-trigger-card {
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.08);
    border-left: 3px solid #64748b;
    border-radius: 6px;
    padding: 10px 12px;
    margin-bottom: 8px;
    cursor: pointer;
    transition: box-shadow 0.15s;
}
.alert-trigger-card:hover { box-shadow: 0 0 0 1px rgba(255,255,255,0.18) inset; }
/* Faint wash of each level's own hex, so the card background reflects its
   health/alert severity (left border stays the full-strength colour). */
.alert-trigger-card.sev-info       { border-left-color: #38bdf8; background: color-mix(in srgb, #38bdf8 10%, transparent); }
.alert-trigger-card.sev-monitoring { border-left-color: #22d3ee; background: color-mix(in srgb, #22d3ee 10%, transparent); }
.alert-trigger-card.sev-warning    { border-left-color: #f59e0b; background: color-mix(in srgb, #f59e0b 10%, transparent); }
.alert-trigger-card.sev-critical   { border-left-color: #ef4444; background: color-mix(in srgb, #ef4444 10%, transparent); }
.alert-trigger-card.sev-severe     { border-left-color: #b91c1c; background: color-mix(in srgb, #b91c1c 14%, transparent); }
.atc-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.atc-sev {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.8px;
    padding: 2px 6px;
    border-radius: 3px;
    background: rgba(255,255,255,0.08);
    color: #cbd5e1;
}
.atc-name { font-size: 13px; font-weight: 600; color: #e2e8f0; }
.atc-buoy { font-size: 11px; color: #94a3b8; }
.atc-time { font-size: 11px; color: #64748b; }
/* Buoy · time on one line; carries the spacing the .atc-time block used to. */
.atc-meta { font-size: 11px; color: #64748b; margin-bottom: 6px; }
.atc-rules { margin: 4px 0 6px; padding-left: 16px; font-size: 12px; color: #cbd5e1; }
.atc-rules li em { color: #64748b; font-style: normal; font-size: 11px; }
/* .atc-clear now inherits the base Uiverse button + the #btn-new-alert sizing
   rule above (two-span markup), so it matches the "Create a new alert" button. */
.atc-clear--loading { opacity: 0.75; cursor: default; }
.alert-trigger-card.compact { padding: 8px 10px; }

/* Map marker indicator when buoy has alerts */
.buoy-marker-icon.has-alert { filter: drop-shadow(0 0 4px rgba(239,68,68,0.8)); }
@media (max-width: 640px) {
    .sensor-guide { flex-direction: column; }
    .alerts-guide { flex-direction: column; }
    .sensor-guide-sidebar {
        flex: 0 0 auto;
        flex-direction: row;
        overflow-x: auto;
    }
    .sensor-guide-pill { white-space: nowrap; }
    .sensor-guide-actions { grid-template-columns: 1fr; }
}

/* Measurement tiles */
.buoy-measurements-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 8px;
}
.buoy-measurement-tile {
    background: rgba(255,255,255,0.04);
    border: none;
    border-radius: 8px;
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    transition: background 0.4s;
    /* Positioning context for the pin button overlay — see
       .buoy-measurement-tile .buoy-measurement-pin-btn below, which
       absolute-positions itself into the top-right corner over the
       tile's border-radius. */
    position: relative;
    /* Whole-tile drag affordance for SortableJS. Filtered children
       (name / pin / chart) keep their own cursors via specificity. */
    cursor: grab;
}
.buoy-measurement-tile:active {
    cursor: grabbing;
}
.buoy-measurement-tile.telemetry-updated {
    background: rgba(96, 165, 250, 0.12);
    border-color: rgba(96, 165, 250, 0.3);
}
.buoy-measurement-value.telemetry-stale {
    /* Steady dim only — see .buoy-status-value.telemetry-stale. */
    opacity: 0.4;
    transition: opacity 0.4s ease;
}
/* Tile body is presentation-only — the click action now lives on the
   .buoy-measurement-name label inside the tile (opens the
   measurement-name popup). */
.buoy-measurement-tile .buoy-measurement-name {
    cursor: pointer;
    /* Dotted underline signals the label is interactive (opens the
       measurement popup). underline-offset lifts it a touch off the
       baseline so descenders don't crowd it. */
    text-decoration: underline dotted rgba(203, 213, 225, 0.5);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    transition: color 0.15s ease, text-decoration-color 0.15s ease;
}
.buoy-measurement-tile .buoy-measurement-name:hover {
    color: #ffffff;
    text-decoration-color: #ffffff;
}
/* Row that holds the measurement name on the left and the chart-icon
   shortcut on the right. Flexbox + margin-left:auto on the button
   pushes the icon to the trailing edge regardless of label length. */
.buoy-measurement-tile .buoy-measurement-name-row {
    display: flex;
    align-items: center;
    gap: 8px;
}
/* Estimated-reading marker — lives inside the measurement name span, left
   of the text, inheriting its colour; hidden unless the tile's value is
   estimator-sourced. */
.buoy-measurement-est-btn {
    display: none;
    background: transparent;
    border: none;
    padding: 0;
    margin-right: 4px;
    color: #ffffff;
    cursor: pointer;
    font-size: 10px;
    line-height: 1;
    vertical-align: middle;
    transition: color 0.15s ease;
}
.buoy-measurement-tile.telemetry-estimated .buoy-measurement-est-btn {
    display: inline-flex;
}
.buoy-measurement-est-btn:hover {
    color: #60a5fa;
}
.buoy-measurement-tile .buoy-measurement-chart-btn {
    margin-left: auto;
    background: transparent;
    border: none;
    padding: 2px 4px;
    color: #94a3b8;
    cursor: pointer;
    font-size: 12px;
    line-height: 1;
    transition: color 0.15s ease;
}
.buoy-measurement-tile .buoy-measurement-chart-btn:hover {
    color: #ffffff;
}

/* Pin / unpin button. Shares the same trailing-edge slot as the
   chart button on a measurement-name row. data-pinned drives both
   the icon rotation and the colour. Unpinned: thumbtack rotated 45°
   (visually "askew / loose"), muted slate. Pinned: thumbtack upright,
   heavier sky-blue. The rotation transition animates the toggle. */
.buoy-measurement-pin-btn {
    background: transparent;
    border: none;
    padding: 2px 4px;
    color: #94a3b8;
    cursor: pointer;
    font-size: 12px;
    line-height: 1;
    transition: color 0.15s ease;
}
.buoy-measurement-pin-btn:hover {
    color: #ffffff;
}
.buoy-measurement-pin-btn .fa-thumbtack {
    transform: rotate(45deg);
    transform-origin: center;
    transition: transform 0.15s ease;
}
.buoy-measurement-pin-btn[data-pinned="true"] {
    color: #0284c7;             /* sky-600 — heavier than the unpinned palette */
}
.buoy-measurement-pin-btn[data-pinned="true"]:hover {
    color: #0369a1;             /* sky-700 */
}
.buoy-measurement-pin-btn[data-pinned="true"] .fa-thumbtack {
    transform: rotate(0deg);
}
/* Inside a measurement tile (grid view), the pin button is lifted
   out of the name row's trailing-edge group and floated at the
   tile's top-right corner, slightly overhanging the border-radius
   so it reads like a real thumbtack pinned through the card edge.
   Status-row pinned items in the top status column keep their
   inline placement — that scope is unchanged. */
.buoy-measurement-tile .buoy-measurement-pin-btn {
    position: absolute;
    top: -4px;
    right: -4px;
    z-index: 1;
    padding: 4px;
    font-size: 13px;
}
/* Hide the pin button on tiles whose field is already pinned —
   unpinning is done from the top status column (where the unpin
   thumbtack lives next to the row). Also hide all tile pin buttons
   when the pin list is at its cap (body.pin-list-full), so the user
   can't try to pin past the limit. The unpin button on top-section
   rows (.buoy-status-row .buoy-measurement-pin-btn) is never affected. */
.buoy-measurement-tile .buoy-measurement-pin-btn[data-pinned="true"] {
    display: none;
}
body.pin-list-full .buoy-measurement-tile .buoy-measurement-pin-btn {
    display: none;
}

/* Drag handle on pinned top-section rows. Only on rows marked
   data-pinned="true"; Last Transmission has no handle. Muted at
   rest, lifts to a brighter slate on row hover so the affordance
   is discoverable without being noisy. */
.buoy-status-row-drag-handle {
    color: #475569;
    cursor: grab;
    font-size: 11px;
    flex: 0 0 auto;
    margin-right: 4px;
    transition: color 0.15s ease;
}
.buoy-status-row[data-pinned="true"]:hover .buoy-status-row-drag-handle {
    color: #94a3b8;
}
.buoy-status-row-drag-handle:active {
    cursor: grabbing;
}
/* Pin button inside a top-section status row is pushed to the
   trailing edge of the row regardless of the value's natural
   width. (Inside .buoy-measurement-tile the pin button is
   absolute-positioned at the top-right corner — that path is
   unaffected by this margin-left rule.) */
.buoy-status-row .buoy-measurement-pin-btn {
    margin-left: auto;
}

/* ℹ️ on the Last Transmission row — trailing-edge icon button, same
   quiet-to-white hover treatment as the pin buttons. */
.buoy-status-info-btn {
    background: transparent;
    border: none;
    padding: 2px 4px;
    margin-left: auto;
    color: #64748b;
    cursor: pointer;
    font-size: 12px;
    line-height: 1;
    transition: color 0.15s ease;
}
.buoy-status-info-btn:hover {
    color: #ffffff;
}

/* SortableJS visual states.
   --ghost: the placeholder slot where the dragged row will land.
   --chosen: the row currently being dragged. */
.buoy-status-row--sortable-ghost {
    opacity: 0.3;
}
.buoy-status-row--sortable-chosen {
    background: rgba(56, 189, 248, 0.08);
    border-radius: 4px;
}
/* Same ghost/chosen treatment for the measurement-grid tiles. */
.buoy-measurement-tile--sortable-ghost {
    opacity: 0.3;
}
.buoy-measurement-tile--sortable-chosen {
    cursor: grabbing;
    background: rgba(56, 189, 248, 0.10);
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
}

/* ── Measurement-name popup ─────────────────────────────────────
   Floating panel shown by _openMeasurementPopup(). Styled to read
   as a peer of .event-card.focused (dark glass surface, soft border,
   8px radius). Positioned via inline top/left set by JS, with a
   little outer drop-shadow so it lifts off the modal underneath. */
.buoy-measurement-popup {
    position: fixed;
    z-index: 3000;
    min-width: 240px;
    max-width: 320px;
    padding: 12px 14px;
    border-radius: 8px;
    background: rgba(12, 17, 30, 0.96);
    border: 1px solid rgba(255, 255, 255, 0.1);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
    color: #e2e8f0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    backdrop-filter: blur(8px);
}
.buoy-measurement-popup .bmp-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding-bottom: 8px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.buoy-measurement-popup .bmp-icon {
    font-size: 16px;
    color: #cbd5e1;
    flex: 0 0 auto;
}
.buoy-measurement-popup .bmp-name {
    flex: 1;
    font-size: 13px;
    font-weight: 700;
    color: #e2e8f0;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.buoy-measurement-popup .bmp-uom {
    font-size: 11px;
    color: #94a3b8;
}
.buoy-measurement-popup .bmp-desc {
    font-size: 12px;
    line-height: 1.5;
    color: #cbd5e1;
}
.buoy-measurement-popup .bmp-footer {
    display: flex;
    gap: 8px;
    padding-top: 8px;
    border-top: 1px solid rgba(255, 255, 255, 0.06);
}
/* Use the global animated button look (navy + skew-wave hover, like the
   settings modal's action buttons) instead of a flat translucent style — just
   sized compact + rounded for the popup footer. overflow:hidden on the base
   `button` clips the ::after wave to the rounded corners. */
.buoy-measurement-popup .bmp-action {
    flex: 1;
    padding: 9px 12px;
    border-radius: 6px;
    font-size: 12px;
}
.buoy-measurement-popup .bmp-action i {
    margin-right: 6px;
}
/* Settings action buttons (Apply & Reload Data / Load Data / Clear Cache) —
   compact animated style (global navy + skew-wave, sized down). These now
   live in the unified modal's settings detail pane, so scope to
   #tips-detail-settings (was #settings-modal). Other .animated-button uses
   (e.g. AOI Save) keep their full size. */
#tips-detail-settings .animated-button {
    padding: 9px 12px;
    border-radius: 6px;
    font-size: 12px;
}

/* ── Telemetry Chart Overlay ─────────────────────────────── */
/* Sits on top of .buoy-modal-content using the same glass-panel-dark
   surface so it reads as a peer of every other modal in the app. The
   inner .modal-header reuses the project's standard header layout. */
.telemetry-chart-overlay {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    top: 0;
    z-index: 10;
    display: flex;
    flex-direction: column;
    border-radius: 12px;
    overflow: hidden;
    padding: 0;
}
/* Pill-styled header — adopts the visual tokens from `.chart-add-series-btn`
   (subtle blue tint bg, thin blue border, 8px radius) so the header reads
   as a contained chip rather than a heavy banner. */
.telemetry-chart-overlay .modal-header {
    margin-bottom: 0;
    padding: 8px 14px;
    flex-shrink: 0;
}
.telemetry-chart-overlay .modal-title {
    display: inline-flex;
    align-items: baseline;
    gap: 8px;
    margin: 0;
}
.telemetry-chart-body {
    flex: 1;
    padding: 12px 16px;
    min-height: 0;
    position: relative;
}
.telemetry-chart-body canvas {
    width: 100% !important;
    height: 100% !important;
}
/* Centered empty-state when the selected timeframe has no readings. */
.telemetry-chart-empty {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    color: #64748b;
    font-size: 13px;
    padding: 12px;
}
.telemetry-chart-loading {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    color: #94a3b8;
    font-size: 12px;
    z-index: 2;
    pointer-events: none;
    overflow: hidden;
    /* Translucent backdrop while we wait for fresh data — the stale
       canvas itself is hidden via the sibling visibility rule below, so
       the backdrop just dims the chart area and lets the modal bg show
       through faintly. Scoped to chart-body, not the header or controls. */
    background: rgba(12, 17, 30, 0.75);
}
/* Hide the canvas itself while the loading element is visible so any
   stale frame doesn't peek through. Only scoped to the chart body. */
.telemetry-chart-body:has(> #telemetry-chart-loading:not([style*="display: none"])) > #telemetry-canvas {
    visibility: hidden;
}
.telemetry-chart-loading-text {
    color: #94a3b8;
    font-size: 13px;
    letter-spacing: 0.4px;
}
/* ── Chart-window pill (.cwp) ─────────────────────────────────
   True copy of the dark .map-switcher widget under its own
   namespace so visual changes here don't leak into the bottom-left
   map-layer-switcher. Six tabs (5m / 1hr / 12hr / 24hr / 1wk / 1mo).
   --cwp-tab-w sets the per-tab width; the indicator and labels share it. */
.cwp {
    --cwp-tab-w: 64px;
    --cwp-tab-h: 26px;
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    padding: 2px;
    background-color: transparent;
    border-radius: 9px;
    box-shadow: none;
    border: 1px solid rgba(255, 255, 255, 0.18);
    user-select: none;
    margin-left: auto;     /* push to the right of the legend row */
    flex-shrink: 0;
}
.cwp__indicator {
    width: var(--cwp-tab-w);
    height: var(--cwp-tab-h);
    background: rgba(15, 23, 42, 0.92);
    position: absolute;
    top: 2px;
    left: 2px;
    z-index: 1;
    border: 0.5px solid rgba(255, 255, 255, 0.18);
    box-shadow: 0 3px 8px rgba(0, 0, 0, 0.35), 0 1px 2px rgba(0, 0, 0, 0.25);
    border-radius: 7px;
    transition: left 0.2s ease-out;
    pointer-events: none;
}
/* Chart window switcher: the sliding "selected" indicator uses the LB
   (blue-pill) style — gradient fill, pill borders + inset highlight and a
   fully-rounded radius — so the active timeframe reads as a blue pill. The
   selected label gains the pill's text-shadow. Scoped to the switcher's id so
   the base .cwp widget is untouched. */
#chart-window-switcher {
    /* Drop the base .cwp border and wash the container with a very transparent
       (~20% opacity) version of the selected-pill (.cwp__indicator) gradient. */
    border: none;
    background: linear-gradient(to bottom, rgba(82, 168, 232, 0.2) 0%, rgba(55, 122, 208, 0.2) 100%);
}
#chart-window-switcher .cwp__indicator {
    background: linear-gradient(to bottom, #52a8e8 0%, #377ad0 100%);
    border: 1px solid #2e69a3;
    border-top-color: #4081af;
    border-bottom-color: #20559a;
    border-radius: 9px;
    box-shadow: inset 0 1px 0 0 #72b9eb, 0 1px 2px 0 rgba(0, 0, 0, 0.3);
    top: 6px;
    width: calc((100% - 12px) / 4);
}
#chart-window-switcher .cwp__tab:checked + .cwp__label {
    text-shadow: 0 -1px 1px #3275bc;
}
#chart-window-switcher .cwp__label:hover {
    opacity: 1;
}
.cwp__tab {
    width: var(--cwp-tab-w);
    height: var(--cwp-tab-h);
    position: absolute;
    z-index: 3;
    outline: none;
    opacity: 0;
    margin: 0;
    cursor: pointer;
}
.cwp__label {
    width: var(--cwp-tab-w);
    height: var(--cwp-tab-h);
    position: relative;
    z-index: 4;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    margin: 0;
    color: #f8fafc;
    font-size: 0.72rem;
    font-weight: normal;
    opacity: 0.7;
    cursor: pointer;
    transition: color 0.2s ease-out, opacity 0.2s ease-out, font-weight 0.2s ease-out;
}
.cwp__tab:checked + .cwp__label {
    color: #f8fafc;
    opacity: 1;
    font-weight: normal;
}
.cwp__tab--1:checked ~ .cwp__indicator { left: 2px; }
.cwp__tab--2:checked ~ .cwp__indicator { left: calc(var(--cwp-tab-w) + 2px); }
.cwp__tab--3:checked ~ .cwp__indicator { left: calc(var(--cwp-tab-w) * 2 + 2px); }
.cwp__tab--4:checked ~ .cwp__indicator { left: calc(var(--cwp-tab-w) * 3 + 2px); }
.cwp__tab--5:checked ~ .cwp__indicator { left: calc(var(--cwp-tab-w) * 4 + 2px); }
.cwp__tab--6:checked ~ .cwp__indicator { left: calc(var(--cwp-tab-w) * 5 + 2px); }

/* The time-window switcher sits on its own full-width row below the chart,
   above the stats bar. Full-width with 4 equal tabs, 19px radius and 6px
   padding. The indicator (absolute, padding-box %) and the labels (flex
   children, content-box %) state widths/offsets against the 6px (12px total)
   padding so they stay aligned. */
#chart-window-switcher {
    margin: 0 20px;
    width: auto;
    border-radius: 9px;
    padding: 6px;
}
#chart-window-switcher .cwp__label,
#chart-window-switcher .cwp__tab {
    width: calc(100% / 4);
}
#chart-window-switcher .cwp__label {
    font-size: 12px;
}
#chart-window-switcher .cwp__tab--1:checked ~ .cwp__indicator { left: 6px; }
#chart-window-switcher .cwp__tab--2:checked ~ .cwp__indicator { left: calc(6px + (100% - 12px) / 4); }
#chart-window-switcher .cwp__tab--3:checked ~ .cwp__indicator { left: calc(6px + (100% - 12px) * 2 / 4); }
#chart-window-switcher .cwp__tab--4:checked ~ .cwp__indicator { left: calc(6px + (100% - 12px) * 3 / 4); }

.chart-pause-btn {
    padding: 4px 12px;
    font-size: 11px;
    font-weight: 600;
    border-radius: 6px;
    border: 1px solid rgba(255,255,255,0.1);
    background: rgba(255,255,255,0.03);
    color: #94a3b8;
    cursor: pointer;
    transition: all 0.2s;
    margin-left: 8px;
}
.chart-pause-btn:hover {
    background: rgba(255,255,255,0.07);
    color: #e2e8f0;
}
.chart-pause-btn.paused {
    background: rgba(234, 179, 8, 0.15);
    border-color: rgba(234, 179, 8, 0.4);
    color: #eab308;
}

/* ── Chart Legend Row ── */
.chart-legend-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 16px;
    border-bottom: 1px solid rgba(255,255,255,0.06);
    flex-wrap: wrap;
    position: relative;
}
.chart-legend-items {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 6px 16px;
    text-align: center;
}
.chart-legend-item {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    cursor: pointer;
    transition: opacity 0.2s;
}
.chart-legend-item.hidden {
    opacity: 0.35;
}
.chart-legend-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    display: inline-block;
}
.chart-legend-remove {
    background: #ef4444;
    border: none;
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    cursor: pointer;
    padding: 2px 8px;
    line-height: 1;
    border-radius: 6px;
    margin-left: 6px;
    white-space: nowrap;
}
.chart-legend-remove:hover {
    background: #dc2626;
}
.chart-add-series-btn {
    padding: 4px 10px;
    font-size: 12px;
    font-weight: 600;
    border-radius: 6px;
    border: 1px solid rgba(96, 165, 250, 0.3);
    background: rgba(96, 165, 250, 0.08);
    color: #60a5fa;
    cursor: pointer;
    transition: all 0.2s;
}
.chart-add-series-btn:hover {
    background: rgba(96, 165, 250, 0.15);
}
.chart-add-series-dropdown {
    position: absolute;
    top: 100%;
    left: 16px;
    z-index: 20;
    background: rgba(12, 17, 30, 0.98);
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: 8px;
    padding: 0;
    min-width: 240px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.4);
    /* Cap the dropdown height so a long measurement list doesn't run off
       the bottom of the viewport. The picker header stays pinned; only
       the measurement options scroll (see .cs-options below). */
    max-height: 450px;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
/* Buoy picker header inside the add-series dropdown — chevron pill that
   toggles a buoy menu, letting the user pick a different buoy in their
   fleet before choosing a measurement. */
.chart-add-series-dropdown .cs-buoy-picker {
    padding: 6px 8px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.chart-add-series-dropdown .cs-buoy-picker-toggle {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    font-size: 11px;
    font-weight: 600;
    border-radius: 6px;
    border: 1px solid rgba(96, 165, 250, 0.3);
    background: rgba(96, 165, 250, 0.08);
    color: #cbd5e1;
    cursor: pointer;
    transition: background 0.15s;
}
.chart-add-series-dropdown .cs-buoy-picker-toggle:hover {
    background: rgba(96, 165, 250, 0.15);
}
/* The visible content lives inside the Uiverse `.text` wrapper, so the
   gap between From/buoy/chevron needs to live there too. */
.chart-add-series-dropdown .cs-buoy-picker-toggle > .text {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.chart-add-series-dropdown .cs-bp-label {
    color: #94a3b8;
    font-weight: 500;
}
.chart-add-series-dropdown .cs-bp-buoy {
    color: #60a5fa;
}
.chart-add-series-dropdown .cs-bp-chevron {
    font-size: 9px;
    color: #94a3b8;
    margin-left: 2px;
}
/* Buoy menu — opens between the picker and the options list. Scrolls
   internally if the fleet is large. */
.chart-add-series-dropdown .cs-buoy-menu {
    max-height: 180px;
    overflow-y: auto;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    padding: 2px 0;
}
.chart-add-series-dropdown .cs-buoy-menu-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 6px 14px;
    font-size: 12px;
    color: #e2e8f0;
    cursor: pointer;
    transition: background 0.15s;
}
.chart-add-series-dropdown .cs-buoy-menu-item:hover {
    background: rgba(255, 255, 255, 0.05);
}
.chart-add-series-dropdown .cs-buoy-menu-item.selected {
    color: #60a5fa;
}
.chart-add-series-dropdown .cs-buoy-menu-check {
    font-size: 10px;
    color: #60a5fa;
}
.chart-add-series-dropdown .cs-options {
    padding: 4px 0;
    /* Takes the remaining vertical space inside the capped dropdown and
       scrolls when the measurement list overflows. */
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}
.chart-add-series-dropdown .cs-empty {
    padding: 10px 14px;
    font-size: 12px;
    color: #64748b;
}
.chart-series-option {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 8px 14px;
    font-size: 12px;
    color: #e2e8f0;
    cursor: pointer;
    transition: background 0.15s;
}
.chart-series-option:hover {
    background: rgba(255,255,255,0.05);
}
.chart-series-option.disabled {
    color: #475569;
    cursor: default;
}
.chart-series-option.disabled:hover {
    background: none;
}
.chart-series-option-value {
    color: #64748b;
    font-size: 11px;
}
/* Two-level group -> measurement picker inside the +Series dropdown (mirrors
   the alert-config sensor picker; scoped + re-coloured for the chart overlay
   since the .ac-sp-* rules are #alert-config-modal-only). */
.chart-add-series-dropdown .cs-sp-group {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 8px 14px;
    background: transparent;
    border: none;
    color: #e2e8f0;
    font-size: 12px;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
    transition: background 0.15s;
}
.chart-add-series-dropdown .cs-sp-group::after { content: none; display: none; }
.chart-add-series-dropdown .cs-sp-group:hover { background: rgba(255, 255, 255, 0.05); }
.chart-add-series-dropdown .cs-sp-group-name { flex: 1 1 auto; min-width: 0; }
.chart-add-series-dropdown .cs-sp-group-count {
    flex: 0 0 auto;
    font-size: 11px;
    color: #94a3b8;
    background: rgba(255, 255, 255, 0.06);
    border-radius: 10px;
    padding: 1px 8px;
}
.chart-add-series-dropdown .cs-sp-group-arrow { flex: 0 0 auto; color: #64748b; font-size: 11px; }
.chart-add-series-dropdown .cs-sp-back {
    display: block;
    width: 100%;
    text-align: left;
    padding: 6px 14px;
    margin-bottom: 2px;
    background: transparent;
    border: none;
    color: #60a5fa;
    font-size: 12px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
}
.chart-add-series-dropdown .cs-sp-back::after { content: none; display: none; }
.chart-add-series-dropdown .cs-sp-back:hover { color: #93c5fd; }

/* ── Analytics Toolbar ── */
.chart-analytics-toolbar {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 16px;
    flex-wrap: wrap;
    position: relative;  /* anchors the #chart-series-dropdown (top:100%) */
}
/* + Series now lives on the toolbar — match the analytics pills' size. */
.chart-analytics-toolbar .chart-add-series-btn { padding: 6px 12px; line-height: 1; }
.chart-analytics-pill {
    /* Sized to the average of the old pill (3px/10px/11px/6px) and the
       bc-select MA trigger (10px/14px/13px/8px) so the analytics row reads
       as one consistent control size. */
    padding: 6px 12px;
    font-size: 12px;
    line-height: 1;
    font-weight: 500;
    border-radius: 7px;
    border: 1px solid rgba(255,255,255,0.1);
    background: rgba(255,255,255,0.03);
    color: #94a3b8;
    cursor: pointer;
    transition: all 0.2s;
}
.chart-analytics-pill:hover {
    background: rgba(255,255,255,0.07);
    color: #e2e8f0;
}
.chart-analytics-pill.active {
    background: rgba(37, 99, 235, 0.2);
    border-color: rgba(37, 99, 235, 0.5);
    color: #60a5fa;
}
/* Active pill inside a bc-btn-group — matches the chart-window-btn
   active style for consistency across the chart toolbar. */
.bc-btn-group__btn.chart-analytics-pill.active {
    background-color: rgba(96, 165, 250, 0.2);
    color: #60a5fa;
}
.bc-btn-group__btn.chart-analytics-pill.active:hover {
    background-color: rgba(96, 165, 250, 0.28);
    color: #93c5fd;
}
/* Analytics + MA-window toggles: drop the joined-button-group look. These
   are independent toggles, not a connected toolbar cluster, so each pill
   stands alone with its own border and breathing room — matching the
   standalone-pill pattern used by .chart-add-series-btn and the threshold
   pills elsewhere in this area. */
.chart-analytics-toolbar .bc-btn-group.chart-analytics-group {
    background: transparent;
    border: none;
    overflow: visible;
    gap: 6px;
}
.chart-analytics-toolbar .bc-btn-group.chart-analytics-group .bc-btn-group__btn + .bc-btn-group__btn {
    border-left-color: rgba(255, 255, 255, 0.1);
}
.chart-ma-window {
    display: flex;
    gap: 4px;
    align-items: center;
    /* Wrap to its own full-width row below the analytics pills (like the
       threshold controls) instead of opening inline beside the MA button. */
    flex-basis: 100%;
}
.chart-threshold-pills {
    display: flex;
    gap: 6px;
    align-items: center;
    flex-wrap: wrap;
}
.chart-threshold-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    font-size: 11px;
    font-weight: 500;
    border-radius: 6px;
    border: 1px solid;
    background: rgba(255,255,255,0.03);
}
.chart-threshold-pill-edit,
.chart-threshold-pill-delete {
    background: none;
    border: none;
    color: inherit;
    font-size: 12px;
    cursor: pointer;
    padding: 0 1px;
    line-height: 1;
    opacity: 0.6;
}
.chart-threshold-pill-edit:hover { opacity: 1; }
.chart-threshold-pill-delete:hover { opacity: 1; color: #ef4444; }
.chart-threshold-save,
.chart-threshold-cancel {
    padding: 3px 10px;
    font-size: 11px;
    border-radius: 4px;
    border: 1px solid rgba(255,255,255,0.1);
    cursor: pointer;
}
.chart-threshold-save {
    background: rgba(96, 165, 250, 0.15);
    color: #60a5fa;
    border-color: rgba(96, 165, 250, 0.3);
}
.chart-threshold-save:hover { background: rgba(96, 165, 250, 0.25); }
.chart-threshold-cancel {
    background: rgba(255,255,255,0.03);
    color: #94a3b8;
}
.chart-threshold-cancel:hover { background: rgba(255,255,255,0.07); }
.chart-threshold-controls {
    display: flex;
    gap: 6px;
    align-items: center;
}
.chart-threshold-select, .chart-threshold-input {
    padding: 3px 8px;
    font-size: 11px;
    border-radius: 4px;
    border: 1px solid rgba(255,255,255,0.1);
    background: rgba(255,255,255,0.05);
    color: #e2e8f0;
}
.chart-threshold-input {
    width: 80px;
}
/* Chart-toolbar action buttons unified to the global navy + skew-wave look
   (same as #btn-new-alert / the base `button` rule). Only background, text
   colour, border and hover are overridden — each button keeps its own padding,
   font-size and border-radius from the rules above, so they read as smaller
   versions of the reference. The base button ::after wave + overflow:hidden are
   already inherited, and every one of these carries the two-span pattern so the
   label stays above the wave. Placed after the rules above to win on source
   order; the .active pill states (higher specificity) are intentionally kept.
   The MA-period picker, threshold <select>s and <input> are left on their flat
   style — native/select controls can't carry the wave. */
.chart-analytics-pill,
.bc-btn-group__btn.chart-analytics-pill,
.chart-add-series-btn,
.chart-threshold-save,
.chart-threshold-cancel {
    background: rgba(22, 56, 128, 0.5);   /* #163880 at 50% opacity */
    color: #fff;
    border: none;
    font-weight: normal;
}
.chart-analytics-pill:hover,
.bc-btn-group__btn.chart-analytics-pill:hover,
.chart-add-series-btn:hover,
.chart-threshold-save:hover,
.chart-threshold-cancel:hover {
    /* Background stays navy (50% opacity); the inherited ::after wave provides
       the hover. Label colour shift matches the base button:hover. */
    background: rgba(22, 56, 128, 0.5);
    color: #14587F;
}
/* ── Chart-toolbar action buttons → LB (blue-pill) style ──────────────────────
   The 3 analytics toggle pills + threshold Save/Cancel carry .blue-pill-button.
   This scoped block (later + higher specificity than the legacy navy/active rules
   above) makes the blue-pill gradient win. Inactive toggle pills are greyscaled;
   .active (overlay on) shows full colour. Cancel is the LB style, greyscaled. */
.chart-analytics-toolbar .chart-analytics-pill.blue-pill-button,
.chart-analytics-toolbar .chart-threshold-save.blue-pill-button,
.chart-analytics-toolbar .chart-threshold-cancel.blue-pill-button,
.telemetry-chart-overlay .chart-add-series-btn.blue-pill-button {
    background: linear-gradient(to bottom, #52a8e8 0%, #377ad0 100%);
    border: 1px solid #2e69a3;
    border-top-color: #4081af;
    border-bottom-color: #20559a;
    border-radius: 9px;
    color: #eeeeee;
    font-weight: bold;
}
.chart-analytics-toolbar .chart-analytics-pill.blue-pill-button:hover,
.chart-analytics-toolbar .chart-threshold-save.blue-pill-button:hover,
.chart-analytics-toolbar .chart-threshold-cancel.blue-pill-button:hover,
.telemetry-chart-overlay .chart-add-series-btn.blue-pill-button:hover {
    background: linear-gradient(to bottom, #3e9ee5 0%, #206bcb 100%);
    color: #eeeeee;
}
.chart-analytics-toolbar .chart-analytics-pill.blue-pill-button:not(.active) {
    filter: grayscale(0.6);
    opacity: 0.8;
}
.chart-analytics-toolbar .chart-threshold-cancel.blue-pill-button {
    filter: grayscale(1);
}
.btn-reset-zoom {
    padding: 3px 10px;
    font-size: 11px;
    font-weight: 500;
    border-radius: 6px;
    border: 1px solid rgba(239, 68, 68, 0.3);
    background: rgba(239, 68, 68, 0.1);
    color: #ef4444;
    cursor: pointer;
    margin-left: auto;
}
.btn-reset-zoom:hover {
    background: rgba(239, 68, 68, 0.2);
}

/* ── Stats Bar ── */
.chart-stats-bar {
    padding: 8px 16px;
    font-size: 11px;
    color: #94a3b8;
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.chart-stats-row {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    padding: 5px;
}
.chart-stats-label {
    font-weight: 600;
    min-width: 100px;
}
.chart-stats-export {
    padding: 2px 10px;
    font-size: 11px;
    border-radius: 4px;
    border: 1px solid rgba(255,255,255,0.1);
    background: rgba(255,255,255,0.03);
    color: #94a3b8;
    cursor: pointer;
}
.chart-stats-export:hover {
    background: rgba(255,255,255,0.07);
    color: #e2e8f0;
}

.buoy-measurement-value-row {
    display: flex;
    align-items: center;
    gap: 8px;
}
.buoy-measurement-icon {
    width: 22px;
    font-size: 18px;
    text-align: center;
    color: #60a5fa;
    flex-shrink: 0;
}
.buoy-measurement-name {
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    color: #64748b;
    margin-top: 2px;
}
.buoy-measurement-value {
    font-size: 18px;       /* was 22px — reduced ~20% for a calmer grid */
    font-weight: 700;
    /* Back to white (2026-07-21) — estimated readings are marked by the
       tile's wand icon + Estimated Read popup, not by text colour. */
    color: #e2e8f0;
    line-height: 1.1;
}
.buoy-measurement-uom {
    font-size: 11px;
    font-weight: 400;
    color: #64748b;
    margin-left: 2px;
}

/* ── Buoy Alarm Items (modal) ─────────────────────────────── */
.buoy-alarms-section {
    margin-bottom: 8px;
}
.buoy-alarm-item {
    padding: 8px 10px;
    margin-bottom: 4px;
    border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, 0.06);
}
.buoy-alarm-item.alarm-high {
    background: rgba(234, 179, 8, 0.08);
    border-left: 3px solid #eab308;
}
.buoy-alarm-item.alarm-critical {
    background: rgba(239, 68, 68, 0.08);
    border-left: 3px solid #ef4444;
}
.buoy-alarm-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 2px;
}
.buoy-alarm-severity {
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.5px;
}
.alarm-high .buoy-alarm-severity { color: #eab308; }
.alarm-critical .buoy-alarm-severity { color: #ef4444; }
.buoy-alarm-date {
    font-size: 9px;
    color: #64748b;
}
.buoy-alarm-name {
    font-size: 11px;
    font-weight: 600;
    color: #e2e8f0;
}
.buoy-alarm-status {
    font-size: 9px;
    color: #94a3b8;
    margin-top: 1px;
}

/* ── Leaflet Overrides ─────────────────────────────────────── */
.leaflet-control-zoom a {
    background: rgba(12, 17, 30, 0.8) !important;
    color: #e2e8f0 !important;
    border-color: rgba(255,255,255,0.1) !important;
    backdrop-filter: blur(8px);
}
.leaflet-control-zoom a:hover {
    background: rgba(30, 40, 60, 0.9) !important;
}
.leaflet-control-attribution {
    background: rgba(12, 17, 30, 0.6) !important;
    color: #64748b !important;
    font-size: 9px !important;
}
.leaflet-control-attribution a { color: #94a3b8 !important; }

/* ── Pulse-bar loader ─────────────────────────────────────────
   24 vertical bars stretch in sequence around an 80×80 frame,
   forming a rotating "ring" pulse. Use as a status / busy
   indicator. Override --bc-loader-color to retint. */
.bc-loader {
    --bc-loader-color: rgb(53, 143, 246);
    display: flex;
    justify-content: center;
    align-items: center;
    width: 80px;
    height: 80px;
    position: relative;
}
.bc-loader-inner {
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
}
.bc-loader-block {
    display: inline-block;
    width: 8px;
    height: 10px;
    margin: 2px;
    background-color: var(--bc-loader-color);
    box-shadow: 0 0 30px var(--bc-loader-color);
    animation: bc-loader-pulse 5s infinite;
}
.bc-loader-block:nth-child(1)  { animation-delay: 0.2s; }
.bc-loader-block:nth-child(2)  { animation-delay: 0.4s; }
.bc-loader-block:nth-child(3)  { animation-delay: 0.6s; }
.bc-loader-block:nth-child(4)  { animation-delay: 0.8s; }
.bc-loader-block:nth-child(5)  { animation-delay: 1.0s; }
.bc-loader-block:nth-child(6)  { animation-delay: 1.2s; }
.bc-loader-block:nth-child(7)  { animation-delay: 1.4s; }
.bc-loader-block:nth-child(8)  { animation-delay: 1.6s; }
.bc-loader-block:nth-child(9)  { animation-delay: 1.8s; }
.bc-loader-block:nth-child(10) { animation-delay: 2.0s; }
.bc-loader-block:nth-child(11) { animation-delay: 2.2s; }
.bc-loader-block:nth-child(12) { animation-delay: 2.4s; }
.bc-loader-block:nth-child(13) { animation-delay: 2.6s; }
.bc-loader-block:nth-child(14) { animation-delay: 2.8s; }
.bc-loader-block:nth-child(15) { animation-delay: 3.0s; }
.bc-loader-block:nth-child(16) { animation-delay: 3.2s; }
.bc-loader-block:nth-child(17) { animation-delay: 3.4s; }
.bc-loader-block:nth-child(18) { animation-delay: 3.6s; }
.bc-loader-block:nth-child(19) { animation-delay: 3.8s; }
.bc-loader-block:nth-child(20) { animation-delay: 4.0s; }
.bc-loader-block:nth-child(21) { animation-delay: 4.2s; }
.bc-loader-block:nth-child(22) { animation-delay: 4.4s; }
.bc-loader-block:nth-child(23) { animation-delay: 4.6s; }
.bc-loader-block:nth-child(24) { animation-delay: 4.8s; }
@keyframes bc-loader-pulse {
    0%   { transform: scale(1);    box-shadow: 0 0 40px var(--bc-loader-color); }
    13%  { transform: scale(1, 4); box-shadow: 0 0 60px var(--bc-loader-color); }
    26%  { transform: scale(1);    box-shadow: 0 0 40px var(--bc-loader-color); }
}

/* ── Alert Config Redesign ──────────────────────────────────
   New tabbed/staged Configure Alert modal. All rules live under
   #alert-config-modal so we never collide with global selectors. */
#alert-config-modal .modal-content.alert-config-content {
    width: 720px;
    max-width: calc(100vw - 32px);
    max-height: 85vh;
    display: flex;
    flex-direction: column;
}
#alert-config-modal .alert-config-body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding-right: 4px;
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    gap: 24px;
}
#alert-config-modal .alert-config-footer {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding-top: 14px;
    margin-top: 14px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}
#alert-config-modal .ac-footer-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
}

/* Footer CTA is a .blue-pill-button now. The pre-existing id-scoped
   #alert-config-modal .ac-btn-save rules (background/border/border-radius/
   overflow/isolation) out-specify the bare .blue-pill-button class, so
   re-assert the pill's enabled-state visuals at matching id specificity
   (1,2,0) to let the gradient/25px pill actually render. */
#alert-config-modal .ac-btn-save.blue-pill-button {
    background: linear-gradient(to bottom, #52a8e8 0%, #377ad0 100%);
    border-top: 1px solid #4081af;
    border-right: 1px solid #2e69a3;
    border-bottom: 1px solid #20559a;
    border-left: 1px solid #2e69a3;
    border-radius: 25px;
    box-shadow: inset 0 1px 0 0 #72b9eb, 0 1px 2px 0 rgba(0, 0, 0, .3);
    overflow: visible;
    isolation: auto;
}
#alert-config-modal .ac-btn-save.blue-pill-button:hover {
    background: linear-gradient(to bottom, #3e9ee5 0%, #206bcb 100%);
    border-top: 1px solid #2a73a6;
    border-right: 1px solid #165899;
    border-bottom: 1px solid #07428f;
    border-left: 1px solid #165899;
    box-shadow: inset 0 1px 0 0 #62b1e9;
}
/* Dim + lock the CTA when blocked. Pin the (gradient) background so the
   disabled state stays the dimmed pill, and keep hover from flipping it. */
#alert-config-modal .ac-btn-save.blue-pill-button:disabled,
#alert-config-modal .ac-btn-save.blue-pill-button:disabled:hover {
    background: linear-gradient(to bottom, #52a8e8 0%, #377ad0 100%);
    border-top: 1px solid #4081af;
    border-right: 1px solid #2e69a3;
    border-bottom: 1px solid #20559a;
    border-left: 1px solid #2e69a3;
    opacity: 0.45;
    cursor: not-allowed;
    box-shadow: inset 0 1px 0 0 rgba(114, 185, 235, 0.4), 0 1px 2px 0 rgba(0, 0, 0, 0.3);
}

/* ── Config modal: [left rail | form] layout ─────────────────── */
#alert-config-modal .ac-rail-mount {
    flex: 0 0 141px;
    position: sticky;
    top: 0;
}
#alert-config-modal .ac-form-col {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 24px;
}
#alert-config-modal .ac-rail-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
#alert-config-modal .ac-rail-step {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 10px;
    border-radius: 8px;
    cursor: pointer;
    border-left: 2px solid transparent;
    transition: background 0.15s, border-color 0.15s;
}
#alert-config-modal .ac-rail-step:hover:not(.ac-rail-step--locked) {
    background: rgba(255, 255, 255, 0.03);
}
#alert-config-modal .ac-rail-num {
    flex: 0 0 22px;
    height: 22px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: 'Oxanium', system-ui, sans-serif;
    font-size: 12px;
    font-weight: 700;
    background: rgba(255, 255, 255, 0.06);
    color: #94a3b8;
    border: 1px solid rgba(255, 255, 255, 0.10);
}
#alert-config-modal .ac-rail-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}
#alert-config-modal .ac-rail-label {
    font-size: 13px;
    font-weight: 600;
    color: #e2e8f0;
}
#alert-config-modal .ac-rail-status {
    font-size: 11px;
    color: #64748b;
    line-height: 1.3;
}
/* The Name step's status is the alert name (variable length) — clip it. */
#alert-config-modal .ac-rail-status--name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
}
/* done */
#alert-config-modal .ac-rail-step--done .ac-rail-num {
    background: rgba(56, 189, 248, 0.15);
    border-color: rgba(56, 189, 248, 0.45);
    color: #38bdf8;
}
#alert-config-modal .ac-rail-step--done .ac-rail-status { color: #38bdf8; }
/* active */
#alert-config-modal .ac-rail-step--active {
    background: rgba(56, 189, 248, 0.06);
    border-left-color: #38bdf8;
}
#alert-config-modal .ac-rail-step--active .ac-rail-num {
    background: #38bdf8;
    border-color: #38bdf8;
    color: #0b1220;
}
#alert-config-modal .ac-rail-step--active .ac-rail-status { color: #bae6fd; }
/* locked */
#alert-config-modal .ac-rail-step--locked {
    cursor: default;
    opacity: 0.5;
}
@media (prefers-reduced-motion: reduce) {
    #alert-config-modal .ac-rail-step { transition: none; }
}

/* ── bc-select — generic custom-select ────────────────────── */
.bc-select {
    position: relative;
    display: inline-block;
    width: 100%;
    font-family: inherit;
}
/* Mirror .sensor-guide-select exactly so the rule-editor pickers read as
   select-boxes, not buttons. The global <button> rules (skew :after wave,
   navy background, hover colour shift) get explicitly nuked here so a
   bcSelect trigger looks and behaves identical to the buoy-modal picker. */
.bc-select__trigger {
    display: flex;
    align-items: center;
    width: 100%;
    padding: 10px 14px;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 8px;
    color: #e2e8f0;
    font-size: 13px;
    font-weight: 500;
    line-height: 1;
    cursor: pointer;
    text-align: left;
    gap: 10px;
    user-select: none;
    transition: background 0.2s, border-color 0.2s;
    isolation: auto;
    overflow: visible;
}
.bc-select__trigger:hover {
    background: rgba(255, 255, 255, 0.03);
    border-color: rgba(255, 255, 255, 0.12);
    color: #e2e8f0;
}
/* MA period picker in the chart analytics row — shrink the trigger to match
   the analytics pills (averaged size). Scoped so other bc-selects are
   unaffected. */
#chart-ma-select .bc-select__trigger {
    padding: 6px 12px;
    font-size: 12px;
    border-radius: 7px;
}
/* Kill the global <button>::after skew wave so the picker doesn't get a
   button hover animation it shouldn't carry. */
.bc-select__trigger::after { content: none; display: none; }
.bc-select.bc-select--open .bc-select__trigger {
    border-color: rgba(56, 189, 248, 0.4);
    background: rgba(255, 255, 255, 0.05);
}
.bc-select__icon {
    display: inline-flex;
    align-items: center;
    color: #64748b;
    font-size: 14px;
    flex: 0 0 auto;
}
.bc-select__icon:empty { display: none; }
.bc-select__value {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.bc-select__value.bc-select__value--placeholder {
    color: #64748b;
    font-style: italic;
}
.bc-select__chevron {
    flex: 0 0 auto;
    color: #64748b;
    font-size: 11px;
    transition: transform 0.15s ease;
}
.bc-select.bc-select--open .bc-select__chevron {
    transform: rotate(180deg);
}
.bc-select__panel {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    z-index: 2100;
    min-width: 100%;
    max-height: 50vh;
    overflow-y: auto;
    background: #0d0d0d;
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 6px;
    box-shadow: 0 12px 24px rgba(0, 0, 0, 0.5);
    padding: 4px;
    animation: ac-stage-in 0.14s ease;
}
.bc-select__panel[hidden] { display: none; }
/* Flip upward (open above the field) when JS adds .bc-select--up because the
   panel wouldn't fit below — e.g. the last field in the rule editor. */
.bc-select--up > .bc-select__panel {
    top: auto;
    bottom: calc(100% + 6px);
}
/* In-place anchored dropdown — the panel drops directly under its field
   (top:100% from the base .bc-select__panel rule). When open, lift its
   stacking context above sibling rows so it overlaps the field below. */
.bc-select.bc-select--open { z-index: 50; }
@media (prefers-reduced-motion: reduce) {
    .bc-select__panel { animation: none; }
}
.bc-select__option {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 13px;
    color: #cbd5e1;
}
.bc-select__option:hover,
.bc-select__option.bc-select__option--highlight {
    background: rgba(96, 165, 250, 0.12);
    color: #e2e8f0;
}
.bc-select__group {
    padding: 8px 10px 4px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.8px;
    text-transform: uppercase;
    color: #64748b;
    pointer-events: none;
}
.bc-select__group:first-child { padding-top: 4px; }
.bc-select__option--selected {
    background: rgba(96, 165, 250, 0.18);
    color: #bfdbfe;
}
.bc-select__option-icon {
    flex: 0 0 auto;
    color: #60a5fa;
}
.bc-select__option-icon:empty { display: none; }
.bc-select__option-name { flex: 1 1 auto; }
.bc-select__option-meta {
    flex: 0 0 auto;
    color: #64748b;
    font-size: 11px;
}
.bc-select[data-disabled="true"] {
    opacity: 0.5;
}
/* Keep pointer events so a [data-tooltip] hover still fires on a locked select;
   opening is already blocked in JS (bindEvents checks data-disabled). */
.bc-select[data-disabled="true"] .bc-select__trigger { cursor: not-allowed; }
.bc-select__panel::-webkit-scrollbar { width: 6px; }
.bc-select__panel::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 3px; }

/* ─── Alert config modal — unified field styling ────────────────────────────
   The two-level sensor picker, every bc-select, and the text inputs all draw
   from one set of design tokens, so the rule editor reads as a single
   congruent family: the cyan look of the sensor picker the design is built
   around (rather than the bc-select's old translucent-grey + blue-400). */
#alert-config-modal {
    --acf-bg: #1e293b;
    --acf-border: rgba(255, 255, 255, 0.1);
    --acf-radius: 6px;
    --acf-accent: #38bdf8;
    --acf-accent-hover: rgba(56, 189, 248, 0.12);
    --acf-accent-sel: rgba(56, 189, 248, 0.16);
    --acf-accent-border: rgba(56, 189, 248, 0.4);
    --acf-accent-open: rgba(56, 189, 248, 0.6);
    --acf-panel-bg: #0f172a;
}
/* bc-select trigger → matches .ac-sp-trigger */
#alert-config-modal .bc-select__trigger {
    height: 34px;
    padding: 0 12px;
    background: var(--acf-bg);
    border: 1px solid var(--acf-border);
    border-radius: var(--acf-radius);
    font-weight: 500;
}
#alert-config-modal .bc-select__trigger:hover {
    background: var(--acf-bg);
    border-color: var(--acf-accent-border);
}
#alert-config-modal .bc-select.bc-select--open .bc-select__trigger {
    background: var(--acf-bg);
    border-color: var(--acf-accent-open);
}
#alert-config-modal .bc-select__value--placeholder { font-style: normal; }
/* bc-select panel + options → matches .ac-sp-panel / .ac-sp-item */
#alert-config-modal .bc-select__panel {
    background: var(--acf-panel-bg);
    border: 1px solid var(--acf-accent-border);
    border-radius: 8px;
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.55);
    /* Size to the (one-line) options rather than being forced to the
       natural-width trigger's width via the base left:0;right:0. */
    right: auto;
    width: max-content;
}
#alert-config-modal .bc-select__option {
    border-radius: var(--acf-radius);
    color: #e2e8f0;
    white-space: nowrap;
}
#alert-config-modal .bc-select__option:hover,
#alert-config-modal .bc-select__option.bc-select__option--highlight {
    background: var(--acf-accent-hover);
    color: #e2e8f0;
}
#alert-config-modal .bc-select__option--selected {
    background: var(--acf-accent-sel);
    color: #e2e8f0;
}
#alert-config-modal .bc-select__option-icon { color: var(--acf-accent); }
#alert-config-modal .bc-select__icon { color: var(--acf-accent); }
#alert-config-modal .bc-select__group { letter-spacing: 0.6px; }

#alert-config-modal .ac-stage {
    display: flex;
    flex-direction: column;
    gap: 8px;
    animation: ac-stage-in 0.2s ease;
}
@keyframes ac-stage-in {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}
#alert-config-modal .ac-stage-label {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.6px;
    color: #94a3b8;
    text-transform: uppercase;
}
/* Sensor / Condition / Value labels read in lowercase to match the
   "and is sustained for…" label, not shouty caps. */
#alert-config-modal .ac-triple-row .ac-stage-label { text-transform: none; }
/* Rule-editor field labels (sensor / condition / value / sustained) adopt the
   .ac-rules-title look: 13px, muted slate, no caps or letter-spacing. */
#alert-config-modal .ac-rule--editing .ac-stage-label {
    font-size: 13px;
    color: #64748b;
    letter-spacing: normal;
    text-transform: none;
}
/* Severity / Notifications section headings reuse the rules-header-title look
   (13px, 600, light slate — no caps / letter-spacing) so all the modal's
   section headers read consistently. */
#alert-config-modal .ac-stage[data-stage="notifications"] > .ac-stage-label {
    font-size: 13px;
    font-weight: 600;
    letter-spacing: normal;
    text-transform: none;
    color: #e2e8f0;
}
#alert-config-modal .ac-input {
    height: 34px;
    padding: 0 12px;
    background: var(--acf-bg);
    border: 1px solid var(--acf-border);
    border-radius: var(--acf-radius);
    color: #e2e8f0;
    font-size: 13px;
    font-family: inherit;
}
#alert-config-modal .ac-input:focus {
    outline: none;
    border-color: var(--acf-accent-open);
    background: var(--acf-bg);
}
#alert-config-modal .ac-input.ac-input--error {
    border-color: rgba(239, 68, 68, 0.6);
}
#alert-config-modal .ac-input[readonly] {
    background: rgba(255, 255, 255, 0.02);
    color: #94a3b8;
    cursor: default;
}
#alert-config-modal .ac-name-row {
    display: flex;
    gap: 8px;
    align-items: center;
}
#alert-config-modal .ac-name-row .ac-input {
    flex: 1 1 auto;
}
#alert-config-modal .ac-name-hint {
    margin-top: 7px;
    font-size: 11.5px;
    line-height: 1.45;
    color: #64748b;
}
#alert-config-modal .ac-name-hint em {
    font-style: italic;
    color: #94a3b8;
}
/* Standardize the config-modal ACTION buttons to the blue-pill look.
   button.blue-pill-button (1,1,1) outranks the legacy id-scoped
   .ac-name-save/.ac-name-edit/.ac-add-rule-btn/.ac-rule-save
   rules (1,1,0), so the pill wins without editing each one. Layout props
   (flex/margin) on those rules are left intact. */
#alert-config-modal button.blue-pill-button {
    font-size: 12px;
    padding: 7px 16px;
    border-radius: 25px;
    background: linear-gradient(to bottom, #52a8e8 0%, #377ad0 100%);
    border-top: 1px solid #4081af;
    border-right: 1px solid #2e69a3;
    border-bottom: 1px solid #20559a;
    border-left: 1px solid #2e69a3;
    box-shadow: inset 0 1px 0 0 #72b9eb, 0 1px 2px 0 rgba(0, 0, 0, .3);
    text-shadow: 0 -1px 1px #3275bc;
    color: #eeeeee;
}
#alert-config-modal button.blue-pill-button:hover {
    background: linear-gradient(to bottom, #3e9ee5 0%, #206bcb 100%);
    border-top: 1px solid #2a73a6;
    border-right: 1px solid #165899;
    border-bottom: 1px solid #07428f;
    border-left: 1px solid #165899;
    box-shadow: inset 0 1px 0 0 #62b1e9;
    color: #eeeeee;
}
#alert-config-modal button.blue-pill-button::after { content: none; display: none; }
#alert-config-modal button.blue-pill-button[disabled] { opacity: 0.45; cursor: not-allowed; }
/* Empty-rules inline "Add a rule." link (dotted underline). */
/* Empty-rules state — modelled on the alarms-hero (icon + bold title + sub),
   no button. */
#alert-config-modal .ac-rules-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 8px;
    padding: 0 24px 28px;
}
#alert-config-modal .ac-rules-empty-icon {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(56, 189, 248, 0.12);
    color: #38bdf8;
    font-size: 22px;
    margin-bottom: 4px;
}
#alert-config-modal .ac-rules-empty-title {
    font-size: 15px;
    font-weight: 700;
    color: #e2e8f0;
}
#alert-config-modal .ac-rules-empty-sub {
    font-size: 13px;
    color: #94a3b8;
    max-width: 360px;
    line-height: 1.5;
}
#alert-config-modal .ac-name-save,
#alert-config-modal .ac-name-edit {
    flex: 0 0 auto;
    padding: 8px 16px;
    font-size: 12px;
    line-height: 1;
    border-radius: 6px;
}
/* Rules-section header shows the alert's name — bold, not uppercased. */
#alert-config-modal .ac-rules-title {
    color: #64748b;
    font-size: 13px;
    font-weight: 700;
    text-transform: none;
}
/* "+ Add rule" — sits inline at the right of the ALERT RULES header.
   Sized to match .ac-name-edit (small pill) so the two control buttons
   feel like a family. The .ac-rules-header flex container is what
   actually pushes it right via margin-left:auto. */
#alert-config-modal .ac-rules-header {
    display: flex;
    align-items: center;
    gap: 8px;
}
/* Left-aligned heading shown beside the add-rule button once a rule exists
   (same weight/size as the rail step labels). */
#alert-config-modal .ac-rules-header-title {
    font-size: 13px;
    font-weight: 600;
    color: #e2e8f0;
}
#alert-config-modal .ac-rules-header .ac-add-rule-btn {
    margin-left: auto;
}
#alert-config-modal .ac-add-rule-btn,
#alert-config-modal .ac-goto-severity-btn {
    flex: 0 0 auto;
    padding: 8px 16px;
    background: #163880;
    color: #fff;
    border: none;
    border-radius: 6px;
    font-size: 12px;
    line-height: 1;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    isolation: isolate;
}
/* Compact "+ Add another" button — 3px padding (the severity CTA keeps its
   larger padding). */
#alert-config-modal .ac-add-rule-btn { padding: 3px; }
#alert-config-modal .ac-add-rule-btn:hover,
#alert-config-modal .ac-goto-severity-btn:hover { color: #14587F; }
#alert-config-modal .ac-add-rule-btn:after,
#alert-config-modal .ac-goto-severity-btn:after {
    content: "";
    position: absolute;
    inset: 0;
    background-color: #071831;
    transform: scaleY(0);
    transform-origin: bottom center;
    transition: transform 600ms cubic-bezier(0.48,0,0.12,1);
    z-index: -1;
}
#alert-config-modal .ac-add-rule-btn:hover:after,
#alert-config-modal .ac-goto-severity-btn:hover:after {
    transform: scaleY(2);
}
/* "Set alert severity level" CTA under the rule list — a centred helper line
   above a button that mirrors the dark-blue "+ Add rule" pill. */
#alert-config-modal .ac-rules-advance {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    margin-top: 14px;
    text-align: center;
}
#alert-config-modal .ac-rules-advance-text {
    margin: 0;
    font-size: 12px;
    color: #94a3b8;
    line-height: 1.4;
}
#alert-config-modal .ac-rule {
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 8px;
    padding: 12px 14px;
    background: rgba(255, 255, 255, 0.02);
}
/* Editing rule card: no head row (the "adding rule #N" context lives in the
   left step rail) and no bounding box — the fields sit flush in the stage. */
#alert-config-modal .ac-rule.ac-rule--editing {
    border: none;
    background: none;
    padding: 0;
}
#alert-config-modal .ac-row {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 16px;
}
#alert-config-modal .ac-row > label {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.6px;
    color: #94a3b8;
    text-transform: uppercase;
}
#alert-config-modal .ac-required { color: #f87171; }
#alert-config-modal .ac-cond-row {
    flex-direction: row;
    gap: 8px;
    align-items: stretch;
}
/* Sensor / Condition / Value share one row on a content-aware grid:
   Sensor sizes to content, Condition takes the wide 1fr track so long
   labels ("Values +/- at least") fit, Value is a narrow
   fixed track + unit. Stacks to one column at <=640px (below). */
#alert-config-modal .ac-triple-row {
    /* Sensor sits on its own full-width row; Condition + Value sit together on
       the row beneath it (a trailing 1fr column absorbs the leftover space so
       they pack left). align-items:end aligns the label-less Value input with
       the Condition select. */
    display: grid;
    grid-template-columns: auto 100px 1fr;
    gap: 8px;
    align-items: end;
}
/* Buoy (rule 1 only) + Sensor share one row; the sensor fills the remaining
   width so its in-flow group/sensor panel has room. */
#alert-config-modal .ac-bs-row {
    display: flex;
    flex-direction: row;
    gap: 8px;
    align-items: flex-start;
    flex-wrap: wrap;
}
#alert-config-modal .ac-bs-row .ac-sensor {
    flex: 1 1 auto;
    min-width: 0;
}
/* Range / rate-of-change types warrant a wider value track: range fits the
   two bound inputs (Low/High), and rate types pair with longer condition
   labels that read better with extra room. */
#alert-config-modal .ac-triple-row--wide-value {
    grid-template-columns: auto 160px 1fr;
}
#alert-config-modal .ac-triple-row .ac-sensor,
#alert-config-modal .ac-triple-row .ac-cond,
#alert-config-modal .ac-triple-row .ac-val {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
/* Single-column stack on narrow viewports. */
@media (max-width: 640px) {
    #alert-config-modal .ac-triple-row,
    #alert-config-modal .ac-triple-row--wide-value {
        grid-template-columns: 1fr;
    }
}
/* ── Two-level sensor picker (replaces the flat sensor bc-select) ── */
#alert-config-modal .ac-sensor-picker { position: relative; width: 100%; }
#alert-config-modal .ac-sensor-picker.ac-sp--open { z-index: 50; }  /* lift the open picker + its floating panel above sibling rows */
#alert-config-modal .ac-sp-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    height: 34px;
    padding: 0 12px;
    background: var(--acf-bg);
    border: 1px solid var(--acf-border);
    border-radius: var(--acf-radius);
    color: #e2e8f0;
    font-size: 13px;
    cursor: pointer;
    font-family: inherit;
}
#alert-config-modal .ac-sp-trigger::after { content: none; display: none; }
#alert-config-modal .ac-sp-trigger:hover { border-color: var(--acf-accent-border); }
#alert-config-modal .ac-sp--open .ac-sp-trigger {
    border-color: var(--acf-accent-open);
}
#alert-config-modal .ac-sp-crumb {
    display: flex; align-items: center; gap: 6px; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#alert-config-modal .ac-sp-crumb-group { color: #94a3b8; }
#alert-config-modal .ac-sp-crumb-sep { color: #64748b; }
#alert-config-modal .ac-sp-crumb-sensor { color: #e2e8f0; font-weight: 600; }
#alert-config-modal .ac-sp-crumb-placeholder { color: #64748b; }
#alert-config-modal .ac-sp-chevron { color: #64748b; font-size: 11px; flex: 0 0 auto; }

#alert-config-modal .ac-sp-panel {
    /* Floats over the form (like the bc-select dropdowns) rather than pushing
       content down — anchored to the TOP of the trigger so the open list sits
       on top of it; capped, scrolls. */
    position: absolute;
    top: 0;
    left: 0;
    right: auto;
    width: max-content;
    max-width: 100%;
    max-height: 210px;
    overflow-y: auto;
    background: var(--acf-panel-bg);
    border: 1px solid var(--acf-accent-border);
    border-radius: 8px;
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.55);
    z-index: 2100;
    padding: 4px;
}
/* Match the bc-select dropdown scrollbar style. */
#alert-config-modal .ac-sp-panel::-webkit-scrollbar { width: 6px; }
#alert-config-modal .ac-sp-panel::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.15); border-radius: 3px; }
#alert-config-modal .ac-sp-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    padding: 8px 10px;
    background: transparent;
    border: none;
    border-radius: 6px;
    color: #e2e8f0;
    font-size: 13px;
    cursor: pointer;
    font-family: inherit;
    text-align: left;
}
#alert-config-modal .ac-sp-item::after { content: none; display: none; }
#alert-config-modal .ac-sp-item:hover { background: var(--acf-accent-hover); }
#alert-config-modal .ac-sp-item .text {
    display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1 1 auto;
}
#alert-config-modal .ac-sp-group-name { flex: 1 1 auto; }
#alert-config-modal .ac-sp-group-count {
    flex: 0 0 auto;
    font-size: 11px;
    color: #94a3b8;
    background: rgba(255, 255, 255, 0.06);
    border-radius: 10px;
    padding: 1px 8px;
}
#alert-config-modal .ac-sp-group-arrow { color: #64748b; font-size: 11px; flex: 0 0 auto; }
#alert-config-modal .ac-sp-item--selected { background: var(--acf-accent-sel); }
#alert-config-modal .ac-sp-item-meta { color: #64748b; font-size: 11px; margin-left: auto; }
#alert-config-modal .ac-sp-back {
    display: block; width: 100%; text-align: left;
    padding: 6px 10px; margin-bottom: 2px;
    background: transparent; border: none;
    color: #38bdf8; font-size: 12px; font-weight: 600;
    cursor: pointer; font-family: inherit;
}
#alert-config-modal .ac-sp-back::after { content: none; display: none; }
#alert-config-modal .ac-sp-back:hover { color: #7dd3fc; }
#alert-config-modal .ac-duration {
    display: flex;
    gap: 8px;
}
#alert-config-modal .ac-duration .ac-dur-num { flex: 0 0 90px; }
#alert-config-modal .ac-duration .bc-select  { flex: 1 1 auto; }
/* Buoy + sustained selects and the sensor-picker trigger keep their natural
   (content) width instead of stretching to fill the row. */
#alert-config-modal .ac-row > .bc-select[data-acfield="buoy"],
#alert-config-modal .ac-cond .bc-select[data-acfield="condition"] {
    width: fit-content;
    align-self: flex-start;
}
#alert-config-modal .ac-duration .bc-select[data-acfield="sustained-unit"] {
    flex: 0 0 auto;
    width: fit-content;
}
#alert-config-modal .ac-sensor-picker .ac-sp-trigger {
    width: fit-content;
}
#alert-config-modal .ac-rule-footer {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 16px;
}
/* Recent-telemetry hint above the footer — last-known cached values for the
   selected measurement (fleet average, or a single buoy's latest). */
#alert-config-modal .ac-tel-hint {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 14px;
    margin-bottom: 16px;
    padding: 8px 12px;
    background: var(--acf-panel-bg);
    border: 1px solid var(--acf-accent-border);
    border-radius: 8px;
    font-size: 12px;
    color: #cbd5e1;
    /* Greyscale look — desaturates the accent border + measurement icon. */
    filter: grayscale(1);
}
#alert-config-modal .ac-tel-icon { color: var(--acf-accent); flex: 0 0 auto; }
#alert-config-modal .ac-tel-sensor {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-weight: 600;
    color: #e2e8f0;
}
#alert-config-modal .ac-tel-stat { white-space: nowrap; }
#alert-config-modal .ac-tel-k {
    color: #94a3b8;
    font-weight: 600;
    font-size: 10px;
    letter-spacing: 0.3px;
    margin-right: 2px;
}
#alert-config-modal .ac-tel-meta { color: #64748b; margin-left: auto; }
#alert-config-modal .ac-rule-save {
    padding: 6px 14px;
    border: none;
    border-radius: 6px;
    font-size: 12px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    isolation: isolate;
    background: #163880;
    color: #fff;
}
/* When the sensor picker is open it takes the full rule-editor row so the
   2nd-level list has room and reflows in-flow (condition + value drop below);
   keeps the long list from clipping in the narrow Sensor column. */
#alert-config-modal .ac-triple-row:has(.ac-sp--open) .ac-sensor { grid-column: 1 / -1; }
/* Modal Cancel = the same pill design as "+ Add rule" but greyscale. */
#alert-config-modal .ac-btn-cancel.blue-pill-button:not(.ac-armed) { filter: grayscale(1); }
#alert-config-modal .ac-btn-cancel.blue-pill-button:not(.ac-armed):hover { filter: grayscale(0.85); }
/* Delete = the Cancel pill shape, in a red shade (no greyscale filter). */
#alert-config-modal .ac-rule-delete.blue-pill-button {
    background: linear-gradient(to bottom, #ef5350 0%, #c62828 100%);
    border-top-color: #e57373;
    border-right-color: #b71c1c;
    border-bottom-color: #9b1c1c;
    border-left-color: #b71c1c;
    filter: none;
}
#alert-config-modal .ac-rule-delete.blue-pill-button:hover {
    background: linear-gradient(to bottom, #f56b68 0%, #d32f2f 100%);
}
#alert-config-modal .ac-rule--saved {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 12px;
    /* No bounding box — the saved rule reads as plain (clickable) text, not a
       boxed cell — and tight 3px padding instead of the base card's 12px 14px. */
    border: none;
    background: none;
    padding: 3px;
}
#alert-config-modal .ac-rule-summary-text {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13px;
    color: #cbd5e1;
    cursor: pointer;
}
#alert-config-modal .ac-rule-summary-text:focus-visible {
    outline: 2px solid var(--acf-accent);
    outline-offset: 2px;
    border-radius: 3px;
}
/* The rule description reads as an interactive label — dotted underline like the
   buoy measurement names (.buoy-measurement-name). The number badge stays plain. */
#alert-config-modal .ac-rule-summary-link {
    text-decoration: underline dotted rgba(203, 213, 225, 0.5);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    transition: color 0.15s ease, text-decoration-color 0.15s ease;
}
#alert-config-modal .ac-rule-summary-text:hover .ac-rule-summary-link,
#alert-config-modal .ac-rule-summary-text:focus-visible .ac-rule-summary-link {
    color: #ffffff;
    text-decoration-color: #ffffff;
}
/* Numbered badge leading the saved-rule summary — a small blue circle with the
   rule number, in the accent colour (same blue as the telemetry-hint icon). */
#alert-config-modal .ac-rule-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--acf-accent);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    vertical-align: middle;
    margin-right: 8px;
}
#alert-config-modal .ac-edit-guard-msg {
    margin-top: 10px;
    padding: 8px 12px;
    background: rgba(239, 68, 68, 0.1);
    border: 1px solid rgba(239, 68, 68, 0.3);
    color: #fca5a5;
    border-radius: 6px;
    font-size: 12px;
    font-style: italic;
}

/* Reuse the AOI delete morph styling by aliasing onto our class. */
#alert-config-modal .ac-rule-remove .adc-icon-default,
#alert-config-modal .ac-rule-remove .adc-confirm-label {
    transition: opacity 0.2s ease, max-width 0.25s ease;
}
#alert-config-modal .ac-rule-remove .adc-confirm-label {
    display: inline-block;
    overflow: hidden;
    max-width: 0;
    opacity: 0;
    white-space: nowrap;
    vertical-align: middle;
    /* Match the modal-close armed "Sure?" — same 12px / 500 weight as
       .bc-btn-group__btn renders by default. */
    font-size: 12px;
    font-weight: 500;
    line-height: 1;
    text-transform: none;
    letter-spacing: normal;
}
#alert-config-modal .ac-rule-remove.ac-armed {
    background-color: rgba(239, 68, 68, 0.25);
    color: #fecaca;
}
#alert-config-modal .ac-rule-remove.ac-armed .adc-icon-default { display: none; }
#alert-config-modal .ac-rule-remove.ac-armed .adc-confirm-label {
    max-width: 64px;
    opacity: 1;
}

/* Armed state for the footer Cancel button + the modal's X close. Same visual
   language as the rule-delete morph: red wash + inline "Sure?" text. */
#alert-config-modal .ac-btn-cancel.ac-armed,
#alert-config-modal [data-dismiss="alert-config-modal"].ac-armed {
    background-color: rgba(239, 68, 68, 0.25);
    color: #fecaca;
    border-color: rgba(239, 68, 68, 0.5);
}

/* TRIGGER WHEN + COOLDOWN share one row at 50/50. When only cooldown
   qualifies (1 saved rule, no trigger needed) flex-grow lets it spread
   to fill the full width on its own. */
#alert-config-modal .ac-trigger-cooldown-row {
    display: flex;
    gap: 16px;
    align-items: stretch;
}
#alert-config-modal .ac-trigger-cooldown-row > .ac-stage {
    flex: 1 1 50%;
    min-width: 0;
    margin: 0;
}

/* Trigger logic switcher — the same .cwp segmented blue-pill switcher as the
   chart window picker, but full-width with two (taller, wrapping) tabs. */
#ac-trigger-cwp {
    --cwp-tab-h: 40px;
    width: 100%;
    margin: 0;
    padding: 3px;
    border: none;
    border-radius: 9px;
    background: linear-gradient(to bottom, rgba(82, 168, 232, 0.2) 0%, rgba(55, 122, 208, 0.2) 100%);
}
#ac-trigger-cwp .cwp__label,
#ac-trigger-cwp .cwp__tab {
    width: calc(100% / 2);
}
#ac-trigger-cwp .cwp__label {
    font-size: 11px;
    line-height: 1.25;
    padding: 0 8px;
    text-align: center;
}
#ac-trigger-cwp .cwp__indicator {
    background: linear-gradient(to bottom, #52a8e8 0%, #377ad0 100%);
    border: 1px solid #2e69a3;
    border-top-color: #4081af;
    border-bottom-color: #20559a;
    border-radius: 9px;
    box-shadow: inset 0 1px 0 0 #72b9eb, 0 1px 2px 0 rgba(0, 0, 0, 0.3);
    top: 3px;
    width: calc((100% - 6px) / 2);
}
#ac-trigger-cwp .cwp__tab:checked + .cwp__label { text-shadow: 0 -1px 1px #3275bc; }
#ac-trigger-cwp .cwp__tab--1:checked ~ .cwp__indicator { left: 3px; }
#ac-trigger-cwp .cwp__tab--2:checked ~ .cwp__indicator { left: calc(3px + (100% - 6px) / 2); }
/* Narrow screens: the long labels can wrap to 3+ lines — give the tabs room so
   they don't overflow below the indicator pill. */
@media (max-width: 640px) {
    #ac-trigger-cwp { --cwp-tab-h: 58px; }
}
#alert-config-modal .ac-severity-row {
    display: flex;
    gap: 8px;
    flex-wrap: nowrap;
    /* Span the full form-column width (align-self:stretch); 10px top/bottom
       keeps the pills clear of the title above and the body below. */
    align-self: stretch;
    margin: 10px 0;
}
/* Severity pills — every pill shows its --sev tint. At rest the border AND the
   label are a lighter shade of the pill's own --sev colour; on hover or when
   selected both take the full --sev (hover mirrors the selected state). */
#alert-config-modal .ac-sev-pill {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 7px 4px;
    border: 1px solid color-mix(in srgb, var(--sev) 55%, #ffffff);
    border-radius: 9px;
    background: color-mix(in srgb, var(--sev) 16%, transparent);
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
}
#alert-config-modal .ac-sev-pill::after { content: none; display: none; }
#alert-config-modal .ac-sev-label {
    color: color-mix(in srgb, var(--sev) 55%, #ffffff);
    font-size: 11px;
    font-weight: 700;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    transition: color 0.15s;
    /* Stay above the global button::after Uiverse overlay. */
    position: relative;
    z-index: 2;
}
#alert-config-modal .ac-sev-pill:hover,
#alert-config-modal .ac-sev-pill.active {
    border-color: var(--sev);
}
#alert-config-modal .ac-sev-pill:hover .ac-sev-label,
#alert-config-modal .ac-sev-pill.active .ac-sev-label {
    color: var(--sev);
}
/* Severe's --sev (#b91c1c) is too dark to read as label text on the dark
   panel, so its selected/hover label uses a lighter red than the border. */
#alert-config-modal .ac-sev-pill[data-sev="severe"]:hover .ac-sev-label,
#alert-config-modal .ac-sev-pill[data-sev="severe"].active .ac-sev-label {
    color: #f87171;
}
/* Severity options panel — left-aligned title + body around the pills. */
#alert-config-modal .ac-sev-hint {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    text-align: left;
    gap: 8px;
    padding: 0 0 4px;
}
#alert-config-modal .ac-sev-hint-title {
    font-size: 13px;
    font-weight: 600;
    color: #e2e8f0;
}
#alert-config-modal .ac-sev-hint-sub {
    font-size: 13px;
    color: #94a3b8;
    max-width: 360px;
    line-height: 1.5;
}
#alert-config-modal .ac-notifications {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}
#alert-config-modal .ac-notif-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    background: var(--acf-accent-hover);
    border: 1px solid var(--acf-accent-border);
    color: #bae6fd;
    border-radius: 999px;
    font-size: 11px;
}
/* The "self" recipient pill (the signed-in user's own email) uses the same blue
   palette as every other recipient pill — no separate green treatment. */
#alert-config-modal .ac-notif-pill-x {
    cursor: pointer;
    color: #cbd5e1;
    font-size: 14px;
    line-height: 1;
}
/* The "Add recipient" button reuses the standard blue-pill look (it carries
   the .blue-pill-button class); only flex sizing is button-specific here. */
#alert-config-modal .ac-add-recipient {
    flex: 0 0 auto;
}
#alert-config-modal .ac-add-recipient-row {
    flex: 1 1 100%;
    display: flex;
    gap: 6px;
    align-items: center;
    margin-top: 4px;
}
#alert-config-modal .ac-add-recipient-row .ac-add-recipient-input {
    flex: 1 1 auto;
    font-size: 12px;
}
#alert-config-modal .ac-add-recipient-input.ac-input-invalid {
    border-color: rgba(239, 68, 68, 0.6);
    box-shadow: 0 0 0 1px rgba(239, 68, 68, 0.4);
}
#alert-config-modal .ac-add-recipient-error {
    flex: 1 1 100%;
    margin-top: 4px;
    color: #fca5a5;
    font-size: 11px;
    line-height: 1.4;
}
#alert-config-modal .ac-advanced > summary {
    cursor: pointer;
    font-size: 12px;
    color: #94a3b8;
    padding: 6px 0;
    list-style: none;
}
#alert-config-modal .ac-advanced > summary::before {
    content: "▸ ";
    color: #64748b;
}
#alert-config-modal .ac-advanced[open] > summary::before { content: "▾ "; }
#alert-config-modal .ac-btn-cancel,
#alert-config-modal .ac-btn-save {
    padding: 8px 16px;
    border: none;
    border-radius: 6px;
    font-size: 13px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    isolation: isolate;
}
#alert-config-modal .ac-btn-cancel {
    background: #3f3f46;
    color: #e5e5e5;
}
#alert-config-modal .ac-btn-save {
    background: #163880;
    color: #fff;
}
#alert-config-modal .ac-btn-save[disabled] {
    opacity: 0.4;
    cursor: not-allowed;
}
#alert-config-modal .ac-range-inputs {
    display: flex;
    gap: 6px;
}
#alert-config-modal .ac-range-inputs .ac-rule-number {
    flex: 1 1 0;
    min-width: 0;
}

/* ── Multi-client account picker (login-time interrupt) ──────────
   Full-screen blocking overlay shown when the logged-in user's email maps
   to >1 client account. Sits above the loading overlay; resolves the init
   chain only when a card is clicked. Reused by the "Switch account" action. */
.client-picker-overlay {
    position: fixed;
    inset: 0;
    z-index: 100000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    /* Same backdrop as the loading screen (#loading-overlay) so the account
       picker feels like a continuation of it. The dark glass panel + cards
       keep everything legible over the image, so no scrim/blur needed. */
    background: #080c18 url('/static/back.jpg') center / cover no-repeat;
    animation: client-picker-fade 0.25s ease;
}
.client-picker-overlay.is-dismissing { opacity: 0; transition: opacity 0.18s ease; }
@keyframes client-picker-fade { from { opacity: 0; } to { opacity: 1; } }

.client-picker-panel {
    width: 100%;
    max-width: 560px;
    padding: 28px;
    border-radius: 16px;
}
.client-picker-head { text-align: center; margin-bottom: 22px; }
.client-picker-icon { font-size: 28px; color: #60a5fa; margin-bottom: 12px; }
.client-picker-title {
    font-size: 20px;
    font-weight: 600;
    color: #e2e8f0;
    margin: 0 0 6px;
    letter-spacing: 0.3px;
}
.client-picker-sub { font-size: 13px; color: #94a3b8; margin: 0; line-height: 1.5; }

.client-picker-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 12px;
}
/* Cards are <button>s — just the client name in a thin-bordered container
   that subtly tints its background + border on hover (kill the inherited
   Uiverse skew-wave). No logo. */
.client-picker-card {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 14px 16px;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 8px;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
}
.client-picker-card::after { display: none; }  /* kill the inherited skew-wave */
.client-picker-card:hover {
    background: rgba(96, 165, 250, 0.08);
    border-color: rgba(96, 165, 250, 0.45);
}
.client-picker-card:hover .client-picker-name { color: #ffffff; }
.client-picker-name {
    font-size: 16px;
    font-weight: 600;
    color: #e2e8f0;
    text-align: center;
    line-height: 1.3;
    transition: color 0.15s;
}

/* Switch-account dropdown — same visual language as the card (transparent
   fill, thin border, blue hover tint) but as a native <select>. Selecting an
   option enters that account immediately. */
.client-picker-switch { margin-top: 16px; }
.client-picker-switch-label {
    display: block;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: #94a3b8;
    text-align: center;
    margin-bottom: 8px;
}
.client-picker-select-wrap { position: relative; }
.client-picker-select {
    width: 100%;
    appearance: none;
    -webkit-appearance: none;
    padding: 14px 40px 14px 16px;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 8px;
    color: #e2e8f0;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
}
.client-picker-select:hover,
.client-picker-select:focus {
    background: rgba(96, 165, 250, 0.08);
    border-color: rgba(96, 165, 250, 0.45);
    outline: none;
}
.client-picker-select-chevron {
    position: absolute;
    right: 16px;
    top: 50%;
    transform: translateY(-50%);
    color: #64748b;
    font-size: 12px;
    pointer-events: none;
}
/* Dark option list so the native menu isn't white-on-white where the OS
   honours option colours. */
.client-picker-select option { background: #0f1626; color: #e2e8f0; }

/* "Log out" escape hatch under the account list — plain text link. */
.client-picker-logout {
    display: block;
    margin: 20px auto 0;
    padding: 6px 10px;
    background: transparent;
    border: none;
    color: #94a3b8;
    font-size: 13px;
    cursor: pointer;
    transition: color 0.15s;
}
.client-picker-logout::after { display: none; }  /* kill the inherited skew-wave */
.client-picker-logout:hover { color: #e2e8f0; text-decoration: underline; }

/* ── Logout / Switch-account menu (multi-client) ─────────────────
   Small popover anchored to the user-bar logout icon, shown only when the
   user has >1 client account. Single-client users get a direct confirm. */
.user-logout-menu {
    position: fixed;
    z-index: 100001;
    min-width: 180px;
    padding: 6px;
    background: rgb(13 13 13 / 92%);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
    animation: client-picker-fade 0.15s ease;
}
.user-logout-menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 12px;
    background: transparent;
    border: none;
    border-radius: 6px;
    color: #cbd5e1;
    font-size: 13px;
    text-align: left;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}
.user-logout-menu-item::after { display: none; }  /* no inherited skew-wave */
.user-logout-menu-item:hover { background: rgba(96, 165, 250, 0.14); color: #fff; }
.user-logout-menu-item i { width: 16px; text-align: center; color: #60a5fa; }

/* Shared (read-only) AOI popup — "Shared by NAME" badge + non-clickable name */
/* Specificity (0,4,0) beats the base .aoi-popup .aoi-item > .dt-panel-action-link
   rule (0,3,0) and its :hover variant (0,3,1), suppressing cursor:pointer,
   the dotted underline affordance, and the hover colour change. */
.aoi-popup .aoi-item > .dt-panel-action-link.aoi-shared-name {
    cursor: default;
    text-decoration: none;
    color: #bfdbfe;
}
/* AOI Share modal — contact picker */
.aoi-share-intro {
    font-size: 13px;
    color: #cbd5e1;
    margin: 4px 0 12px;
    line-height: 1.4;
}
.aoi-share-area-name {
    font-weight: 700;
    color: #fff;
}
.aoi-share-contacts {
    max-height: 320px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.aoi-share-loading {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: #94a3b8;
    padding: 8px 2px;
}
.aoi-share-empty {
    font-size: 13px;
    color: #94a3b8;
    padding: 8px 2px;
}
.aoi-share-contact {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 8px;
    cursor: pointer;
    border: 1px solid rgba(148, 163, 184, 0.18);
    background: rgba(148, 163, 184, 0.06);
}
.aoi-share-contact:hover {
    background: rgba(148, 163, 184, 0.14);
}
.aoi-share-contact input[type="checkbox"] {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    accent-color: #3b82f6;
}
.aoi-share-contact-text {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
    min-width: 0;
}
.aoi-share-contact-name {
    font-size: 13px;
    font-weight: 600;
    color: #f1f5f9;
}
.aoi-share-contact-meta {
    font-size: 11px;
    color: #94a3b8;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── Maintenance Tab (PIECE B) ──────────────────────────────── */
/* Resting = the .buoy-measurement-tile treatment (no border, near-invisible
   wash); hover keeps the highlight but never gains a border. */
.maint-card { border: none; border-radius: 8px; padding: 6px 7px; margin-bottom: 5px; background: rgba(255, 255, 255, 0.04); }
.maint-card--clickable { cursor: pointer; transition: background 140ms ease; }
.maint-card--clickable:hover { background: rgba(30, 41, 59, .55); }
/* Up-to-date / done cards stay listed (early completions, planning,
   instructions) but sit quieter; full strength on hover. */
.maint-card--quiet { opacity: .7; transition: opacity 140ms ease, background 140ms ease; }
.maint-card--quiet:hover { opacity: 1; }
.maint-card-head { display: flex; align-items: center; gap: 5px; }
.maint-card-chevron { font-size: 11px; color: #64748b; margin-left: auto; flex-shrink: 0; }
.maint-name { font-weight: 600; flex: 1; }
/* Card name matches the alert-card name (.alarm-card--v2 .alarm-card-name:
   13px / 600 / #f1f5f9). Scoped to cards — the modal headers keep the blue
   .maint-proc-title treatment. */
.maint-card .maint-name {
    font-size: 13px;
    font-weight: 600;
    color: #f1f5f9;
}
.maint-badge { font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 999px; text-transform: uppercase; letter-spacing: .4px; }
.maint-overdue { background: rgba(239, 68, 68, .18); color: #fca5a5; }
.maint-due_soon { background: rgba(245, 158, 11, .18); color: #fcd34d; }
.maint-ok { background: rgba(34, 197, 94, .16); color: #86efac; }
.maint-done { background: rgba(59, 130, 246, .16); color: #93c5fd; }
.maint-meta { font-size: 12px; color: #94a3b8; margin-top: 4px; }
/* Halved spacing inside the tab-list cards only — the modal's .maint-meta keeps 4px. */
.maint-card .maint-meta { margin-top: 2px; }
.maint-detail { margin-top: 10px; padding-top: 10px; border-top: 1px solid rgba(148, 163, 184, .15); font-size: 13px; line-height: 1.5; }
.maint-detail img.maint-img { max-width: 100%; border-radius: 6px; margin: 6px 0; display: block; }
.maint-history { font-size: 12px; color: #cbd5e1; }

/* "Last done" date link (cards + procedure modal) — same dotted-underline
   affordance as chartable status/measurement labels. */
.maint-last-done-link {
    cursor: pointer;
    text-decoration: underline dotted rgba(191, 219, 254, 0.55);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    transition: color 0.15s ease, text-decoration-color 0.15s ease;
}
.maint-last-done-link:hover {
    color: #bfdbfe;
    text-decoration-color: #bfdbfe;
}

/* Completion-history modal — stacks above the buoy modal (2000) and the
   procedure modal (2600). */
.maint-history-modal {
    z-index: 2650;
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

/* Completion-history table: Date | Notes | Completed by | ✕ */
.maint-history-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.maint-history-table th {
    text-align: left;
    font-size: 11px;
    font-weight: 600;
    color: #94a3b8;
    text-transform: uppercase;
    letter-spacing: .4px;
    padding: 4px 8px 6px 0;
    border-bottom: 1px solid rgba(148, 163, 184, .25);
}
.maint-history-table td {
    padding: 6px 8px 6px 0;
    border-bottom: 1px solid rgba(148, 163, 184, .12);
    vertical-align: top;
    color: #cbd5e1;
}
.maint-hist-who { color: #94a3b8; }
.maint-hist-none { color: #475569; }
.maint-hist-del { width: 1%; white-space: nowrap; text-align: right; }
.maint-empty { color: #94a3b8; font-size: 13px; padding: 16px; text-align: center; }

/* ── Maintenance Procedure Modal (stacked above buoy modal) ─── */
/* z-index 2600: above .modal-overlay (2000), below AI side panel (3000) */
.maint-proc-modal {
    z-index: 2600;
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}
.maint-proc-dialog {
    position: relative;
    width: 100%;
    max-width: 560px;
    max-height: 85vh;
    overflow-y: auto;
    padding: 20px 22px;
    box-sizing: border-box;
    border-radius: 12px;
    font-size: 13px;
    line-height: 1.5;
    color: #e2e8f0;
}
.maint-proc-header {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin-bottom: 10px;
}
.maint-proc-title {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1;
    flex-wrap: wrap;
}
/* Procedure name in the modal header — same treatment as the buoy modal's
   .modal-title (h5#buoy-modal-title). */
.maint-proc-title .maint-name {
    font-weight: 600;
    letter-spacing: 1.5px;
    color: #60a5fa;
    font-size: 1rem;
}
.maint-proc-close {
    background: none;
    border: none;
    color: #94a3b8;
    font-size: 16px;
    cursor: pointer;
    padding: 0 2px;
    line-height: 1;
    flex-shrink: 0;
    transition: color 120ms;
}
.maint-proc-close:hover { color: #e2e8f0; }
.maint-proc-loading { color: #94a3b8; text-align: center; padding: 24px 0; }
.maint-proc-dialog img.maint-img { max-width: 100%; border-radius: 6px; margin: 6px 0; display: block; }

/* ── Maintenance instructions images (rendered inside .maint-instructions) ─ */
.maint-instructions .maint-images {
    margin-top: 10px;
}

/* ── Maintenance original-view action row (reveal + close-cancel) ── */
.maint-reveal-row {
    margin-top: 18px;
}

/* ── Maintenance "Record completion" form ────────────────────── */
.maint-perform-form {
    margin-top: 20px;
    padding-top: 18px;
    border-top: 1px solid rgba(148, 163, 184, .18);
}
.maint-perform-heading {
    font-weight: 600;
    margin-bottom: 14px;
}
.maint-field {
    margin-bottom: 12px;
}
.maint-field label {
    display: block;
    font-size: 12px;
    color: #94a3b8;
    margin-bottom: 5px;
}
.maint-field-optional {
    font-weight: 400;
    opacity: 0.7;
}
.maint-field input[type="date"],
.maint-field textarea {
    width: 100%;
    box-sizing: border-box;
    background: rgba(15, 23, 42, .6);
    border: 1px solid rgba(148, 163, 184, .3);
    border-radius: 6px;
    color: #e2e8f0;
    padding: 7px 10px;
    font-size: 13px;
    font-family: inherit;
    transition: border-color 140ms ease;
}
.maint-field input[type="date"] {
    width: auto;
    min-width: 150px;
    color-scheme: dark;
}
.maint-field input[type="date"]:focus,
.maint-field textarea:focus {
    outline: none;
    border-color: rgba(148, 163, 184, .65);
}
.maint-field textarea {
    resize: vertical;
    min-height: 64px;
}
.maint-perform-form .blue-pill-button {
    display: block;
    width: 100%;
    margin-top: 6px;
}

/* ── Maintenance history delete button ─────────────────────────
   Mirror of the alarm-card delete pill (.alarm-card-actions .alarm-delete):
   red gradient pill + two-step "Sure?" crossfade. Own class (NOT
   .alarm-delete) so the alarm handler's DELETE /alerts/{id} never fires. */
.maint-del-completion-modal.blue-pill-button {
    background: linear-gradient(to bottom, #ef5350 0%, #c62828 100%);
    border-top-color: #e57373;
    border-right-color: #b71c1c;
    border-bottom-color: #9b1c1c;
    border-left-color: #b71c1c;
}
.maint-del-completion-modal.blue-pill-button:hover {
    background: linear-gradient(to bottom, #f56b68 0%, #d32f2f 100%);
}
/* Armed ("Sure?") — brighten the red so the confirm state reads. */
.maint-del-completion-modal.blue-pill-button.maint-del-confirm {
    background: linear-gradient(to bottom, #ff7b78 0%, #e53935 100%);
}
.maint-del-completion-modal .text {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.maint-del-completion-modal .adc-icon-default,
.maint-del-completion-modal .adc-confirm-label {
    transition: opacity 0.2s ease, max-width 0.25s ease;
}
.maint-del-completion-modal .adc-confirm-label {
    overflow: hidden;
    max-width: 0;
    opacity: 0;
    white-space: nowrap;
    font-size: 11px;
    letter-spacing: 0.4px;
}
.maint-del-completion-modal.maint-del-confirm .adc-icon-default { display: none; }
.maint-del-completion-modal.maint-del-confirm .adc-confirm-label {
    max-width: 64px;
    opacity: 1;
}

/* ── Maintenance completion action row (submit / confirm / cancel) ── */
.maint-action-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 8px;
}
.maint-action-row .blue-pill-button {
    flex: 1;
}
/* Cancel = the blue pill in greyscale (same treatment as the alert-config
   Cancel and the alarm mute/unmute pills). */
.maint-cancel-complete.blue-pill-button,
.maint-cancel-close.blue-pill-button {
    filter: grayscale(1);
}
.maint-cancel-complete.blue-pill-button:hover,
.maint-cancel-close.blue-pill-button:hover {
    filter: grayscale(0.85);
}

/* ── Alerts Panel — maintenance trigger card (PIECE C) ───────── */
.atc-procedure { margin: 6px 0; padding: 8px 10px; background: rgba(168, 85, 247, .10); border-left: 2px solid #a855f7; border-radius: 3px; }
.atc-procedure-label { font-size: 12px; color: #c4b5fd; display: flex; align-items: center; gap: 6px; }
