/* ═══════════════════════════════════════════════════════════════════
   Viewers — shared stylesheet
   Every viewer uses a ".xv-shell" flex container with a ".xv-toolbar" on
   top and either ".xv-split" (canvas + sidebar) or ".xv-single" (full
   width content) beneath. Individual viewers namespace with their own
   prefix (mdv-, svgv-, tblv-, ...) only when they need a specific style.
   ═══════════════════════════════════════════════════════════════════ */

/* Generic shell used by every viewer. Legacy classes (.dxfv-shell,
   .ifcv-shell) kept for backwards compatibility with DXF/IFC code. */
.xv-shell,
.dxfv-shell, .ifcv-shell,
.mdv-shell, .svgv-shell, .tblv-shell, .docv-shell, .pptv-shell, .dbv-shell,
.stlv-shell {
  display: flex;
  flex-direction: column;
  height: calc(100vh - 220px);
  min-height: 480px;
  margin-top: var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  overflow: hidden;
}

/* Toolbar row on top of every viewer. */
.xv-toolbar,
.dxfv-toolbar, .ifcv-toolbar,
.mdv-toolbar, .svgv-toolbar, .tblv-toolbar, .docv-toolbar, .pptv-toolbar, .dbv-toolbar,
.stlv-toolbar {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border-light);
  flex-wrap: wrap;
  background: var(--bg);
}

.xv-tool-group,
.dxfv-tool-group, .ifcv-tool-group,
.mdv-tool-group, .svgv-tool-group, .tblv-tool-group, .docv-tool-group, .pptv-tool-group, .dbv-tool-group,
.stlv-tool-group {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding-right: var(--sp-3);
  border-right: 1px solid var(--border-light);
}
.xv-tool-group:last-of-type,
.dxfv-tool-group:last-of-type, .ifcv-tool-group:last-of-type,
.mdv-tool-group:last-of-type, .svgv-tool-group:last-of-type, .tblv-tool-group:last-of-type,
.docv-tool-group:last-of-type, .pptv-tool-group:last-of-type, .dbv-tool-group:last-of-type,
.stlv-tool-group:last-of-type { border-right: none; }

/* "Close file" (red, trailing edge). */
.xv-tool-group-end,
.dxfv-tool-group-end, .ifcv-tool-group-end,
.mdv-tool-group-end, .svgv-tool-group-end, .tblv-tool-group-end, .docv-tool-group-end, .pptv-tool-group-end, .dbv-tool-group-end,
.stlv-tool-group-end {
  padding-right: 0;
  border-right: none;
  padding-left: var(--sp-3);
  border-left: 1px solid var(--border-light);
}
.xv-btn-danger,
.dxfv-btn-danger, .ifcv-btn-danger,
.mdv-btn-danger, .svgv-btn-danger, .tblv-btn-danger, .docv-btn-danger, .pptv-btn-danger, .dbv-btn-danger,
.stlv-btn-danger {
  color: #fff;
  background: #c23b2e;
  border: 1px solid #a53126;
  font-weight: 500;
}
.xv-btn-danger:hover,
.dxfv-btn-danger:hover, .ifcv-btn-danger:hover,
.mdv-btn-danger:hover, .svgv-btn-danger:hover, .tblv-btn-danger:hover, .docv-btn-danger:hover, .pptv-btn-danger:hover, .dbv-btn-danger:hover,
.stlv-btn-danger:hover {
  background: #d04535;
  border-color: #b33629;
}
.xv-btn-danger:active,
.dxfv-btn-danger:active, .ifcv-btn-danger:active,
.mdv-btn-danger:active, .svgv-btn-danger:active, .tblv-btn-danger:active, .docv-btn-danger:active, .pptv-btn-danger:active, .dbv-btn-danger:active,
.stlv-btn-danger:active {
  background: #a53126;
}

.dxfv-tool.active, .ifcv-tool.active,
.mdv-tool.active, .svgv-tool.active, .tblv-tool.active, .dbv-tool.active {
  background: var(--primary-light);
  color: var(--primary);
}

.xv-status,
.dxfv-status, .ifcv-status,
.mdv-status, .svgv-status, .tblv-status, .dbv-status,
.stlv-status {
  margin-left: auto;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 11px;
  color: var(--text-secondary);
  white-space: nowrap;
}

/* Split pane (canvas + sidebar). */
.xv-split,
.dxfv-split, .ifcv-split,
.mdv-split, .svgv-split, .tblv-split, .docv-split, .pptv-split, .dbv-split,
.stlv-split {
  display: flex;
  flex: 1;
  min-height: 0;
}

.xv-canvas-wrap,
.dxfv-canvas-wrap, .ifcv-canvas-wrap,
.svgv-canvas-wrap {
  flex: 1;
  min-width: 0;
  min-height: 0;
  position: relative;
  /* Neutral dark gray — any color cast here bleeds into how ACI-7
     "default color" lines look in the CAD/SVG viewers. */
  background: #141414;
  overflow: hidden;
  /* Block native pinch-zoom; the viewers implement their own. */
  touch-action: none;
}
.dxfv-canvas-wrap canvas, .ifcv-canvas-wrap canvas,
.xv-canvas-wrap canvas {
  display: block;
  width: 100%;
  height: 100%;
  cursor: grab;
  /* Suppress the browser's native pinch-zoom / double-tap-zoom so the
     viewer can handle those gestures itself (pinch-to-zoom the drawing). */
  touch-action: none;
}

/* 3D DXF scene lives in the same canvas area as the 2D canvas so the
   sidebar layout doesn't shift when the user flips modes. Absolute
   positioning over the (hidden) 2D canvas keeps both at the exact same
   size without needing a wrapper flexbox reorder. */
.dxfv-3d-wrap {
  position: absolute;
  inset: 0;
  background: #141414;
  touch-action: none;
}
.dxfv-3d-wrap.hidden { display: none; }
.dxfv-3d-wrap canvas { display: block; width: 100%; height: 100%; }

/* Sidebar (layers, props, measurements, sheets, …). */
.xv-side,
.dxfv-side, .ifcv-side,
.tblv-side, .dbv-side, .docv-side, .pptv-side {
  width: 320px;
  flex-shrink: 0;
  border-left: 1px solid var(--border-light);
  overflow-y: auto;
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg);
}
.xv-side-heading,
.dxfv-side-heading, .ifcv-side-heading,
.tblv-side-heading, .dbv-side-heading {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-secondary);
  margin: var(--sp-3) 0 var(--sp-2);
}
.xv-side-heading:first-of-type,
.dxfv-side-heading:first-of-type, .ifcv-side-heading:first-of-type,
.tblv-side-heading:first-of-type, .dbv-side-heading:first-of-type { margin-top: 0; }
.dxfv-side-count, .ifcv-side-count, .tblv-side-count, .dbv-side-count {
  color: var(--text-tertiary);
  font-weight: 400;
  margin-left: 4px;
}

/* ─── DXF-specific ──────────────────────────────────────────────── */
.dxfv-layer-list { display: flex; flex-direction: column; gap: 2px; max-height: 260px; overflow-y: auto; border: 1px solid var(--border-light); border-radius: var(--radius-sm); padding: 4px; }
.dxfv-layer-row { display: flex; align-items: center; gap: 8px; padding: 4px 6px; border-radius: 4px; cursor: pointer; font-size: 12px; margin: 0; }
.dxfv-layer-row:hover { background: var(--border-light); }
.dxfv-layer-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dxfv-layer-count { color: var(--text-tertiary); font-size: 11px; font-variant-numeric: tabular-nums; }

.dxfv-info-table, .ifcv-info-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.dxfv-info-table th, .ifcv-info-table th { text-align: left; font-weight: 500; color: var(--text-secondary); padding: 3px 8px 3px 0; vertical-align: top; width: 40%; }
.dxfv-info-table td, .ifcv-info-table td { padding: 3px 0; vertical-align: top; word-break: break-word; }

.dxfv-info, .ifcv-info {
  font-size: 13px;
  padding: var(--sp-2);
  background: var(--bg-color);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
}

.dxfv-measure-list, .ifcv-measure-list { display: flex; flex-direction: column; gap: 4px; }
.dxfv-measure-row, .ifcv-measure-row {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 8px;
  border-radius: 4px;
  background: var(--bg-color);
  border: 1px solid var(--border-light);
  font-size: 12px;
}
.dxfv-measure-idx, .ifcv-measure-idx { color: var(--text-tertiary); font-size: 11px; width: 28px; flex-shrink: 0; }
.dxfv-measure-val, .ifcv-measure-val { color: #ffb800; font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* Calibration banner lives at the top of the measurements list when the
   drawing has been scaled. Subtle accent tint so it reads as an active
   "mode" indicator without screaming — the Reset button is the loud
   element when the user wants to bail back to raw DXF units. */
.dxfv-calib-banner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px;
  padding: 6px 10px;
  margin-bottom: 6px;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent-color) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent-color) 35%, transparent);
  font-size: 12px;
}
.dxfv-calib-text { color: var(--text-main); }

/* Two-column form grid used inside the calibration modal. Keeps the label
   column from growing with long values in the input column. */
.dxfv-calib-grid { display: grid; grid-template-columns: 1fr; gap: 10px; }
.dxfv-calib-row {
  display: grid;
  grid-template-columns: 160px 1fr;
  align-items: center;
  gap: 12px;
  font-size: 13px;
}
.dxfv-calib-row > span:first-child { color: var(--text-muted); }
.dxfv-calib-row input[type="number"],
.dxfv-calib-row input[type="text"] {
  width: 100%;
  padding: 6px 8px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-color);
  color: var(--text-main);
  font-size: 13px;
}
.dxfv-calib-preview {
  padding-top: 6px;
  border-top: 1px dashed var(--border-light);
  color: var(--accent-color);
}
@media (max-width: 520px) {
  .dxfv-calib-row { grid-template-columns: 1fr; gap: 4px; }
}

.ifcv-pset { margin-top: var(--sp-2); border: 1px solid var(--border-light); border-radius: var(--radius-sm); overflow: hidden; }
.ifcv-pset > summary {
  padding: 6px 10px;
  cursor: pointer;
  background: var(--bg-color);
  font-size: 12px;
  font-weight: 500;
  user-select: none;
}
.ifcv-pset > summary:hover { background: var(--border-light); }
.ifcv-pset > table { padding: 6px 10px 10px; font-size: 11.5px; }

/* ─── Markdown viewer ───────────────────────────────────────────── */
.mdv-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
}
.mdv-pane {
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.mdv-pane + .mdv-pane { border-left: 1px solid var(--border-light); }
.mdv-pane-header {
  padding: 6px var(--sp-3);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-tertiary);
  background: var(--bg-color);
  border-bottom: 1px solid var(--border-light);
  font-weight: 600;
}
.mdv-editor {
  flex: 1;
  width: 100%;
  padding: var(--sp-3);
  border: none;
  outline: none;
  resize: none;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 13px;
  line-height: 1.55;
  background: var(--bg);
  color: var(--text-primary);
  tab-size: 2;
}
.mdv-preview {
  flex: 1;
  overflow-y: auto;
  padding: var(--sp-4) var(--sp-5);
  background: var(--bg);
  line-height: 1.6;
  /* Claim the pinch gesture so our own handler can zoom the preview
     instead of letting the browser pinch-zoom the whole page. */
  touch-action: pan-x pan-y;
}
.mdv-preview h1, .mdv-preview h2, .mdv-preview h3 { margin-top: 1.2em; }
.mdv-preview h1 { font-size: 1.8em; border-bottom: 1px solid var(--border-light); padding-bottom: 0.3em; }
.mdv-preview h2 { font-size: 1.45em; border-bottom: 1px solid var(--border-light); padding-bottom: 0.3em; }
.mdv-preview h3 { font-size: 1.2em; }
.mdv-preview code { background: var(--bg-color); padding: 0.1em 0.4em; border-radius: 3px; font-size: 0.9em; }
.mdv-preview pre { background: var(--bg-color); padding: var(--sp-3); border-radius: var(--radius-sm); overflow-x: auto; border: 1px solid var(--border-light); }
.mdv-preview pre code { background: transparent; padding: 0; }
.mdv-preview blockquote { border-left: 3px solid var(--primary); padding-left: var(--sp-3); color: var(--text-secondary); margin-left: 0; }
.mdv-preview table { border-collapse: collapse; margin: var(--sp-3) 0; }
.mdv-preview table th, .mdv-preview table td { border: 1px solid var(--border-light); padding: 6px 10px; }
.mdv-preview table th { background: var(--bg-color); }
.mdv-preview img { max-width: 100%; border-radius: var(--radius-sm); }
.mdv-preview a { color: var(--primary); }
.mdv-preview hr { border: none; border-top: 1px solid var(--border-light); margin: 1.5em 0; }
.mdv-preview ul, .mdv-preview ol { padding-left: 1.6em; }
.mdv-preview input[type="checkbox"] { margin-right: 6px; }

/* ─── SVG viewer ────────────────────────────────────────────────── */
.svgv-split { display: grid; grid-template-columns: 1fr; }
.svgv-split.svgv-with-source { grid-template-columns: 1fr 1fr; }
.svgv-canvas-wrap {
  background: repeating-conic-gradient(#202020 0% 25%, #151515 0% 50%) 50% / 20px 20px;
  display: flex; align-items: center; justify-content: center;
}
.svgv-canvas-wrap.svgv-bg-light {
  background: repeating-conic-gradient(#e0e0e0 0% 25%, #f0f0f0 0% 50%) 50% / 20px 20px;
}
.svgv-canvas-wrap svg { max-width: 100%; max-height: 100%; display: block; }
.svgv-source {
  border-left: 1px solid var(--border-light);
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
}
.svgv-source textarea {
  flex: 1;
  padding: var(--sp-3);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 12px;
  border: none;
  outline: none;
  resize: none;
  background: var(--bg-color);
  color: var(--text-primary);
}

/* ─── Table viewer ──────────────────────────────────────────────── */
.tblv-main { flex: 1; min-width: 0; overflow: hidden; display: flex; flex-direction: column; }
.tblv-sheet-bar {
  display: flex; align-items: center; gap: 4px;
  padding: 4px var(--sp-3);
  background: var(--bg-color);
  border-bottom: 1px solid var(--border-light);
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  flex-shrink: 0;
}
.tblv-sheet-tab {
  padding: 4px 10px;
  font-size: 12px;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  background: var(--bg);
  cursor: pointer;
  white-space: nowrap;
}
.tblv-sheet-tab.active {
  background: var(--primary-light);
  color: var(--primary);
  border-color: var(--primary);
}
.tblv-table-wrap {
  flex: 1;
  min-height: 0;
  overflow: auto;
}
.tblv-table {
  border-collapse: separate;
  border-spacing: 0;
  font-size: 12px;
  font-family: var(--font-mono, ui-monospace, monospace);
  min-width: 100%;
}
.tblv-table th, .tblv-table td {
  border-right: 1px solid var(--border-light);
  border-bottom: 1px solid var(--border-light);
  padding: 4px 8px;
  vertical-align: top;
  min-width: 80px;
  max-width: 320px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tblv-table th {
  background: var(--bg-color);
  font-weight: 600;
  color: var(--text-secondary);
  position: sticky;
  top: 0;
  z-index: 2;
  text-align: left;
}
.tblv-table th.tblv-rownum,
.tblv-table td.tblv-rownum {
  background: var(--bg-color);
  color: var(--text-tertiary);
  text-align: right;
  min-width: 48px;
  font-variant-numeric: tabular-nums;
  position: sticky;
  left: 0;
  z-index: 1;
  user-select: none;
}
.tblv-table th.tblv-rownum { z-index: 3; }
.tblv-table td[contenteditable="true"]:focus {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
  overflow: visible;
  white-space: normal;
  background: var(--bg);
}
.tblv-table tr:hover td:not(.tblv-rownum) { background: var(--bg-color); }

/* ─── Document viewers (DOCX, PPTX) ────────────────────────────── */
.docv-main, .pptv-main {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: var(--sp-5) var(--sp-6);
  background: var(--hover-bg);
  /* Allow pan-scroll but claim pinch so our handler can zoom the doc
     instead of the browser zooming the page. */
  touch-action: pan-x pan-y;
}
.docv-document {
  max-width: 820px;
  margin: 0 auto;
  padding: var(--sp-6);
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  color: #222;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif;
  line-height: 1.55;
}
.docv-document p { margin: 0.6em 0; }
.docv-document h1, .docv-document h2, .docv-document h3, .docv-document h4 { margin-top: 1.3em; }
.docv-document table { border-collapse: collapse; margin: 1em 0; }
.docv-document table th, .docv-document table td { border: 1px solid #ccc; padding: 6px 10px; }
.docv-document img { max-width: 100%; }

.pptv-pptxjs-host {
  display: block;
  margin: 0 auto;
  max-width: 100%;
}
/* PPTXjs renders each slide as .slide; give them a paper look inside our
   viewer so they stand out from the page background. */
.pptv-pptxjs-host .slide {
  margin: 0 auto var(--sp-5);
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  position: relative;
}
.pptv-slide-num {
  position: absolute; top: 8px; right: 12px;
  font-size: 11px; color: #888;
  background: rgba(255, 255, 255, 0.85);
  padding: 2px 6px; border-radius: 10px;
  z-index: 10;
}
/* Legacy class used by the text-only fallback. */
.pptv-slides { display: flex; flex-direction: column; gap: var(--sp-5); align-items: center; }
.pptv-slides .pptv-slide {
  width: 960px; max-width: 100%;
  aspect-ratio: 16 / 9;
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  position: relative;
}
/* ─── Database viewer ──────────────────────────────────────────── */
.dbv-split { display: flex; flex: 1; min-height: 0; }
.dbv-main { flex: 1; display: flex; flex-direction: column; min-width: 0; overflow: hidden; }
.dbv-query-bar {
  display: flex; gap: var(--sp-2);
  padding: var(--sp-3);
  border-bottom: 1px solid var(--border-light);
  align-items: flex-end;
}
.dbv-query {
  flex: 1;
  min-height: 40px; max-height: 200px;
  padding: var(--sp-2);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  resize: vertical;
}
.dbv-result-wrap { flex: 1; min-height: 0; overflow: auto; }
.dbv-tables {
  display: flex; flex-direction: column; gap: 2px;
  max-height: 380px; overflow-y: auto;
  font-size: 12px;
}
.dbv-table-row {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 10px;
  border-radius: 4px;
  cursor: pointer;
  font-family: var(--font-mono, ui-monospace, monospace);
}
.dbv-table-row:hover { background: var(--border-light); }
.dbv-table-row .dbv-table-cols { color: var(--text-tertiary); font-size: 11px; margin-left: auto; }
.dbv-engine-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  background: var(--primary-light);
  color: var(--primary);
  letter-spacing: 0.05em;
}

/* Shared empty/status panels inside viewers. */
.xv-empty {
  flex: 1; display: flex; align-items: center; justify-content: center;
  color: var(--text-tertiary); font-size: 13px;
  padding: var(--sp-5);
  text-align: center;
}

/* ─── Responsive ──────────────────────────────────────────────────
   On narrow screens (phones + small tablets) the classic "canvas
   beside a 320-px sidebar" layout no longer fits, and cramming the
   sidebar underneath the canvas with a single hair-line border made
   it look like an accidental continuation of the viewer. Instead we
   break the sidebar out as its own card with margin, border, its
   own rounded corners and a shadow — visually a second container
   stacked beneath the view. The same treatment is applied to the
   Markdown editor pane and the SVG "source" pane. */
@media (max-width: 900px) {
  .dxfv-split, .ifcv-split, .tblv-split, .dbv-split, .svgv-split, .docv-split, .pptv-split {
    flex-direction: column;
    gap: var(--sp-3);
    padding: 0 var(--sp-3) var(--sp-3);
    /* Let the child cards escape the split container's frame. */
    overflow: visible;
  }
  .mdv-split {
    grid-template-columns: 1fr;
    gap: var(--sp-3);
    padding: 0 var(--sp-3) var(--sp-3);
  }
  .svgv-split.svgv-with-source { grid-template-columns: 1fr; }

  /* The top "canvas / document / editor" half no longer has a right-
     side sibling — round its bottom corners and drop its fixed min so
     touch-zoom has room to actually show content. */
  .dxfv-canvas-wrap, .ifcv-canvas-wrap, .svgv-canvas-wrap {
    min-height: 320px;
    border-radius: var(--radius);
    border: 1px solid var(--border);
  }

  .mdv-pane,
  .svgv-source,
  .dxfv-side, .ifcv-side, .tblv-side, .dbv-side, .docv-side, .pptv-side {
    width: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg);
    box-shadow: var(--shadow-sm);
    padding: var(--sp-3);
    max-height: 60vh;
    overflow-y: auto;
  }
  /* The pane wrapper is already the card; the editor / preview bring
     their own padding, so drop the pane padding to avoid compounding
     indents on mobile. Same pattern for the SVG source editor. */
  .mdv-pane, .svgv-source { padding: 0; overflow: hidden; }
  .mdv-pane + .mdv-pane { border-top: 1px solid var(--border); }
  .svgv-source textarea { padding: var(--sp-3); }

  /* The outer shell no longer needs its frame on mobile — the inner
     cards carry their own borders. Also drop the height cap so the
     page grows naturally. */
  .xv-shell,
  .dxfv-shell, .ifcv-shell,
  .mdv-shell, .svgv-shell, .tblv-shell, .docv-shell, .pptv-shell, .dbv-shell,
  .stlv-shell {
    height: auto;
    min-height: 0;
    border: none;
    border-radius: 0;
    overflow: visible;
    background: transparent;
  }

  /* Toolbar becomes a vertical stack on mobile: every tool-group gets
     its own full-width row so the sections read as clearly separated
     blocks ("mode", "actions", "settings", "close") instead of buttons
     wrapping mid-row. */
  .xv-toolbar,
  .dxfv-toolbar, .ifcv-toolbar,
  .mdv-toolbar, .svgv-toolbar, .tblv-toolbar, .docv-toolbar, .pptv-toolbar, .dbv-toolbar,
  .stlv-toolbar {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    margin: 0 var(--sp-3) var(--sp-3);
    background: var(--bg);
    overflow: hidden;
  }

  /* Each group = one horizontal strip, full width, with its own
     padding and a thin separator. The trailing "Close file" group
     loses its separator (it's the card's bottom edge). */
  .xv-tool-group,
  .dxfv-tool-group, .ifcv-tool-group,
  .mdv-tool-group, .svgv-tool-group, .tblv-tool-group, .docv-tool-group, .pptv-tool-group, .dbv-tool-group,
  .stlv-tool-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-start;
    gap: var(--sp-2);
    row-gap: var(--sp-2);
    width: 100%;
    padding: var(--sp-2) var(--sp-3);
    border: none;
    border-top: 1px solid var(--border-light);
  }
  .xv-tool-group:first-child,
  .dxfv-tool-group:first-child, .ifcv-tool-group:first-child,
  .mdv-tool-group:first-child, .svgv-tool-group:first-child,
  .tblv-tool-group:first-child, .docv-tool-group:first-child,
  .pptv-tool-group:first-child, .dbv-tool-group:first-child,
  .stlv-tool-group:first-child { border-top: none; }

  /* "Close file" trailer: dedicated strip with a muted background so
     it reads as a footer, but the button itself stays left-aligned —
     same starting column as every other button above, so the
     toolbar has one consistent edge top-to-bottom. */
  .xv-tool-group-end,
  .dxfv-tool-group-end, .ifcv-tool-group-end,
  .mdv-tool-group-end, .svgv-tool-group-end, .tblv-tool-group-end, .docv-tool-group-end, .pptv-tool-group-end, .dbv-tool-group-end,
  .stlv-tool-group-end {
    justify-content: flex-start;
    background: var(--hover-bg);
  }

  /* Align text-first children (plain spans and wrapping labels) with
     the text baseline of sibling `.btn` items. Without this, rows
     that start with a label like "Section:" or "Color" sit ~10 px
     further left than rows that start with a button — buttons have
     built-in horizontal padding, bare spans do not, so the row's
     visual left edge jumps around. */
  .xv-toolbar > .xv-tool-group > span:first-child:not([class*="badge"]),
  .xv-toolbar > .xv-tool-group > label:first-child:not(.checkbox),
  .dxfv-toolbar > .dxfv-tool-group > span:first-child:not([class*="badge"]),
  .dxfv-toolbar > .dxfv-tool-group > label:first-child:not(.checkbox),
  .ifcv-toolbar > .ifcv-tool-group > span:first-child:not([class*="badge"]),
  .ifcv-toolbar > .ifcv-tool-group > label:first-child:not(.checkbox),
  .mdv-toolbar > .mdv-tool-group > span:first-child:not([class*="badge"]),
  .mdv-toolbar > .mdv-tool-group > label:first-child:not(.checkbox),
  .svgv-toolbar > .svgv-tool-group > span:first-child:not([class*="badge"]),
  .svgv-toolbar > .svgv-tool-group > label:first-child:not(.checkbox),
  .tblv-toolbar > .tblv-tool-group > span:first-child:not([class*="badge"]),
  .tblv-toolbar > .tblv-tool-group > label:first-child:not(.checkbox),
  .docv-toolbar > .docv-tool-group > span:first-child:not([class*="badge"]),
  .docv-toolbar > .docv-tool-group > label:first-child:not(.checkbox),
  .pptv-toolbar > .pptv-tool-group > span:first-child:not([class*="badge"]),
  .pptv-toolbar > .pptv-tool-group > label:first-child:not(.checkbox),
  .dbv-toolbar > .dbv-tool-group > span:first-child:not([class*="badge"]),
  .dbv-toolbar > .dbv-tool-group > label:first-child:not(.checkbox),
  .stlv-toolbar > .stlv-tool-group > span:first-child:not([class*="badge"]),
  .stlv-toolbar > .stlv-tool-group > label:first-child:not(.checkbox) {
    padding-left: 10px;
  }

  /* Uniform control height — buttons, checkboxes and dropdowns all
     line up on one baseline so each row reads as a tidy strip. */
  .xv-toolbar .btn, .xv-toolbar .checkbox, .xv-toolbar select, .xv-toolbar input[type="range"],
  .dxfv-toolbar .btn, .ifcv-toolbar .btn,
  .mdv-toolbar .btn, .svgv-toolbar .btn, .tblv-toolbar .btn, .docv-toolbar .btn, .pptv-toolbar .btn, .dbv-toolbar .btn,
  .stlv-toolbar .btn,
  .dxfv-toolbar .checkbox, .ifcv-toolbar .checkbox,
  .mdv-toolbar .checkbox, .svgv-toolbar .checkbox, .tblv-toolbar .checkbox,
  .docv-toolbar .checkbox, .pptv-toolbar .checkbox, .dbv-toolbar .checkbox,
  .stlv-toolbar .checkbox,
  .dxfv-toolbar select, .ifcv-toolbar select,
  .mdv-toolbar select, .svgv-toolbar select, .tblv-toolbar select,
  .docv-toolbar select, .pptv-toolbar select, .dbv-toolbar select,
  .stlv-toolbar select {
    min-height: 32px;
  }
  .xv-toolbar .checkbox,
  .dxfv-toolbar .checkbox, .ifcv-toolbar .checkbox,
  .mdv-toolbar .checkbox, .svgv-toolbar .checkbox, .tblv-toolbar .checkbox,
  .docv-toolbar .checkbox, .pptv-toolbar .checkbox, .dbv-toolbar .checkbox,
  .stlv-toolbar .checkbox {
    padding: 2px 6px;
  }
  /* Range sliders get to breathe: take up the leftover space so the
     "Section" row doesn't jam slider + label + flip button together. */
  .xv-toolbar input[type="range"],
  .dxfv-toolbar input[type="range"], .ifcv-toolbar input[type="range"],
  .mdv-toolbar input[type="range"], .svgv-toolbar input[type="range"],
  .tblv-toolbar input[type="range"], .docv-toolbar input[type="range"],
  .pptv-toolbar input[type="range"], .dbv-toolbar input[type="range"],
  .stlv-toolbar input[type="range"] {
    flex: 1 1 140px;
    min-width: 0;
  }

  .xv-status, .dxfv-status, .ifcv-status, .mdv-status, .svgv-status, .tblv-status, .dbv-status, .pptv-status, .docv-status { display: none; }

  /* The toggles row (Snap / Fill / Ignore-2D / Light BG, with future
     room for a basemap dropdown + opacity slider) needs to flow as a
     two-column grid on phones — pure flex-wrap leaves the labels
     stacked at unpredictable widths and the row reads as a jumble.
     Keeping it explicit prevents layout drift when more controls are
     added later (the basemap dropdown is the next thing landing here). */
  .dxfv-toolbar .dxfv-toggles {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px var(--sp-2);
    align-items: center;
  }
  .dxfv-toolbar .dxfv-toggles .dxfv-cb { width: 100%; }

  /* DXF layer list and measurement rows shrink-to-fit inside the mobile
     card rather than forcing horizontal scroll. */
  .dxfv-layer-list, .ifcv-pset { max-height: 240px; }

  .pptv-slide, .pptv-slides .pptv-slide { width: 100%; }

  /* Make document/table content cards breathe a little on mobile. */
  .docv-main, .pptv-main {
    padding: var(--sp-3);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg);
  }
  .docv-document { padding: var(--sp-4); box-shadow: none; }
  .tblv-main, .dbv-main {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
  }
}

/* ─── Background style + basemap controls ─────────────────────────
   The Background dropdown group lives in its own toolbar tool-group
   so it can host the dropdown, an optional CRS button (visible only
   for Mapbox styles) and an opacity slider. On desktop they sit on
   one row; on mobile they reflow into the toggles grid below. */
.dxfv-bg-group {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
}
.dxfv-bg-label {
  font-size: 12px;
  color: var(--text-secondary);
  font-weight: 500;
}
.dxfv-bg-select {
  padding: 4px 6px;
  font-size: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text-primary);
  min-width: 120px;
}
.dxfv-bg-crs {
  /* When a Mapbox style is active we expose the current CRS label so
     the user always knows which projection their data is being
     interpreted as. Tabular numerals keep EPSG codes from jiggling. */
  font-variant-numeric: tabular-nums;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dxfv-bg-opacity-wrap {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text-secondary);
}
.dxfv-bg-opacity {
  width: 110px;
  cursor: ew-resize;
}
.dxfv-bg-opacity-wrap.hidden,
.dxfv-bg-crs.hidden { display: none; }

/* ─── CRS picker modal ───────────────────────────────────────────── */
.crs-picker .modal {
  /* Roomier than the default modal so the searchable list of ~140+
     CRSs has space to breathe. Capped by the modal's own max-height
     so it stays scrollable on small viewports. */
  max-width: 640px;
}

/* Auto-detect chips above the search box. Each one is a one-click
   shortcut for a likely CRS based on the file's bbox, so users with
   no idea what their data is in still get a sensible answer. */
.crs-suggest-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 0 0 var(--sp-2);
}
.crs-suggest-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-tertiary);
  margin-right: 4px;
}
.crs-suggest-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
  font-size: 12px;
  color: var(--text-primary);
  line-height: 1.4;
}
.crs-suggest-chip:hover {
  border-color: var(--primary);
  color: var(--primary);
}
.crs-suggest-chip-code {
  color: var(--text-tertiary);
  font-size: 11px;
}

.crs-search {
  width: 100%;
  padding: 10px 12px;
  font-size: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text-primary);
  margin-bottom: var(--sp-2);
}
.crs-search:focus {
  outline: 2px solid var(--primary);
  outline-offset: -1px;
  border-color: var(--primary);
}

/* Scrollable result list. We cap at a readable height so the modal
   stays a sane size even when the user clears the search and we show
   the full ~140-row list. */
.crs-results {
  max-height: 360px;
  overflow-y: auto;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  padding: 4px;
}
.crs-row {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "name code"
    "hint hint";
  align-items: center;
  width: 100%;
  padding: 8px 10px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-align: left;
  font-size: 13px;
  color: var(--text-primary);
  gap: 2px 12px;
}
.crs-row-name { grid-area: name; font-weight: 500; }
.crs-row-code { grid-area: code; color: var(--text-secondary); }
.crs-row-hint { grid-area: hint; color: var(--text-tertiary); font-size: 12px; }
.crs-row:hover {
  background: var(--hover-bg);
  border-color: var(--border-light);
}
/* Keyboard cursor — visually identical to hover so the user's mouse
   and keyboard share a single highlight metaphor. */
.crs-row.highlighted {
  background: var(--hover-bg);
  border-color: var(--primary);
}
.crs-row.active {
  background: var(--primary-light);
  border-color: var(--primary);
  color: var(--primary);
}
/* "Use EPSG:NNNN as custom code" affordance — visually distinguished
   from preset rows so users notice it's a fall-through, not a
   pre-vetted entry. */
.crs-row-custom {
  border-style: dashed;
  border-color: var(--border);
  margin-top: 4px;
  font-style: italic;
}
.crs-row-custom:hover { border-style: solid; }

.crs-empty {
  padding: 16px;
  text-align: center;
  color: var(--text-tertiary);
  font-size: 13px;
  line-height: 1.5;
}

/* Mobile picker: drop the max-width so the modal claims the screen
   and bump row padding so taps land reliably. */
@media (max-width: 600px) {
  .crs-picker .modal { max-width: none; }
  .crs-row { padding: 12px 10px; min-height: 44px; }
  .crs-results { max-height: 50vh; }
}

/* ─── Mobile basemap controls ─────────────────────────────────────
   Inside the mobile toolbar card the background group becomes its
   own row strip (one strip per tool-group already, see above) but
   we still want the dropdown / CRS button / slider to wrap nicely
   instead of overflowing.

   Order: label first, dropdown beside it, CRS button on its own
   line, opacity slider takes the remaining space. */
@media (max-width: 900px) {
  .dxfv-toolbar .dxfv-bg-group {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 6px var(--sp-2);
    width: 100%;
  }
  .dxfv-toolbar .dxfv-bg-group .dxfv-bg-label { padding-left: 4px; }
  .dxfv-toolbar .dxfv-bg-select {
    width: 100%;
    min-width: 0;
    /* Native selects on iOS render bigger than 16 px text — anything
       smaller triggers an auto-zoom that breaks the toolbar layout. */
    font-size: 14px;
  }
  .dxfv-toolbar .dxfv-bg-crs {
    grid-column: 1 / -1;
    justify-self: flex-start;
    width: 100%;
    text-align: left;
    /* No flex on a button means we must allow the inner span to grow,
       so the CRS label sits on the left and any future chevron can
       sit on the right. */
  }
  .dxfv-toolbar .dxfv-bg-opacity-wrap {
    grid-column: 1 / -1;
    width: 100%;
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--sp-2);
  }
  .dxfv-toolbar .dxfv-bg-opacity { width: auto; }
}

/* Extra tightening for very small (phone-portrait) screens so the
   DXF / IFC toolbars don't wrap into four rows of buttons. */
@media (max-width: 520px) {
  /* Keep the card padding 0 — the group strips provide their own. */
  .xv-tool-group,
  .dxfv-tool-group, .ifcv-tool-group,
  .mdv-tool-group, .svgv-tool-group, .tblv-tool-group, .docv-tool-group, .pptv-tool-group, .dbv-tool-group,
  .stlv-tool-group {
    padding: var(--sp-2);
  }
  .dxfv-canvas-wrap, .ifcv-canvas-wrap, .svgv-canvas-wrap { min-height: 260px; }
}

/* ════════════════════════════════════════════════════════════════════
   Vector viewer v2 layout (DXF / Shapefile)

   The v2 shell drops the fixed canvas + side-bar split in favour of a
   stacked layout: a single-row toolbar of custom dropdowns, an info
   row (CRS + cursor coordinates), a full-width canvas, and finally a
   3-column panel row (Layers / Selection / Measurements) underneath.
   On mobile the toolbar becomes horizontally scrollable and the panel
   row collapses to a single column.

   All v2 rules live in their own block so the legacy DXF/Shape rules
   above keep working for the IFC / SVG / table viewers (which still
   use the old `.dxfv-split` structure via shared classes).
   ════════════════════════════════════════════════════════════════════ */

.xv-shell-v2 {
  display: flex;
  flex-direction: column;
  gap: 0;
  /* Override the legacy `.dxfv-shell` rule (height: calc(100vh - 220px),
     min-height: 480px, overflow: hidden). The v2 shell flows top-to-
     bottom with the canvas + panel row sized inside; the shell itself
     just sets the chrome and lets the document scroll if it overflows.
     `overflow: visible` is critical so dropdown menus that pop above
     the canvas don't get clipped at the toolbar's bottom edge. */
  height: auto;
  min-height: 0;
  /* Drop the legacy `overflow: hidden` so dropdown menus that
     position-absolute inside the toolbar can extend past the
     toolbar's bottom edge without getting clipped. */
  overflow: visible;
  /* Drop the rounded-rectangle chrome — each child section
     (toolbar / info / canvas / panels) styles its own background and
     borders, which gives a cleaner stacked look than wrapping the
     whole thing in one rounded card. */
  border: none;
  border-radius: 0;
  background: transparent;
}

/* ── Toolbar row ────────────────────────────────────────────────── */
.xv-toolbar-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border-light);
  background: var(--bg);
  /* Single row, scroll-x on overflow. iOS momentum-scroll for free. */
  flex-wrap: nowrap;
  overflow-x: auto;
  overflow-y: visible;
  -webkit-overflow-scrolling: touch;
  /* Hide the scrollbar visually; the user can still flick on touch
     and use trackpad gestures on desktop. */
  scrollbar-width: thin;
}
.xv-toolbar-row::-webkit-scrollbar { height: 6px; }
.xv-toolbar-row::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
.xv-toolbar-spacer { flex: 1 1 auto; min-width: 8px; }

/* ── Custom dropdown ────────────────────────────────────────────── */
.xv-dd { position: relative; flex: 0 0 auto; }
.xv-dd-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px 5px 10px;
  font-size: 13px;
  background: transparent;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  color: var(--text-main);
  cursor: pointer;
  white-space: nowrap;
  user-select: none;
  min-height: 30px;
}
.xv-dd-trigger:hover { background: var(--hover-bg); border-color: var(--border); }
.xv-dd.open > .xv-dd-trigger {
  background: var(--primary-light);
  border-color: var(--primary);
  color: var(--primary);
}
.xv-dd-label {
  font-weight: 500;
  /* Cap dropdown trigger label so a long preset name (e.g. "Satellite +
     labels") doesn't push the rest of the toolbar off-screen. */
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.xv-dd-chev {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  transition: transform 120ms ease;
}
.xv-dd.open .xv-dd-chev { transform: rotate(180deg); }

.xv-dd-menu {
  /* `position: fixed` (with top/left set by JS in `positionMenu`) is
     critical here: the toolbar uses `overflow-x: auto` so it scrolls
     horizontally on mobile, which per CSS spec forces `overflow-y` to
     also create a scrolling context — that clips any
     `position: absolute` descendant to the toolbar's height. Fixed
     positioning escapes the scroll context and lets the menu hang
     below the trigger no matter how narrow the viewport is. */
  position: fixed;
  /* Above the page header (z:100), the sticky sidebar (z:1001), and
     the install / banner overlays — but below toasts (z:9999) so a
     toast that fires while a menu is open still reads on top. */
  z-index: 5000;
  min-width: 200px;
  max-width: min(320px, calc(100vw - 24px));
  padding: 4px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  gap: 1px;
}
/* The HTML `hidden` attribute resolves to `display: none` via the UA
   stylesheet, but our author rule above (`display: flex`) has the
   same specificity and wins — so without this, `m.hidden = true`
   silently does nothing and the menus pile up on screen. Attribute
   selector bumps specificity above the base rule. */
.xv-dd-menu[hidden] {
  display: none;
}
.xv-dd-section {
  padding: 6px 10px 4px;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  font-weight: 600;
}
.xv-dd-sep {
  height: 1px;
  background: var(--border-light);
  margin: 4px 6px;
}
.xv-dd-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  font-size: 13px;
  color: var(--text-main);
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  text-align: left;
  cursor: pointer;
  width: 100%;
  text-decoration: none;
}
.xv-dd-item:hover:not(.disabled) {
  background: var(--hover-bg);
}
.xv-dd-item.active {
  background: var(--primary-light);
  color: var(--primary);
  font-weight: 600;
}
.xv-dd-item.disabled {
  opacity: 0.45;
  pointer-events: none;
}
.xv-dd-item-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.xv-dd-item-hint {
  color: var(--text-tertiary);
  font-size: 11px;
  font-weight: 400;
  flex-shrink: 0;
}
.xv-dd-item.active .xv-dd-item-hint { color: var(--primary); opacity: 0.8; }

/* Checkbox-style menu item (Options dropdown). Reads as a row with
   the check on the left, label centred-ish. The native checkbox
   renders inline so it inherits theme colour without extra CSS. */
.xv-dd-check {
  cursor: pointer;
}
.xv-dd-check input[type="checkbox"] {
  flex-shrink: 0;
  margin: 0;
  accent-color: var(--primary);
}

/* ── Info row (CRS + coords) ───────────────────────────────────── */
.xv-info-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 6px var(--sp-3);
  border-bottom: 1px solid var(--border-light);
  background: var(--bg-color);
  font-size: 12px;
  color: var(--text-secondary);
  overflow-x: auto;
  flex-wrap: nowrap;
  white-space: nowrap;
}
.xv-crs-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  font-size: 12px;
  background: transparent;
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  cursor: pointer;
  flex-shrink: 0;
}
.xv-crs-btn:hover { background: var(--hover-bg); border-color: var(--primary); color: var(--text-main); }
.xv-crs-prefix {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.xv-crs-label {
  font-variant-numeric: tabular-nums;
  color: var(--text-main);
  font-weight: 500;
}
.xv-coords {
  margin-left: auto;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 11px;
  color: var(--text-secondary);
  white-space: nowrap;
  /* Override the legacy .dxfv-status `margin-left: auto` already
     present — we're inside a flex row that already pushes us to the
     right via the spacer convention. */
}

/* ── Canvas + floating opacity overlay ─────────────────────────── */
.xv-shell-v2 .dxfv-canvas-wrap {
  /* Fill remaining vertical space inside the shell, but keep a sane
     minimum so the view never collapses to zero on tall headers. */
  flex: 1 1 auto;
  min-height: 460px;
  height: calc(100vh - 380px);
  border-radius: 0;
}
.xv-opacity-overlay {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: var(--radius-sm);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  font-size: 11px;
  pointer-events: auto;
}
.xv-opacity-overlay .xv-opacity-label {
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
}
.xv-opacity-overlay input[type="range"] {
  width: 120px;
  cursor: ew-resize;
  accent-color: var(--primary);
}
[data-theme="light"] .xv-opacity-overlay {
  background: rgba(255, 255, 255, 0.85);
  color: var(--text-main);
  border-color: var(--border);
}

/* ── Panel row (layers / selection / measurements) ─────────────── */
.xv-panels-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-3);
  padding: var(--sp-3);
  border-top: 1px solid var(--border-light);
  background: var(--bg);
}
.xv-panel {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: var(--sp-3);
  background: var(--bg-color);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  max-height: 360px;
  overflow-y: auto;
}
.xv-panel .dxfv-side-heading:first-of-type { margin-top: 0; }

/* ── Mobile reflow ─────────────────────────────────────────────── */
@media (max-width: 900px) {
  .xv-shell-v2 .dxfv-canvas-wrap {
    height: 60vh;
    min-height: 360px;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    margin: 0 var(--sp-3);
  }
  .xv-panels-row {
    grid-template-columns: 1fr;
  }
  .xv-info-row {
    /* Already scrolls horizontally; slightly tighter padding so the
       row doesn't look hollow on narrow screens. */
    padding: 6px var(--sp-3);
  }
  .xv-opacity-overlay {
    right: 8px;
    bottom: 8px;
    padding: 4px 8px;
  }
  .xv-opacity-overlay input[type="range"] { width: 90px; }
}
@media (max-width: 520px) {
  .xv-toolbar-row { padding: var(--sp-2); }
  .xv-dd-trigger { padding: 4px 6px 4px 8px; min-height: 28px; font-size: 12px; }
  .xv-dd-menu { min-width: 180px; }
  .xv-shell-v2 .dxfv-canvas-wrap { height: 55vh; min-height: 320px; }
}

/* On mobile the v1 .dxfv-shell rule reflows tools into a stacked
   card layout. v2 doesn't want any of that — keep the scrollable
   toolbar row and let each section flow naturally. */
@media (max-width: 900px) {
  .dxfv-shell.xv-shell-v2 .xv-toolbar-row {
    flex-direction: row;
    align-items: center;
    gap: var(--sp-2);
  }
  .dxfv-shell.xv-shell-v2 .xv-panels-row {
    padding: var(--sp-3);
  }
}

/* The v1 mobile stylesheet sets `.dxfv-status / .ifcv-status /
   .stlv-status { display: none }` because the legacy viewers crammed
   that text into a narrow toolbar. The v2 status / coord readout sits
   in its own info row so we want it visible on every screen size. */
@media (max-width: 600px) {
  .xv-shell-v2 .xv-coords.dxfv-status,
  .xv-shell-v2 .xv-coords.ifcv-status,
  .xv-shell-v2 .xv-coords.stlv-status { display: block; }
}

/* ── Slider row inside a dropdown ──────────────────────────────────
   Used by the IFC viewer's section-position control. The slider lives
   inside the menu so it stays one click away from the axis radio
   buttons; we don't want it to look like a clickable item, hence the
   plain padding without hover affordances. */
.xv-dd-slider {
  padding: 8px 10px 10px;
  display: flex;
  align-items: center;
}
.xv-dd-slider input[type="range"] {
  width: 100%;
  margin: 0;
  accent-color: var(--primary);
  cursor: ew-resize;
}

/* ── Color picker row inside the Options dropdown ────────────────
   Used by the STL viewer's "Mesh colour" control. Native colour
   inputs need an explicit small size and a sensible border, otherwise
   they balloon to ~70px tall on Chromium. */
.xv-dd-color {
  /* Cursor stays default — the actual picker affordance is on the
     <input> child, not on the row label. Avoids the row looking
     misleadingly clickable when only the swatch opens the picker. */
  cursor: default;
}
.xv-dd-color input[type="color"] {
  width: 32px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
  background: transparent;
  flex-shrink: 0;
}
