/* Dashboards — block-builder surface. Chrome uses the platform design tokens (same look as every
   other webviz page: neutral surfaces, blue accent, shared page-head / button.primary / icon-btn /
   seg-toggle), so light/dark themes come for free. Scoped under #dashboards-host. */

#dashboards-host {
  background: var(--bg-app);
  overflow: auto;
}

.dashroot { min-height: 100%; }

/* section header band — same padding rhythm as #toolbar on the other pages */
.dashroot .dh-header { padding: var(--page-pad-y) var(--page-pad-x) var(--page-head-gap); }
.dashroot .page-actions { gap: 8px; }
.dashroot .page-actions .primary { display: inline-flex; align-items: center; gap: 7px; }
.dashroot .page-actions .primary .lucide { width: 15px; height: 15px; }

/* Maps-style builder toolbar: a floating glass bar (same recipe as .map-toolbar) with pill buttons.
   Default View; the Edit toggle unlocks editing and reveals the "Add block" dropdown. Title centred. */
.dashroot .dash-toolbar { display: flex; align-items: center; gap: 6px;
  margin: 10px var(--page-pad-x) 0; padding: 0 6px; height: 44px;
  background: var(--map-glass-bg); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border: 1px solid var(--map-glass-border); border-radius: var(--r-lg); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.18);
  position: sticky; top: 10px; z-index: 6; }
.dashroot .dash-tb-btn { display: inline-flex; align-items: center; gap: 6px; flex: none; height: 32px;
  padding: 0 12px; border: 0; border-radius: var(--r-pill); background: transparent; color: var(--map-glass-fg);
  font: inherit; font-size: var(--fs-sm); cursor: pointer; white-space: nowrap; transition: background .15s; }
.dashroot .dash-tb-btn:hover { background: var(--icon-btn-hover); }
.dashroot .dash-tb-btn .lucide { width: 15px; height: 15px; flex: none; }
.dashroot .dash-tb-btn[aria-expanded="true"] { background: var(--icon-btn-hover); }
/* active = editing: blue with WHITE text (matches the map's accent buttons) */
.dashroot .dash-tb-btn.active { background: var(--accent); color: #fff; }
.dashroot .dash-tb-btn.active:hover { background: var(--accent-hover); }
.dashroot .dash-tb-title { flex: 1 1 auto; min-width: 0; text-align: center; padding: 0 10px;
  font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--map-glass-fg);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Save (primary): force white text on the blue, including dark theme where --accent-fg is dark ink. */
.dashroot .dash-tb-save.primary { color: #fff; display: inline-flex; align-items: center; gap: 6px; height: 32px; }
.dashroot .dash-tb-save .lucide { width: 15px; height: 15px; }
/* Web-shared badge: a pill (success-tinted) that copies the public link on click. */
.dashroot .dash-tb-badge { display: inline-flex; align-items: center; gap: 6px; flex: none; height: 28px;
  padding: 0 11px; border: 1px solid var(--success); border-radius: var(--r-pill); background: transparent;
  color: var(--success); font: inherit; font-size: var(--fs-sm); cursor: pointer; white-space: nowrap; }
.dashroot .dash-tb-badge:hover { background: var(--success); color: #fff; }
.dashroot .dash-tb-badge .lucide { width: 14px; height: 14px; }
/* Editor-only controls (Add block, Save) hide in View mode. */
.dashroot:not(.is-edit) .dash-tb-edit { display: none; }

/* grid + block shell */
.dashroot .dh-canvas { padding: var(--space-1) var(--space-5) var(--space-7); }
.dashroot .grid-stack-item-content { inset: 0; overflow: visible !important; }
/* Fit-to-content blocks: let the content flow at its natural height so GridStack's sizeToContent measures
   the real content, not the cell (our default inset:0 + .blk height:100% otherwise report the cell or 0,
   collapsing the block / leaving the tools above a short card). */
.dashroot .grid-stack-item-content:has(> .blk.dh-fit) { position: relative; inset: auto; height: auto; }
.dashroot .blk.dh-fit { height: auto; }
.dashroot .blk { background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--r-lg);
  box-shadow: var(--shadow-dropdown); height: 100%; overflow: hidden; position: relative; display: flex; flex-direction: column; }
.dashroot .blk.bare { background: transparent; border: none; box-shadow: none; }
.dashroot.is-edit .blk:not(.bare) { outline: 1.5px dashed var(--border-input); outline-offset: -2px; }
/* Aligned with the card-head control row (top/height match the head's padding + control height). */
.dashroot .blk-tools { position: absolute; top: 16px; right: 16px; display: none; gap: 4px; z-index: 5; }
/* Bare blocks (KPI / text) have no head row to align with — sit the tools nearer the top edge. */
.dashroot .blk.bare .blk-tools { top: 9px; }
.dashroot.is-edit .blk-tools { display: flex; }
.dashroot .blk-tools button { width: var(--control-h); height: var(--control-h); border-radius: var(--r-xs); border: 1px solid var(--border-input);
  background: var(--surface); display: grid; place-items: center; color: var(--text-secondary); cursor: pointer; }
/* Solid hover bg (not translucent): the tools float over the map, so a see-through plate would let
   the light map show and a light-theme/dark-theme icon could vanish. Solid surface keeps contrast. */
.dashroot .blk-tools button:hover { background: var(--surface-hover); color: var(--text-strong); border-color: var(--border); }
.dashroot .blk-tools button.danger:hover { background: var(--surface-hover); color: var(--danger-icon-hover-fg); border-color: var(--danger-border); }
.dashroot .blk-tools .lucide { width: 15px; height: 15px; }
/* edit mode: whole block is the drag handle (grab); interactive zones keep their cursor */
.dashroot.is-edit .blk { cursor: grab; }
.dashroot.is-edit .blk:active { cursor: grabbing; }
.dashroot.is-edit .blk button { cursor: default; }
.dashroot.is-edit .blk-head { padding-right: 92px; }  /* clear the two 30px tool buttons at right:16 */

/* KPI */
/* grid-auto-rows:1fr → each KPI card fills the block height, so the block's tools sit ON the card (no
   stranded gap above a centred card) and same-row cards stay even. The card's own flex (align-items)
   centres the badge + value vertically. This makes KPI always card-filling (supersedes per-block valign). */
.dashroot .dh-kpirow { display: grid; grid-template-columns: repeat(5, 1fr); grid-auto-rows: 1fr; gap: var(--space-4); height: 100%; }
.dashroot .dh-kpi { background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--r-lg);
  padding: var(--space-4) var(--space-5); display: flex; align-items: center; gap: var(--space-4); box-shadow: var(--shadow-dropdown); }
.dashroot .dh-kpi .badge { width: 42px; height: 42px; border-radius: var(--r-pill); background: var(--accent-soft-bg);
  display: grid; place-items: center; color: var(--accent); flex: none; }
.dashroot .dh-kpi .label { font-size: var(--fs-sm); color: var(--text-secondary); font-weight: var(--fw-medium); margin-bottom: 3px; }
.dashroot .dh-kpi .val { font-weight: var(--fw-bold); font-size: 25px; color: var(--text-strong); line-height: 1.05; letter-spacing: -.4px; }
.dashroot .dh-kpi .val .u { font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--text-secondary); margin-left: 3px; }
.dashroot .dh-kpi .delta { display: flex; align-items: center; gap: 4px; font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--success); margin-top: 6px; }
.dashroot .dh-kpi .delta .lucide { width: 14px; height: 14px; }
.dashroot .dh-kpi .sub { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 3px; }

/* Breakdown: one tile per area, wrapping to new rows when they don't fit on one line. */
.dashroot .dh-kpi-break {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  grid-template-columns: none;
}
.dashroot .dh-kpi-break .dh-kpi-tile {
  flex: 1 1 120px;
  min-width: 110px;
  padding: var(--space-3) var(--space-4);
  box-shadow: none;
}
.dashroot .dh-kpi-tile .label { margin-bottom: 2px; }
.dashroot .dh-kpi-tile .val { font-size: 20px; }

/* card head (map + table) */
.dashroot .blk-head { position: relative; display: flex; align-items: center; gap: var(--space-4); padding: var(--space-4) var(--space-4) var(--space-3); }
/* Tools live inside the head for blocks that have one → vertically centre them against the head row
   (any height, incl. title+date) instead of pinning to a fixed top. */
.dashroot .blk-head > .blk-tools { top: 50%; transform: translateY(-50%); }
.dashroot .blk-head .titles { display: flex; flex-direction: column; gap: 2px; }
.dashroot .blk-head h3 { margin: 0; font-size: var(--fs-lg); font-weight: var(--fw-semibold); color: var(--text-strong); }
.dashroot .blk-head .date { font-size: var(--fs-xs); color: var(--text-muted); }
.dashroot .blk-head .dh-spacer { flex: 1; }
.dashroot .dh-field { display: flex; align-items: center; gap: 8px; font-size: var(--fs-sm); color: var(--text-secondary); }
.dashroot .dh-select { display: inline-flex; align-items: center; gap: 8px; height: var(--control-h); background: var(--surface); border: 1px solid var(--border-input);
  border-radius: var(--r-sm); padding: 0 10px; font-size: var(--fs-sm); color: var(--text); cursor: pointer; }
.dashroot .dh-select .lucide { width: 15px; height: 15px; color: var(--text-muted); }
.dashroot .dh-outline { display: inline-flex; align-items: center; gap: 7px; height: var(--control-h); background: var(--surface); border: 1px solid var(--border-input);
  border-radius: var(--r-sm); padding: 0 12px; font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--text-secondary); cursor: pointer; }
.dashroot .dh-outline .lucide { width: 15px; height: 15px; }

/* cache-warming overlay — shown on a block after save while its figures populate the server cache */
.dashroot .grid-stack-item-content.dh-warming { pointer-events: none; }
.dashroot .grid-stack-item-content.dh-warming > :not(.dh-warm) { opacity: 0.45; filter: grayscale(0.2); }
.dashroot .dh-warm { position: absolute; inset: 0; z-index: 6; display: flex; align-items: center; justify-content: center;
  background: var(--overlay-modal, rgba(0, 0, 0, 0.18)); }
.dashroot .dh-warm-card { display: flex; flex-direction: column; align-items: center; gap: 9px; padding: 16px 20px;
  min-width: 168px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md); box-shadow: var(--shadow-dropdown); }
.dashroot .dh-warm-spin { width: 22px; height: 22px; border-radius: 50%; border: 2.5px solid var(--surface-sunken);
  border-top-color: var(--accent); animation: dh-warm-spin 0.8s linear infinite; }
@keyframes dh-warm-spin { to { transform: rotate(360deg); } }
.dashroot .dh-warm-txt { font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--text-strong); }
.dashroot .dh-warm-bar { width: 150px; height: 6px; border-radius: 999px; background: var(--surface-sunken); overflow: hidden; }
.dashroot .dh-warm-fill { height: 100%; width: 0; border-radius: 999px; background: var(--accent); transition: width 0.2s ease; }
.dashroot .dh-warm-count { font-size: var(--fs-xs); color: var(--text-secondary); font-variant-numeric: tabular-nums; }

/* map overlays (over the map image): solid plaques on platform tokens */
.dashroot .dh-map { position: relative; flex: 1; min-height: 0; }
.dashroot .dh-map-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dashroot .dh-legend { position: absolute; left: 14px; top: 14px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: var(--shadow-dropdown); padding: 10px 12px; }
.dashroot .dh-legend .lh { display: flex; align-items: center; gap: 6px; font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--text-strong); margin-bottom: 8px; }
.dashroot .dh-legend .lh .lucide { width: 13px; height: 13px; color: var(--text-muted); }
.dashroot .dh-legend .row { display: flex; align-items: center; gap: 9px; font-size: var(--fs-xs); color: var(--text-secondary); padding: 2px 0; }
.dashroot .dh-legend .sw { width: 14px; height: 14px; border-radius: 4px; }
/* One joined zoom control: border/bg/radius on the container, buttons borderless with a divider. */
.dashroot .dh-zoom { position: absolute; right: 14px; top: 14px; display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden; box-shadow: var(--shadow-dropdown); }
.dashroot .dh-zoom button { width: 32px; height: 32px; background: transparent; border: none;
  border-bottom: 1px solid var(--border); display: grid; place-items: center; color: var(--text-secondary); cursor: pointer; }
.dashroot .dh-zoom button:last-child { border-bottom: none; }
.dashroot .dh-slider { position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%); display: flex; align-items: center;
  gap: 4px; max-width: calc(100% - 28px); background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-pill); box-shadow: var(--shadow-dropdown); padding: 5px 8px; }
/* The chips scroll horizontally when there are too many dates to fit; ◀ play ▶ stay pinned outside it. */
.dashroot .dh-slider-steps { display: flex; align-items: center; gap: 4px; min-width: 0; overflow-x: auto; scroll-behavior: smooth; scrollbar-width: thin; }
.dashroot .dh-slider-steps::-webkit-scrollbar { height: 6px; }
.dashroot .dh-slider .nav { flex: none; width: 26px; height: 26px; border: none; background: transparent; display: grid; place-items: center;
  color: var(--text-secondary); cursor: pointer; border-radius: var(--r-pill); }
.dashroot .dh-slider .play { background: transparent; color: var(--accent); }
.dashroot .dh-slider .step { font-size: var(--fs-sm); color: var(--text-secondary); padding: 5px 11px; border-radius: var(--r-pill); cursor: pointer; font-weight: var(--fw-medium); }
.dashroot .dh-slider .step.active { background: var(--accent); color: var(--accent-fg); font-weight: var(--fw-semibold); }
.dashroot .dh-slider .nav.play.active { background: var(--accent-soft-bg); color: var(--accent); }
.dashroot .dh-slider .step { flex: none; max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Live MapLibre choropleth (dashmap.js): the GL canvas fills .dh-map; the legend / slider above reuse
   the styles already defined and sit over it. */
.dashroot .dh-map-live { position: absolute; inset: 0; }
.dashroot .dh-map-canvas { position: absolute; inset: 0; }
.dashroot .dh-map-canvas .maplibregl-map { width: 100%; height: 100%; }
.dashroot .dh-map-live .dh-legend, .dashroot .dh-map-live .dh-slider { z-index: 2; }
/* Layer switcher — a control on the right of the block header (multi-layer maps). */
.dashroot .dh-map-switch { max-width: 200px; height: var(--control-h, 30px); padding: 0 10px; background: var(--surface);
  color: var(--text-strong); border: 1px solid var(--border-input, var(--border)); border-radius: var(--r-sm);
  font-size: var(--fs-sm); cursor: pointer; }
.dashroot .blk-head .dh-map-switch-slot { display: flex; align-items: center; }
/* Fallback when a map has no header (title hidden + somehow no slot): float it over the map, top-right. */
.dashroot .dh-map-live .dh-map-switch-float { position: absolute; top: 12px; right: 12px; z-index: 3; box-shadow: var(--shadow-dropdown); }
/* In edit the block tools sit at top-right (16/16) — push the map's zoom/fullscreen controls clear. */
.dashroot.is-edit .dh-map-live .maplibregl-ctrl-top-right { margin-top: 40px; }
.dashroot .dh-map-msg { position: absolute; inset: 0; display: grid; place-items: center; padding: 16px;
  text-align: center; color: var(--text-muted); font-size: var(--fs-sm); }
/* Region name + value labels, placed at each polygon's centroid (few regions only). */
.dashroot .dh-map-lbl { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 2px 6px; font-size: 10px; line-height: 1.25; color: var(--text-secondary); text-align: center;
  box-shadow: var(--shadow-sm); pointer-events: none; white-space: nowrap; }
.dashroot .dh-map-lbl b { display: block; color: var(--text-strong); font-size: 11px; }
/* Themed hover popup (dense levels only), matching the region labels rather than MapLibre's white box. */
.maplibregl-popup.dh-map-pop .maplibregl-popup-content {
  padding: 6px 9px; border-radius: var(--r-sm); font-size: var(--fs-xs);
  background: var(--surface); color: var(--text-secondary); border: 1px solid var(--border); box-shadow: var(--shadow-sm);
}
.maplibregl-popup.dh-map-pop .maplibregl-popup-content strong { color: var(--text-strong); }
.maplibregl-popup.dh-map-pop .maplibregl-popup-tip { display: none; }
/* The popup follows the cursor, so it must not intercept the mouse — otherwise hovering it fires the
   fill layer's mouseleave and the popup flickers (hide → reappear a little higher). */
.maplibregl-popup.dh-map-pop { pointer-events: none; }
/* Raster ("as-is") layer legend: a continuous ramp bar + its data-range ends. */
.dashroot .dh-legend .dh-legend-bar { width: 130px; height: 12px; border-radius: 3px; margin-top: 2px; }
.dashroot .dh-legend .dh-legend-ends { display: flex; justify-content: space-between; gap: 12px; font-size: var(--fs-xs); color: var(--text-secondary); margin-top: 4px; }

/* table */
.dashroot .dh-tbl-scroll { flex: 1; overflow: auto; }
.dashroot table { width: 100%; border-collapse: collapse; font-size: var(--fs-base); }
.dashroot thead th { text-align: left; font-weight: var(--fw-semibold); color: var(--head-fg); font-size: var(--fs-sm); padding: 10px var(--space-4);
  border-bottom: 1px solid var(--divider); white-space: nowrap; position: sticky; top: 0; background: var(--head-bg); }
.dashroot tbody td { padding: 12px var(--space-4); border-bottom: 1px solid var(--divider); color: var(--text); }
.dashroot tbody tr:last-child td { border-bottom: none; }
.dashroot .pos { color: var(--success); font-weight: var(--fw-semibold); }
.dashroot .neg { color: var(--danger); font-weight: var(--fw-semibold); }
.dashroot .region { font-weight: var(--fw-semibold); color: var(--text-strong); }

/* text + image blocks */
.dashroot .dh-textblk { padding: var(--space-4) var(--space-5); font-size: var(--fs-md); color: var(--text); }
.dashroot .dh-imgblk { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---- per-block appearance (size / palette colour / align), set from block.source.style ---- */
.dashroot .blk { --dh-fs: 1; }
.dashroot .blk.dh-size-sm { --dh-fs: 0.86; }
.dashroot .blk.dh-size-lg { --dh-fs: 1.2; }
/* Palette colours map to platform semantic tokens, so light/dark just works. Default = inherit. */
.dashroot .blk.dh-color-muted  { --dh-block-color: var(--text-muted); }
.dashroot .blk.dh-color-strong { --dh-block-color: var(--text-strong); }
.dashroot .blk.dh-color-accent { --dh-block-color: var(--accent); }
.dashroot .blk.dh-color-up     { --dh-block-color: var(--success); }
.dashroot .blk.dh-color-down   { --dh-block-color: var(--danger); }
.dashroot .blk .dh-textblk { font-size: calc(var(--fs-md) * var(--dh-fs)); color: var(--dh-block-color, var(--text)); }
.dashroot .blk .dh-kpi .val { font-size: calc(25px * var(--dh-fs)); }
.dashroot .blk .dh-kpi .label { font-size: calc(var(--fs-sm) * var(--dh-fs)); color: var(--dh-block-color, var(--text-secondary)); }
.dashroot .blk .dh-kpi .sub { color: var(--dh-block-color, var(--text-muted)); }
.dashroot .blk .blk-head h3 { color: var(--dh-block-color, var(--text-strong)); }
.dashroot .blk table { font-size: calc(var(--fs-base) * var(--dh-fs)); }
/* ---- Horizontal align (center) — covers text, a single KPI, and breakdown tiles ---- */
.dashroot .blk.dh-align-center { text-align: center; }
.dashroot .blk.dh-align-center .dh-kpi { flex-direction: column; align-items: center; text-align: center; gap: 8px; }
.dashroot .blk.dh-align-center .dh-kpirow { justify-items: center; }      /* single KPI (grid) */
.dashroot .blk.dh-align-center .dh-kpi-break { justify-content: center; } /* breakdown tiles (flex) */

/* ---- Vertical align (middle / bottom) — the content fills the block height and parks accordingly.
   Works for the text block AND the KPI row (single + breakdown). ---- */
.dashroot .blk.dh-valign-middle .dh-textblk,
.dashroot .blk.dh-valign-bottom .dh-textblk { display: flex; flex-direction: column; flex: 1; }
.dashroot .blk.dh-valign-middle .dh-textblk { justify-content: center; }
.dashroot .blk.dh-valign-bottom .dh-textblk { justify-content: flex-end; }
.dashroot .blk.dh-align-center.dh-valign-middle .dh-textblk,
.dashroot .blk.dh-align-center.dh-valign-bottom .dh-textblk { align-items: center; }
/* A single KPI sits centred vertically in its block by default (the grid row no longer stretches to
   the top); Align (vertical) overrides to top / bottom. Breakdown tiles keep their own top packing
   unless a valign is chosen. */
.dashroot .blk .dh-kpirow:not(.dh-kpi-break) { align-content: center; }
.dashroot .blk.dh-valign-top .dh-kpirow { align-content: flex-start; }
.dashroot .blk.dh-valign-middle .dh-kpirow { align-content: center; }
.dashroot .blk.dh-valign-bottom .dh-kpirow { align-content: flex-end; }

/* Block background: transparent (drops the empty card frame), or a subtle/accent tint. Targets the
   block card and the KPI tile (KPI's surface is the inner .dh-kpi, since its .blk wrapper is bare). */
.dashroot .blk.dh-bg-transparent, .dashroot .blk.dh-bg-transparent .dh-kpi { background: transparent; border-color: transparent; box-shadow: none; }
.dashroot .blk.dh-bg-subtle, .dashroot .blk.dh-bg-subtle .dh-kpi { background: var(--surface-sunken); }
.dashroot .blk.dh-bg-accent, .dashroot .blk.dh-bg-accent .dh-kpi { background: var(--accent-soft-bg); }

/* ---- dashboards landing list ---- */
.dashroot .dh-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--space-4); padding: var(--space-4) var(--page-pad-x) var(--space-7); }
.dashroot .dh-empty { color: var(--text-muted); padding: var(--space-7) var(--page-pad-x); font-size: var(--fs-md); }
.dashroot .dh-card { background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--r-lg);
  box-shadow: var(--shadow-dropdown); padding: var(--space-4) var(--space-4) var(--space-3); cursor: pointer; position: relative;
  transition: box-shadow .15s, border-color .15s; }
.dashroot .dh-card:hover { border-color: var(--accent); }
.dashroot .dh-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.dashroot .dh-card-top { display: flex; align-items: center; justify-content: space-between; color: var(--accent); margin-bottom: 10px; }
.dashroot .dh-card-top .lucide { width: 26px; height: 26px; }
.dashroot .dh-card-badge { display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  color: var(--accent-strong); background: var(--accent-soft-bg); border-radius: var(--r-pill); padding: 3px 9px; }
.dashroot .dh-card-badge .lucide { width: 12px; height: 12px; }
.dashroot .dh-card-name { font-weight: var(--fw-semibold); font-size: var(--fs-md); color: var(--text-strong); line-height: 1.25; }
.dashroot .dh-card-meta { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 4px; }
.dashroot .dh-card-acts { display: flex; gap: 4px; margin-top: 12px; opacity: 0; transition: opacity .12s; }
.dashroot .dh-card:hover .dh-card-acts, .dashroot .dh-card:focus-within .dh-card-acts { opacity: 1; }
.dashroot .dh-card-act { width: 30px; height: 30px; border: 1px solid var(--border-input); background: var(--surface);
  border-radius: var(--r-sm); display: grid; place-items: center; color: var(--text-secondary); cursor: pointer; }
.dashroot .dh-card-act:hover { background: var(--surface-hover); color: var(--accent); border-color: var(--accent); }
.dashroot .dh-card-act.danger:hover { color: var(--danger-icon); border-color: var(--danger-border); }
.dashroot .dh-card-act .lucide { width: 15px; height: 15px; }

/* ---- resize handles: hidden in View, clearly visible in Edit (contrast over any block content) ---- */
.dashroot .grid-stack-item > .ui-resizable-handle { display: none; }
.dashroot.is-edit .grid-stack-item > .ui-resizable-handle { display: block; }
/* Corner grip: the conventional diagonal resize lines in the bottom-right corner (like a textarea /
   window resizer) — subtle by default, accent on hover. Masked to the corner triangle. */
.dashroot.is-edit .grid-stack-item > .ui-resizable-se {
  width: 14px; height: 14px; right: 3px; bottom: 3px; z-index: 6;
  background: none; border: none; box-shadow: none; transform: none; opacity: .55;
  background-image: repeating-linear-gradient(-45deg, var(--text-muted) 0 1.5px, transparent 1.5px 4px);
  -webkit-mask: linear-gradient(-45deg, transparent 0 48%, #000 48%);
          mask: linear-gradient(-45deg, transparent 0 48%, #000 48%);
}
.dashroot.is-edit .grid-stack-item > .ui-resizable-se:hover {
  opacity: 1; background-image: repeating-linear-gradient(-45deg, var(--accent) 0 1.5px, transparent 1.5px 4px);
}
/* Edge handles: thin accent bars centred on the right / bottom edges, brighten on hover. */
.dashroot.is-edit .grid-stack-item > .ui-resizable-e,
.dashroot.is-edit .grid-stack-item > .ui-resizable-s {
  background: var(--border-input); border-radius: var(--r-pill); opacity: 0; transition: opacity .12s, background .12s;
}
.dashroot.is-edit .grid-stack-item:hover > .ui-resizable-e,
.dashroot.is-edit .grid-stack-item:hover > .ui-resizable-s { opacity: .9; }
.dashroot.is-edit .grid-stack-item > .ui-resizable-e { width: 4px; right: 3px; top: 50%; height: 36px; margin-top: -18px; }
.dashroot.is-edit .grid-stack-item > .ui-resizable-s { height: 4px; bottom: 3px; left: 50%; width: 36px; margin-left: -18px; }
.dashroot.is-edit .grid-stack-item > .ui-resizable-e:hover,
.dashroot.is-edit .grid-stack-item > .ui-resizable-s:hover { background: var(--accent); opacity: 1; }

/* ---- timeline path: checkbox + Edit names button in the source picker, and the frame editor ---- */
/* The generic `.modal input { width: 100% }` would stretch these checkboxes across the row — pin them to
   native size like `.modal label.modal-checkbox > input[type=checkbox]` does. */
.dh-sp-tl-wrap { display: flex; align-items: center; gap: 12px; }
.dh-sp-tl-note { font-size: var(--fs-sm); color: var(--text-muted); }
.modal .dh-sp-tl-wrap input[type="checkbox"],
.modal .dh-frame-row input[type="checkbox"] { width: auto; flex: 0 0 auto; margin: 0; }
/* .dh-outline is scoped to .dashroot; the picker modal lives on <body>, so style its button here. */
.modal .dh-sp-tl-edit { display: inline-flex; align-items: center; height: var(--control-h); padding: 0 12px;
  border: 1px solid var(--border-input); border-radius: var(--r-sm); background: var(--surface-raised);
  color: var(--text); font: inherit; font-size: var(--fs-sm); cursor: pointer; white-space: nowrap; }
.modal .dh-sp-tl-edit:hover { background: var(--surface-hover); }
.modal.dh-frames .dh-frames-hint { color: var(--text-muted); font-size: var(--fs-sm); margin: 0 0 8px; }
.modal.dh-frames .dh-frames-bulk { display: flex; gap: 12px; margin: 0 0 10px; }
.modal.dh-frames .dh-frames-bulk button { font-size: var(--fs-sm); color: var(--accent); background: none; border: none; padding: 0; cursor: pointer; }
.modal.dh-frames .dh-frames-bulk button:hover { text-decoration: underline; }
.modal.dh-frames .dh-frames-search { margin: 0 0 10px; }
.modal.dh-frames .dh-frames-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; max-height: 52vh; overflow: auto; }
/* Just the row layout — no border/background. The label input already carries the only outline, so the
   rows don't read as nested boxes. */
.dh-frame-row { display: flex; align-items: center; gap: 10px; padding: 1px 0; }
.dh-frame-row .dh-frame-grip { cursor: grab; color: var(--text-muted); user-select: none; line-height: 1; }
.modal .dh-frame-row .dh-frame-label { width: auto; flex: 1 1 auto; min-width: 0; margin: 0; }
/* Band/asset filter sits above the select; both fill the row's control column. */
.dh-sp-asset-wrap { display: flex; flex-direction: column; gap: 4px; flex: 1 1 auto; min-width: 0; }

/* Reused colormap dropdown (maps_picker) in the map-layer editor row: a fixed-width trigger matching the
   other row controls' height, and a wider menu so the ramp names aren't truncated to "y…" in the narrow
   row. NB: the editor modal lives on <body>, NOT under .dashroot — scope to .dh-me (the modal), or these
   rules never match and the menu falls back to the trigger's narrow width. */
.dh-me .dh-me-cmap-mount { flex: none; }
.dh-me .dh-me-cmap-mount .cmap-dd { width: 132px; }
.dh-me .dh-me-cmap-mount .cmap-dd-trigger { height: var(--control-h); }
.dh-me .dh-me-cmap-mount .cmap-dd-menu { width: 172px; right: auto; }

/* ---- extra text sizes (for big titles in text blocks) ---- */
.dashroot .blk.dh-size-xl  { --dh-fs: 1.6; }
.dashroot .blk.dh-size-2xl { --dh-fs: 2.3; }
.dashroot .blk.dh-size-3xl { --dh-fs: 3.2; }

/* Markdown content inside a text block (sizes are em-relative so the block's Text size scales them). */
.dashroot .dh-textblk h1, .dashroot .dh-textblk h2, .dashroot .dh-textblk h3 { margin: 0 0 .35em; font-weight: var(--fw-bold); color: var(--dh-block-color, var(--text-strong)); line-height: 1.15; }
.dashroot .dh-textblk h1 { font-size: 1.9em; letter-spacing: -.01em; }
.dashroot .dh-textblk h2 { font-size: 1.45em; }
.dashroot .dh-textblk h3 { font-size: 1.18em; }
.dashroot .dh-textblk p { margin: 0 0 .5em; }
.dashroot .dh-textblk ul { margin: 0 0 .5em; padding-left: 1.3em; }
.dashroot .dh-textblk li { margin: .15em 0; }
.dashroot .dh-textblk a { color: var(--accent); text-decoration: underline; }
.dashroot .dh-textblk > :last-child { margin-bottom: 0; }

/* Headless block (title hidden): the content fills the block. In edit the per-block tools sit
   top-right, so nudge a headless map's zoom control down to clear them. */
.dashroot.is-edit .blk.no-head .dh-zoom { top: 54px; }

/* Catalog-bound values still loading: muted placeholder until the hydrator fills them in. */
.dashroot .dh-tbl-loading td { color: var(--text-muted); text-align: center; }

/* ---- consistent SOLID hover for every floating block control (never translucent over the map,
   which made icons vanish): zoom +/-, year-slider nav, Fullscreen/Export, layer/indicator pickers ---- */
.dashroot .dh-outline:hover,
.dashroot .dh-select:hover { background: var(--surface-hover); color: var(--text-strong); border-color: var(--accent); }
/* Zoom + slider buttons are inside a bordered container — hover only changes the fill (no border-color,
   which would draw a stray accent edge inside the joined control). */
.dashroot .dh-zoom button:hover,
.dashroot .dh-slider .nav:hover { background: var(--surface-hover); color: var(--text-strong); }
.dashroot .dh-slider .play:hover { background: var(--surface-hover); color: var(--accent); }

/* 24-column grid (halved step) — vendored gridstack.min.css only ships .gs-12 widths. */
.grid-stack.gs-24>.grid-stack-item[gs-w="1"]{width:4.1667%}
.grid-stack.gs-24>.grid-stack-item[gs-w="2"]{width:8.3333%}
.grid-stack.gs-24>.grid-stack-item[gs-w="3"]{width:12.5%}
.grid-stack.gs-24>.grid-stack-item[gs-w="4"]{width:16.6667%}
.grid-stack.gs-24>.grid-stack-item[gs-w="5"]{width:20.8333%}
.grid-stack.gs-24>.grid-stack-item[gs-w="6"]{width:25.0%}
.grid-stack.gs-24>.grid-stack-item[gs-w="7"]{width:29.1667%}
.grid-stack.gs-24>.grid-stack-item[gs-w="8"]{width:33.3333%}
.grid-stack.gs-24>.grid-stack-item[gs-w="9"]{width:37.5%}
.grid-stack.gs-24>.grid-stack-item[gs-w="10"]{width:41.6667%}
.grid-stack.gs-24>.grid-stack-item[gs-w="11"]{width:45.8333%}
.grid-stack.gs-24>.grid-stack-item[gs-w="12"]{width:50.0%}
.grid-stack.gs-24>.grid-stack-item[gs-w="13"]{width:54.1667%}
.grid-stack.gs-24>.grid-stack-item[gs-w="14"]{width:58.3333%}
.grid-stack.gs-24>.grid-stack-item[gs-w="15"]{width:62.5%}
.grid-stack.gs-24>.grid-stack-item[gs-w="16"]{width:66.6667%}
.grid-stack.gs-24>.grid-stack-item[gs-w="17"]{width:70.8333%}
.grid-stack.gs-24>.grid-stack-item[gs-w="18"]{width:75.0%}
.grid-stack.gs-24>.grid-stack-item[gs-w="19"]{width:79.1667%}
.grid-stack.gs-24>.grid-stack-item[gs-w="20"]{width:83.3333%}
.grid-stack.gs-24>.grid-stack-item[gs-w="21"]{width:87.5%}
.grid-stack.gs-24>.grid-stack-item[gs-w="22"]{width:91.6667%}
.grid-stack.gs-24>.grid-stack-item[gs-w="23"]{width:95.8333%}
.grid-stack.gs-24>.grid-stack-item[gs-w="24"]{width:100.0%}
.grid-stack.gs-24>.grid-stack-item[gs-x="1"]{left:4.1667%}
.grid-stack.gs-24>.grid-stack-item[gs-x="2"]{left:8.3333%}
.grid-stack.gs-24>.grid-stack-item[gs-x="3"]{left:12.5%}
.grid-stack.gs-24>.grid-stack-item[gs-x="4"]{left:16.6667%}
.grid-stack.gs-24>.grid-stack-item[gs-x="5"]{left:20.8333%}
.grid-stack.gs-24>.grid-stack-item[gs-x="6"]{left:25.0%}
.grid-stack.gs-24>.grid-stack-item[gs-x="7"]{left:29.1667%}
.grid-stack.gs-24>.grid-stack-item[gs-x="8"]{left:33.3333%}
.grid-stack.gs-24>.grid-stack-item[gs-x="9"]{left:37.5%}
.grid-stack.gs-24>.grid-stack-item[gs-x="10"]{left:41.6667%}
.grid-stack.gs-24>.grid-stack-item[gs-x="11"]{left:45.8333%}
.grid-stack.gs-24>.grid-stack-item[gs-x="12"]{left:50.0%}
.grid-stack.gs-24>.grid-stack-item[gs-x="13"]{left:54.1667%}
.grid-stack.gs-24>.grid-stack-item[gs-x="14"]{left:58.3333%}
.grid-stack.gs-24>.grid-stack-item[gs-x="15"]{left:62.5%}
.grid-stack.gs-24>.grid-stack-item[gs-x="16"]{left:66.6667%}
.grid-stack.gs-24>.grid-stack-item[gs-x="17"]{left:70.8333%}
.grid-stack.gs-24>.grid-stack-item[gs-x="18"]{left:75.0%}
.grid-stack.gs-24>.grid-stack-item[gs-x="19"]{left:79.1667%}
.grid-stack.gs-24>.grid-stack-item[gs-x="20"]{left:83.3333%}
.grid-stack.gs-24>.grid-stack-item[gs-x="21"]{left:87.5%}
.grid-stack.gs-24>.grid-stack-item[gs-x="22"]{left:91.6667%}
.grid-stack.gs-24>.grid-stack-item[gs-x="23"]{left:95.8333%}

/* PDF export — a title/date band, then blocks flowing in grid order; each row lays its blocks side by
   side proportional to grid span, and rows break cleanly across pages (dashexport.js). */
.dh-pdf-header { padding: 0 0 14px; margin-bottom: 18px; border-bottom: 2px solid var(--accent); }
.dh-pdf-header h1 { margin: 0; font-size: 24px; color: var(--text-strong); }
.dh-pdf-date { margin-top: 4px; font-size: 13px; color: var(--text-secondary); }
.dh-pdf-flow { display: flex; flex-direction: column; gap: 14px; }
/* wrap so a wide KPI strip drops to the next line instead of overflowing the page or clipping cards */
.dh-pdf-row { display: flex; flex-wrap: wrap; gap: 14px; align-items: stretch; }
.dh-pdf-block { min-width: 170px; }
.dh-pdf-block > .grid-stack-item-content { width: 100%; height: 100%; }
/* Widen the shared report modal for the landscape dashboard report so the preview reads landscape and
   the blocks flow at a wide (landscape-page) width. */
.mrp-dialog.mrp-wide { width: min(1120px, 96vw); }
.mrp-dialog.mrp-wide .mrp-frame { height: min(72vh, 720px); }
/* Running header on pages 2+ of the PDF report: dashboard name + "page X of N". */
.dh-pdf-runhead { display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding-bottom: 8px; margin-bottom: 4px; border-bottom: 1px solid var(--border);
  font-size: 12px; color: var(--text-muted); }
.dh-pdf-runhead-name { font-weight: var(--fw-semibold); color: var(--text-secondary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dh-pdf-runhead-pg { flex: none; font-variant-numeric: tabular-nums; }
