/* DXV design system.
 *
 * One vocabulary for every report. The tokens are the ones the point cloud
 * viewer already shipped -- a dark instrument panel with amber for anything
 * you can act on -- lifted out of that template so the other reports can say
 * the same thing with the same words instead of each inventing a blue card.
 *
 * Load order matters: this file, then a page's own <style>. Everything here is
 * a single class or an element inside one, so a page overrides a component
 * without fighting specificity.
 *
 * Sections
 *   1  tokens
 *   2  reset and typography
 *   3  shell: topbar, page, cards, grid
 *   4  controls: buttons, fields, tabs
 *   5  data: tables, KPIs, badges, messages
 *   6  comments panel and pins
 *   7  third-party surfaces (leaflet, plotly)
 */

/* ---------- 1. tokens ---------------------------------------------------- */
/* Dark is the design. The light block exists so a report opened on a bright
   screen stays readable; it is the same palette rotated, not a second design.
   Both are defined here, so no colour has its only definition inside a media
   query. */
:root{
  --bg:#10161c; --stage:#0b1015; --panel:#171f27; --panel-2:#1e2833; --line:#2a3742;
  --ink:#dfe6ec; --ink-dim:#8b9aa8; --ink-faint:#5d6b78;
  --amber:#ffb627; --amber-ink:#231a06;
  --magenta:#ff3d7f; --cyan:#3fd0c9; --green:#4ec9a5; --red:#ff6b6b;
  --grid:#243039; --shadow:0 12px 34px rgba(0,0,0,.45);
  --r:3px;
  --bar-h:44px;
}
@media (prefers-color-scheme: light){
  :root:not([data-theme="dark"]){
    --bg:#e7eaee; --stage:#cfd6dc; --panel:#f4f6f8; --panel-2:#e9edf1; --line:#c6cfd7;
    --ink:#141a20; --ink-dim:#54626e; --ink-faint:#7f8d99;
    --amber:#b46f00; --amber-ink:#fff6e3;
    --magenta:#c2185b; --cyan:#00707a; --green:#0f7a55; --red:#c0392b;
    --grid:#b9c3cb; --shadow:0 10px 28px rgba(20,30,40,.18);
  }
}
:root[data-theme="light"]{
  --bg:#e7eaee; --stage:#cfd6dc; --panel:#f4f6f8; --panel-2:#e9edf1; --line:#c6cfd7;
  --ink:#141a20; --ink-dim:#54626e; --ink-faint:#7f8d99;
  --amber:#b46f00; --amber-ink:#fff6e3;
  --magenta:#c2185b; --cyan:#00707a; --green:#0f7a55; --red:#c0392b;
  --grid:#b9c3cb; --shadow:0 10px 28px rgba(20,30,40,.18);
}

/* ---------- 2. reset and typography -------------------------------------- */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
/* The reset above takes `margin:0` to every element, and a <dialog> opened
   with showModal() is centred by the user agent's own `margin:auto`. Zeroing
   it pinned every modal to the top-left corner. Put it back here rather than
   per dialog, so a new modal is centred without having to know this. */
dialog{margin:auto}
html,body{height:100%}
body{
  background:var(--bg); color:var(--ink);
  font-family:"IBM Plex Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,
              Helvetica,Arial,sans-serif;
  font-size:13px; line-height:1.45;
  -webkit-font-smoothing:antialiased;
  /* Containing block for the pin layer, so a pinned comment is placed
     against the whole document rather than the current viewport. */
  position:relative;
}
button,input,select,textarea{font:inherit;color:inherit}
/* .dxv-btn sets display, which would otherwise beat the user agent's [hidden]
   rule and show a button a page meant to keep back. */
[hidden]{display:none!important}
a{color:var(--amber)}
.mono,.dxv-num{
  font-family:"IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace;
  font-variant-numeric:tabular-nums;
}

/* ---------- 3. shell ----------------------------------------------------- */
/* The topbar every report carries. Fixed height, so a full-viewport page can
   size its own body as calc(100vh - var(--bar-h)) without measuring. */
.dxv-bar{
  display:flex; align-items:center; gap:10px; padding:0 10px;
  height:var(--bar-h); background:var(--panel);
  border-bottom:1px solid var(--line);
  position:sticky; top:0; z-index:400;
}
.dxv-bar img.dxv-logo{height:26px;width:auto;display:block}
.dxv-bar .dxv-title{font-weight:600;letter-spacing:-.01em;font-size:14px;white-space:nowrap}
.dxv-bar .dxv-sub{
  color:var(--ink-faint);font-size:11px;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;max-width:30vw;
}
.dxv-bar .dxv-spacer{flex:1}
.dxv-bar .dxv-status{font-size:11px;color:var(--ink-dim);white-space:nowrap}

/* A report that fills the window rather than scrolling as a document: the bar
   stays, the rest is exactly the space left under it. */
body.dxv-full{overflow:hidden}
.dxv-viewport{height:calc(100vh - var(--bar-h));display:flex;min-height:0}
.dxv-viewport>.dxv-main{flex:1;min-width:0;position:relative}
.dxv-viewport>.dxv-side{
  width:330px;flex:none;background:var(--panel);border-left:1px solid var(--line);
  overflow-y:auto;overscroll-behavior:contain;padding:14px 16px;
}

.dxv-page{padding:16px 20px 28px;max-width:1500px;margin:0 auto}
.dxv-page.wide{max-width:1700px}
.dxv-page.narrow{max-width:1100px}

.dxv-h1{
  font-size:20px;font-weight:600;letter-spacing:-.01em;margin-bottom:14px;
  padding-bottom:10px;border-bottom:1px solid var(--line);
}
.dxv-h1 .dxv-h1-note{font-size:11px;font-weight:400;color:var(--ink-faint);margin-left:10px}

.dxv-card{
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
  padding:14px 16px;margin-bottom:14px;
}
.dxv-card>h3,.dxv-card-h{
  font-size:11px;font-weight:600;color:var(--ink-dim);letter-spacing:.02em;
  text-transform:uppercase;margin-bottom:11px;
  display:flex;align-items:center;justify-content:space-between;gap:10px;
}
.dxv-card.flush{padding:0;overflow:hidden}

.dxv-row{display:flex;gap:14px;flex-wrap:wrap}
.dxv-col{flex:1 1 440px;min-width:300px}
.dxv-col.narrow{flex:1 1 300px;min-width:260px}

/* Control strip above the content: refresh interval, window, filters. */
.dxv-controls{
  display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
  padding:11px 14px;margin-bottom:14px;
}
.dxv-controls .dxv-spacer{flex:1}

/* ---------- 4. controls -------------------------------------------------- */
.dxv-btn{
  display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 10px;
  background:var(--panel-2);border:1px solid var(--line);border-radius:var(--r);
  color:var(--ink);cursor:pointer;white-space:nowrap;
}
.dxv-btn:hover{border-color:var(--ink-faint)}
.dxv-btn.primary{background:var(--amber);color:var(--amber-ink);border-color:transparent;font-weight:600}
.dxv-btn.primary:hover{filter:brightness(1.08)}
.dxv-btn.danger{color:var(--red)}
.dxv-btn.danger:hover{border-color:var(--red)}
.dxv-btn.icon{width:26px;padding:0;justify-content:center}
.dxv-btn.tall{height:30px;padding:0 14px}
.dxv-btn[disabled],.dxv-btn:disabled,.dxv-btn[aria-disabled="true"]{opacity:.45;cursor:not-allowed}
.dxv-btn[aria-disabled="true"]:hover{border-color:var(--line)}
.dxv-btn[aria-pressed="true"]{background:var(--amber);color:var(--amber-ink);border-color:transparent;font-weight:600}
.dxv-btn-row{display:flex;gap:7px;flex-wrap:wrap;align-items:center}

.dxv-field{display:flex;flex-direction:column;gap:4px;min-width:0}
.dxv-field>label,.dxv-label{font-size:11px;color:var(--ink-dim);letter-spacing:.02em}
select,input[type=text],input[type=number],input[type=search],
input[type=date],input[type=datetime-local],textarea{
  background:var(--panel-2);border:1px solid var(--line);border-radius:var(--r);
  color:var(--ink);padding:4px 6px;width:100%;min-width:0;
}
select:hover,input:hover,textarea:hover{border-color:var(--ink-faint)}
textarea{resize:vertical}
.dxv-field input,.dxv-field select{width:130px}
.dxv-field.wide input,.dxv-field.wide select{width:190px}
.dxv-field.full input,.dxv-field.full select,.dxv-field.full textarea{width:100%}
:focus-visible{outline:2px solid var(--amber);outline-offset:1px}
input[type=checkbox],input[type=radio]{accent-color:var(--amber);margin:0}
input[type=range]{
  -webkit-appearance:none;appearance:none;background:transparent;width:100%;height:18px;margin:0;
}
input[type=range]::-webkit-slider-runnable-track{height:2px;background:var(--line)}
input[type=range]::-moz-range-track{height:2px;background:var(--line)}
input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none;width:12px;height:12px;border-radius:50%;
  background:var(--amber);margin-top:-5px;cursor:pointer;
}
input[type=range]::-moz-range-thumb{
  width:12px;height:12px;border:0;border-radius:50%;background:var(--amber);cursor:pointer;
}
.dxv-check{display:inline-flex;align-items:center;gap:6px;cursor:pointer}

.dxv-tabs{display:flex;gap:4px;margin-bottom:10px;flex-wrap:wrap}
.dxv-tab{
  padding:4px 11px;border:1px solid var(--line);border-radius:var(--r);
  background:var(--panel-2);color:var(--ink-dim);cursor:pointer;font-size:12px;
}
.dxv-tab:hover{color:var(--ink)}
.dxv-tab.active{background:var(--amber);color:var(--amber-ink);border-color:transparent;font-weight:600}

/* Drop target for a file the browser reads itself. */
.dxv-drop{
  border:1px dashed var(--line);border-radius:var(--r);padding:24px;text-align:center;
  color:var(--ink-dim);cursor:pointer;transition:background .15s,border-color .15s,color .15s;
}
.dxv-drop:hover,.dxv-drop.over{
  border-color:var(--amber);color:var(--ink);
  background:color-mix(in srgb,var(--amber) 10%,transparent);
}

/* ---------- 5. data ------------------------------------------------------ */
table.dxv-table{width:100%;border-collapse:collapse;font-size:12px}
.dxv-table th,.dxv-table td{padding:6px 10px;text-align:left;border-bottom:1px solid var(--line)}
.dxv-table th{
  background:var(--panel-2);color:var(--ink-dim);font-weight:600;font-size:11px;
  letter-spacing:.02em;position:sticky;top:0;z-index:1;
}
.dxv-table th.sortable{cursor:pointer;user-select:none}
.dxv-table th.sortable:hover{color:var(--ink)}
.dxv-table th .dxv-arrow{color:var(--amber);font-size:9px;margin-left:4px}
.dxv-table tbody tr:hover{background:var(--panel-2)}
.dxv-table td.num{
  text-align:right;font-family:"IBM Plex Mono",ui-monospace,monospace;
  font-variant-numeric:tabular-nums;
}
.dxv-table td.selected{
  background:color-mix(in srgb,var(--amber) 16%,transparent);
  box-shadow:inset 0 0 0 1px var(--amber);
}
.dxv-scroll{overflow:auto;max-height:340px;overscroll-behavior:contain}
.dxv-scroll-x{overflow-x:auto}

.dxv-kpis{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:14px}
.dxv-kpi{
  flex:1 1 140px;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r);padding:11px 13px;
}
.dxv-kpi .dxv-value{
  font-size:23px;font-weight:600;letter-spacing:-.02em;
  font-family:"IBM Plex Mono",ui-monospace,monospace;font-variant-numeric:tabular-nums;
}
.dxv-kpi .dxv-label{font-size:11px;color:var(--ink-dim);letter-spacing:.02em;margin-top:2px}

/* Key/value readout: label left, monospaced figure right. */
.dxv-kv{display:grid;grid-template-columns:auto 1fr;gap:3px 12px;font-size:12px}
.dxv-kv dt{color:var(--ink-dim)}
.dxv-kv dd{
  margin:0;text-align:right;font-family:"IBM Plex Mono",ui-monospace,monospace;
  font-variant-numeric:tabular-nums;
}

.dxv-badge{
  display:inline-block;padding:1px 8px;border-radius:20px;font-size:10px;
  border:1px solid var(--line);color:var(--ink-dim);letter-spacing:.02em;
}
.dxv-badge.live{border-color:var(--green);color:var(--green)}
.dxv-badge.sample{border-color:var(--amber);color:var(--amber)}
.dxv-badge.good{border-color:var(--cyan);color:var(--cyan)}
.dxv-badge.bad{border-color:var(--red);color:var(--red)}

.dxv-note,.dxv-hint{font-size:11px;color:var(--ink-faint);line-height:1.4}
.dxv-muted{color:var(--ink-dim);font-size:12px}
.dxv-warn{color:var(--amber);font-size:12px;min-height:15px}
.dxv-error{color:var(--red);font-size:12px;min-height:15px}
.dxv-empty{color:var(--ink-faint);font-size:12px;padding:20px;text-align:center}
.dxv-swatch{width:10px;height:10px;border-radius:50%;display:inline-block;flex:none}
.dxv-legend{display:flex;gap:12px;flex-wrap:wrap;margin-top:9px;font-size:11px;color:var(--ink-dim)}
.dxv-legend span{display:inline-flex;align-items:center;gap:5px}

/* ---------- 6. comments and pins ----------------------------------------- */
/* The panel is a drawer under the topbar. base.html renders it only when the
   visitor may write, so a viewer never sees the affordance. */
/* One panel, three actions. Comments was the first and is still spelled
   .dxv-comments below; Model and Share want exactly the same shape, so it is
   named .dxv-drawer here and the comments rules alias onto it. Adding to the
   section rather than rewriting it: the pin and toast rules live here too. */
/* Leaflet gives its panes and controls z-indexes up to 1000. Left unconfined
   they climb over these drawers (390) and the pin layer (120); isolated, each
   map stacks as one ordinary block. Controls a page floats beside its map,
   outside .leaflet-container, need their own wrapper isolated (see geosat). */
.leaflet-container{isolation:isolate}
.dxv-drawer{
  position:fixed;top:var(--bar-h);right:0;bottom:0;width:330px;z-index:390;
  background:var(--panel);border-left:1px solid var(--line);
  display:none;flex-direction:column;box-shadow:var(--shadow);
}
.dxv-drawer.open{display:flex}
.dxv-drawer-head{
  display:flex;align-items:center;justify-content:space-between;
  padding:9px 12px;border-bottom:1px solid var(--line);
  font-size:11px;letter-spacing:.08em;color:var(--ink-dim);flex-shrink:0;
}
.dxv-drawer-body{flex:1;overflow-y:auto;padding:10px 12px;display:flex;flex-direction:column;gap:9px}
.dxv-drawer-foot{
  border-top:1px solid var(--line);padding:9px 12px;flex-shrink:0;
  display:flex;flex-direction:column;gap:7px;
}
.dxv-drawer-foot textarea{
  width:100%;min-height:62px;resize:vertical;padding:7px 8px;
  background:var(--panel-2);border:1px solid var(--line);border-radius:var(--r);
  color:var(--ink);font:inherit;font-size:12px;
}
.dxv-drawer-note{font-size:11px;color:var(--ink-faint);line-height:1.5}

/* A model turn reads like a comment: an attributed block of text. */
.dxv-turn{
  background:var(--panel-2);border:1px solid var(--line);border-radius:var(--r);
  padding:8px 9px;font-size:12px;
}
.dxv-turn.me{
  background:color-mix(in srgb,var(--amber) 12%,var(--panel-2));
  border-color:color-mix(in srgb,var(--amber) 35%,var(--line));
}
.dxv-turn-who{font-weight:600;color:var(--ink);font-size:11px}
.dxv-turn-text{margin-top:4px;color:var(--ink-dim);white-space:pre-wrap;word-break:break-word}

/* Share: one button per destination this app can send to. */
.dxv-share-target{
  display:block;width:100%;text-align:left;padding:9px 10px;
  background:var(--panel-2);border:1px solid var(--line);border-radius:var(--r);
  color:var(--ink);cursor:pointer;font:inherit;font-size:12px;
}
.dxv-share-target:hover:not(:disabled){border-color:var(--amber)}
.dxv-share-target:disabled{opacity:.45;cursor:default}
/* Separates a page's own exports from the configured destinations. */
.dxv-share-group{font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint);margin:10px 0 6px}
.dxv-share-group:first-child{margin-top:0}
.dxv-share-target b{display:block}
.dxv-share-target span{display:block;font-size:11px;color:var(--ink-dim);margin-top:2px}
/* Share, inbound: a folder being browsed. Entries are tighter than targets --
   a delivery folder can hold dozens. */
.dxv-share-entry{padding:6px 10px;margin-top:4px}
.dxv-share-entry b{font-weight:500;word-break:break-all}
.dxv-share-crumbs{font-size:12px;margin-bottom:6px;color:var(--ink-dim);word-break:break-all}
.dxv-share-crumb{background:none;border:0;padding:0;font:inherit;color:var(--amber);cursor:pointer}
.dxv-share-note{font-size:11px;color:var(--ink-faint);margin-top:6px}

.dxv-comments{
  position:fixed;top:var(--bar-h);right:0;bottom:0;width:330px;z-index:390;
  background:var(--panel);border-left:1px solid var(--line);
  display:none;flex-direction:column;box-shadow:var(--shadow);
}
.dxv-comments.open{display:flex}
.dxv-comments-head{
  display:flex;align-items:center;justify-content:space-between;
  padding:10px 14px;border-bottom:1px solid var(--line);
  font-size:11px;color:var(--ink-dim);letter-spacing:.02em;
}
.dxv-comments-list{
  flex:1;overflow-y:auto;padding:12px 14px;display:flex;
  flex-direction:column;gap:11px;overscroll-behavior:contain;
}
.dxv-comment{border-bottom:1px solid var(--line);padding-bottom:9px}
.dxv-comment:last-child{border-bottom:none}
.dxv-comment-author{font-size:11px;font-weight:600}
.dxv-comment-meta{font-size:10px;color:var(--ink-faint);margin-left:6px}
.dxv-comment-text{font-size:12px;margin-top:3px;white-space:pre-wrap;word-break:break-word}
/* A thread is a root comment, its replies and its two actions. The border
   moves from the comment to the thread, so a conversation reads as one block
   rather than as three separated sentences. */
.dxv-thread{border-bottom:1px solid var(--line);padding-bottom:10px}
.dxv-thread:last-of-type{border-bottom:none}
.dxv-thread .dxv-comment{border-bottom:none;padding-bottom:0}
.dxv-comment.reply{
  margin-top:7px;padding-left:9px;border-left:2px solid var(--line);
}
/* Resolved stays readable -- it is kept for "why did we decide that" -- but
   steps back so open threads are what the eye lands on. */
.dxv-thread.resolved{opacity:.6}
.dxv-thread.resolved .dxv-comment-text{text-decoration:none}
.dxv-comment-actions{display:flex;gap:6px;margin-top:7px}
.dxv-btn.tiny{height:22px;padding:0 8px;font-size:10px}
.dxv-reply-form{display:flex;flex-direction:column;gap:6px;margin-top:7px}
.dxv-reply-form textarea{height:48px;resize:none;font-size:12px}
.dxv-reply-form .dxv-btn{align-self:flex-end}
/* A pin whose thread is resolved: still findable, no longer shouting. */
.dxv-pin.resolved{opacity:.45;filter:grayscale(1)}

.dxv-comment-form{
  border-top:1px solid var(--line);padding:11px 14px;
  display:flex;flex-direction:column;gap:8px;
}
.dxv-comment-form textarea{height:58px;resize:none;font-size:12px}
.dxv-comment-form .dxv-btn{align-self:flex-end}

/* Pin layer: a transparent sheet over the page that only takes the pointer
   while a pin is being placed. Sized against <body> (see the position:relative
   above) and starting under the bar, so it covers the whole scrolling
   document on a report and exactly the canvas on a full-height one. */
.dxv-pin-layer{
  position:absolute;left:0;top:var(--bar-h);width:100%;
  height:calc(100% - var(--bar-h));pointer-events:none;z-index:120;
}
.dxv-pin-layer.placing{pointer-events:auto;cursor:crosshair}
.dxv-pin{
  position:absolute;width:24px;height:24px;margin-left:-12px;margin-top:-24px;
  pointer-events:auto;cursor:pointer;display:flex;align-items:center;justify-content:center;
  background:var(--amber);border:2px solid var(--panel);border-radius:50% 50% 50% 0;
  transform:rotate(45deg);box-shadow:0 1px 6px rgba(0,0,0,.45);
}
.dxv-pin span{transform:rotate(-45deg);font-size:10px;font-weight:700;color:var(--amber-ink)}
.dxv-popover{
  position:absolute;width:230px;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r);box-shadow:var(--shadow);padding:10px;font-size:12px;
  pointer-events:auto;z-index:130;
}
.dxv-popover textarea{height:52px;resize:none;font-size:12px;margin-top:6px}
.dxv-popover .dxv-btn{margin-top:6px}
.dxv-popover .dxv-pop-close{
  float:right;border:none;background:none;color:var(--ink-faint);
  font-size:14px;cursor:pointer;padding:0;line-height:1;
}

/* Toast: one transient line, used for save results and copy confirmations. */
.dxv-toast{
  position:fixed;left:50%;top:calc(var(--bar-h) + 12px);transform:translateX(-50%);
  z-index:500;background:var(--panel);border:1px solid var(--line);
  box-shadow:var(--shadow);padding:7px 13px;border-radius:var(--r);
  max-width:80%;display:none;
}
.dxv-toast.on{display:block}
.dxv-toast.bad{border-color:var(--red);color:var(--red)}

/* ---------- 7. third-party surfaces -------------------------------------- */
/* Leaflet ships a white chrome. These rules only restate its own colours in
   tokens; the tiles themselves are whatever the project's layer provides. */
.leaflet-container{background:var(--stage);font:inherit}
.leaflet-bar a,.leaflet-control-layers,.leaflet-popup-content-wrapper,.leaflet-popup-tip{
  background:var(--panel);color:var(--ink);border-color:var(--line);
}
.leaflet-bar a{border-bottom-color:var(--line)}
.leaflet-bar a:hover{background:var(--panel-2);color:var(--ink)}
.leaflet-control-attribution{background:var(--panel)!important;color:var(--ink-faint)}
.leaflet-control-attribution a{color:var(--ink-dim)}
.leaflet-popup-content{margin:9px 11px;font-size:12px}

/* A plot lives inside a card, so it keeps the card's own background. */
.js-plotly-plot .plotly .modebar{background:transparent!important}
.js-plotly-plot .plotly .modebar-btn path{fill:var(--ink-dim)}
.js-plotly-plot .plotly .modebar-btn:hover path{fill:var(--amber)}

.dxv-share-status{font-size:12px;line-height:1.55;margin-top:4px}
.dxv-share-status:empty{display:none}
.dxv-share-status p{margin:0 0 8px}
.dxv-share-status.ok{color:var(--green,#3fd0c9)}
.dxv-share-status.bad{color:var(--magenta,#ff3d7f)}
.dxv-share-actions{display:flex;gap:8px}
.dxv-spin{display:inline-block;width:10px;height:10px;margin-right:7px;
  border:2px solid currentColor;border-right-color:transparent;border-radius:50%;
  vertical-align:-1px;animation:dxv-spin .7s linear infinite}
@keyframes dxv-spin{to{transform:rotate(360deg)}}
