/* Map workspace: globe, overlay regions, AOI, panels. */

#cesiumContainer {
  position: fixed;
  inset: 0;
  background: var(--color-void);
  z-index: var(--z-map);
}

/* Cesium ships consumer-app chrome we do not want. */
.cesium-widget-credits,
.cesium-viewer-bottom {
  display: none !important;
}

.cesium-widget canvas {
  outline: none;
}

/* The overlay is a pass-through layer; only real controls take pointers. */
.ui {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  pointer-events: none;
}

.ui > * {
  pointer-events: auto;
}

/* ----------------------------------------------------------------- header */

/* A single continuous bar. It owns the top edge outright rather than floating
   as a tile, so every region below aligns to one horizontal rule. */
/* Three zones divided by rules rather than by empty space: brand, tools,
   account. Spacing is carried by each zone's own padding so the dividers land
   on exact pixels instead of floating in the middle of a grid gap. */
.topbar {
  position: absolute;
  inset: 0 0 auto 0;
  height: var(--topbar-height);
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr) minmax(0, auto);
  align-items: stretch;
  background: var(--color-surface-overlay);
  border-bottom: 1px solid var(--color-border);
}

.topbar__brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-right: var(--space-4);
  border-right: 1px solid var(--color-border);
}

/* The mark carries its own plate, so it sits flush into the corner. */
.topbar__mark {
  width: var(--topbar-height);
  height: var(--topbar-height);
  display: block;
  flex: none;
}

.topbar__id {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  min-width: 0;
}

.topbar__name {
  font-size: var(--text-lg);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: 0.01em;
}

.topbar__product {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  line-height: 1.1;
  color: var(--color-text-muted);
}

.topbar__tools {
  display: flex;
  align-items: stretch;
  padding-left: var(--space-3);
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}

.topbar__tools::-webkit-scrollbar {
  display: none;
}

/* Labelled actions. A glyph alone made the operator guess.

   Full bar height rather than a button floating inside it: the tool then
   *belongs* to the bar, and its state can be written on the bar's own bottom
   rule instead of drawn as a separate box around the label. Three states that
   differ on more than one axis — a boxed-in accent fill for the open panel
   read as a pressed button, which is not what "this panel is open" means. */
.tool {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 100%;
  /* Tighter than the chips at either end. There are six of these in a row, so
     16px each side spread them across the bar and left the header reading as
     mostly gap; the chips keep the wider padding because they are the edges. */
  padding: 0 var(--space-3);
  background: transparent;
  border: 0;
  color: var(--color-text-secondary);
  font-size: var(--text-md);
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--motion-fast) var(--motion-ease), color var(--motion-fast) var(--motion-ease);
}

/* Sits *on* the header's bottom border, so the marker and the bar's edge are
   the same line rather than two rules a pixel apart. */
.tool::after {
  content: "";
  position: absolute;
  inset: auto 0 -1px 0;
  height: 2px;
  background: transparent;
  transition: background var(--motion-fast) var(--motion-ease);
}

.tool .icon {
  color: var(--color-text-muted);
  transition: color var(--motion-fast) var(--motion-ease);
}

.tool:hover:not(:disabled) {
  background: var(--color-surface-hover);
  color: var(--color-text);
}

.tool:hover:not(:disabled) .icon {
  color: var(--color-text-secondary);
}

.tool:hover:not(:disabled)::after {
  background: var(--color-border-strong);
}

.tool[aria-pressed="true"] {
  background: var(--color-surface-raised);
  color: var(--color-text);
}

/* Accent marks the one open panel and nothing else. */
.tool[aria-pressed="true"] .icon {
  color: var(--color-accent);
}

.tool[aria-pressed="true"]::after {
  background: var(--color-accent);
}

.tool:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.tool:disabled {
  color: var(--color-text-muted);
  cursor: not-allowed;
}

.tool:disabled .icon {
  opacity: 0.5;
}

.tool__count {
  min-width: 20px;
  height: 20px;
  display: inline-grid;
  place-items: center;
  padding: 0 var(--space-1);
  background: var(--color-surface-sunken);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
  transition: background var(--motion-fast) var(--motion-ease), color var(--motion-fast) var(--motion-ease);
}

.tool[aria-pressed="true"] .tool__count {
  background: var(--color-accent-quiet);
  color: var(--color-accent);
}

.tool__dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-circle);
  background: var(--color-accent);
}

.topbar__account {
  display: flex;
  align-items: stretch;
  border-left: 1px solid var(--color-border);
}

/* Balance, immediately left of the account. Full height and the same hover
   language as everything else in the bar, with its own divider so it reads as a
   distinct fact rather than part of the name beside it. The number is mono
   because it changes and wants a stable width. */
.credits {
  display: inline-flex;
  /* Centred, like the account chip beside it. Baseline alignment here put the
     content against the top of a full-height button rather than in the middle
     of the bar, which is what made the two read as sitting on different lines. */
  align-items: center;
  height: 100%;
  padding: 0 var(--space-4);
  background: transparent;
  border: 0;
  border-right: 1px solid var(--color-border);
  color: inherit;
  cursor: pointer;
  transition: background var(--motion-fast) var(--motion-ease);
}

/* The number and its unit still share a baseline with each other — they are
   different sizes, and centring them against one another looks wrong. */
.credits__pair {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
}

.credits:hover {
  background: var(--color-surface-hover);
}

.credits:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.credits__value {
  font-size: var(--text-lg);
  font-weight: 500;
  color: var(--color-accent);
  font-variant-numeric: tabular-nums;
}

/* Nothing left to spend is a fact worth reading differently, without the alarm
   of an error state. */
.credits[data-empty="true"] .credits__value {
  color: var(--color-text-secondary);
}

.credits__label {
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

/* Full height and the same state language as the tools, so the bar reads as
   one continuous instrument rather than a strip with a widget parked on it. */
.account__chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  height: 100%;
  padding: 0 var(--space-4);
  background: transparent;
  border: 0;
  color: inherit;
  cursor: pointer;
  transition: background var(--motion-fast) var(--motion-ease);
}

.account__chip::after {
  content: "";
  position: absolute;
  inset: auto 0 -1px 0;
  height: 2px;
  background: transparent;
  transition: background var(--motion-fast) var(--motion-ease);
}

.account__chip:hover {
  background: var(--color-surface-hover);
}

.account__chip:hover::after {
  background: var(--color-border-strong);
}

.account__chip[aria-pressed="true"] {
  background: var(--color-surface-raised);
}

.account__chip[aria-pressed="true"]::after {
  background: var(--color-accent);
}

.account__chip:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

/* 28, not 32: in a 48px bar a 32px plate leaves 8px either side and reads as
   the heaviest thing in the header, which the operator's own initials should
   not be. */
.account__initials {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  background: var(--color-accent-quiet);
  border: 1px solid var(--color-accent-line);
  color: var(--color-accent);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
}

.account__who {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  min-width: 0;
}

.account__label {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
  line-height: 1.1;
}

.account__id {
  font-size: var(--text-md);
  line-height: 1.1;
  max-width: 168px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Without this the chip read as a label, so nobody looked for sign out in it. */
.account__caret {
  width: 14px;
  height: 14px;
  color: var(--color-text-muted);
  transition: transform var(--motion-fast) var(--motion-ease);
}

.account__chip[aria-pressed="true"] .account__caret {
  transform: rotate(180deg);
}

/* The same number again inside the panel, given room to be the headline rather
   than a chip. Sunken like the other secondary regions in the app.

   Two rows, not one: the note underneath can run to a full sentence, and on a
   panel this narrow it wraps. Sharing a baseline with the number meant the
   number sat against the first line with the rest hanging below it. */
.account__credits {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin: 0 var(--space-4) var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface-sunken);
  border-left: 2px solid var(--color-accent);
}

/* The count and its unit do share a baseline — both are short and neither wraps. */
.account__creditshead {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
}

.account__creditsvalue {
  font-size: var(--text-2xl);
  line-height: 1;
  font-weight: 500;
  color: var(--color-accent);
  font-variant-numeric: tabular-nums;
}

.account__credits[data-empty="true"] .account__creditsvalue {
  color: var(--color-text-secondary);
}

/* Pushed to the far end of the balance row, and sized to it rather than to
   a footer: the balance box is small, and a full-height primary button would
   outweigh the number it sits beside. */
.account__addcredits {
  margin-left: auto;
  align-self: center;
  min-height: 28px;
  padding-inline: var(--space-3);
  white-space: nowrap;
}

.account__creditsunit {
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.account__creditslabel {
  font-size: var(--text-md);
  line-height: 1.4;
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}

/* The panel is capped to the viewport; in a short window it scrolls rather
   than clipping, so Add credits and Sign out can always be reached. */
.account__body,
.account__credits,
.account .panel__foot {
  flex: none;
}

.account {
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Sign out stays in view while anything above it scrolls. Opaque, so content
   scrolling underneath does not show through. */
.account .panel__foot {
  position: sticky;
  bottom: 0;
  background: var(--color-surface-overlay);
}

/* ------------------------------------------------------------ side panels */

/* Panels hang from the header on the same inline axis, so the top edge of
   every floating region lines up with the bar above it. */
.side,
.account,
.console {
  position: absolute;
  top: calc(var(--topbar-height) + var(--space-4));
  right: var(--space-4);
  width: var(--side-width);
  max-height: calc(100vh - var(--topbar-height) - var(--space-8) - 132px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--color-surface-overlay);
  border: 1px solid var(--color-border);
}

.side,
.console {
  z-index: var(--z-panel);
}

/* The account panel is a menu hung off its chip, not a peer of the others: it
   flies over whatever is open rather than sharing the corner with it. Without
   the z-index it lost the corner to the collection panel, which comes later in
   the DOM — sign out opened underneath and looked missing. */
.account {
  z-index: var(--z-menu);
  top: var(--topbar-height);
  /* Room for a three-digit balance and Add credits on one row. */
  width: 304px;
  /* Hangs off the header's own rule instead of drawing a second one 1px under it. */
  border-top: 0;
}

.side__body {
  overflow-y: auto;
  overscroll-behavior: contain;
  min-height: 0;
}

.side__hint {
  flex: none;
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--color-border-faint);
  font-size: var(--text-md);
  color: var(--color-text-muted);
}

.panel__foot {
  flex: none;
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-border);
}

.count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 18px;
  padding: 0 var(--space-1);
  background: var(--color-accent-quiet);
  color: var(--color-accent);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}

.account__body {
  display: grid;
  gap: 2px;
  padding: var(--space-3) var(--space-4);
}

.account__name {
  font-size: var(--text-lg);
  font-weight: 500;
  overflow-wrap: anywhere;
}

.account__email {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  overflow-wrap: anywhere;
}

/* Layer rows share one column system: grip, eye, dot, name, actions. */
.layer {
  display: grid;
  grid-template-columns: 24px 24px 12px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-border-faint);
}

.layer:hover {
  background: var(--color-surface-hover);
}

.layer[data-selected="true"] {
  background: var(--color-accent-quiet);
  box-shadow: inset 2px 0 0 var(--color-accent);
}

.layer__toggle,
.layer__action {
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  background: transparent;
  border: 0;
  color: var(--color-text-secondary);
  cursor: pointer;
}

.layer__toggle:hover,
.layer__action:hover {
  color: var(--color-text);
}

.layer__action--danger:hover {
  color: var(--color-danger);
}

.layer__action:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

/* Second step for the irreversible action. It sits under the row it belongs
   to and states what is about to go, rather than relying on the operator
   remembering which of two similar icons they pressed. */
.layer__confirm {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface-sunken);
  border-left: 2px solid var(--color-danger);
}

.layer__confirmtext {
  margin: 0;
  font-size: var(--text-md);
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}

.layer__confirmtext strong {
  color: var(--color-danger);
  font-weight: 600;
}

.layer__confirmactions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
}

/* The destructive choice is filled, not outlined: at this point it is the
   action the operator came for, and it must not read as the quiet option. */
.layer__confirmactions .btn--danger {
  background: var(--color-danger);
  border-color: var(--color-danger);
  color: #fff;
}

.layer__confirmactions .btn--danger:hover:not(:disabled) {
  background: var(--color-danger-hover);
  border-color: var(--color-danger);
  color: #fff;
}

/* The purchase line, under the raster it belongs to. Accent on the left edge
   because this is an offer rather than a warning — the same shape as the
   confirm strip, so the panel keeps one vocabulary for "this row has more to
   say". */
/* The enhanced raster, as a child of the row above it. Indented so its name
   starts where the parent's does (row padding + grip + eye + dot + gaps),
   with an elbow dropping from under the parent's dot. */
.layer__sr {
  --sr-indent: calc(var(--space-3) + 24px + 24px + 12px + 3 * var(--space-2));
  position: relative;
  /* Stacked, not side by side: beside two buttons the indented name would get
     a sliver of the panel and its details would wrap three times. */
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3) var(--space-3) var(--sr-indent);
  background: var(--color-surface-sunken);
  border-bottom: 1px solid var(--color-border-faint);
}

.layer__branch {
  position: absolute;
  /* Under the centre of the parent's 8 px dot, which sits at the start of its
     12 px column; down to the middle of the name. */
  left: calc(var(--space-3) + 24px + 24px + 2 * var(--space-2) + 4px);
  top: 0;
  width: 10px;
  height: calc(var(--space-2) + 9px);
  border-left: 1px solid var(--color-border-strong);
  border-bottom: 1px solid var(--color-border-strong);
}

/* While it is on the globe, the row says so in the interaction colour. */
.layer__sr[data-comparing="true"] {
  box-shadow: inset 2px 0 0 var(--color-accent);
}

.layer__sr[data-comparing="true"] .layer__branch {
  border-color: var(--color-accent);
}

.layer__srinfo {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}

.layer__srtitle {
  font-size: var(--text-md);
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Wraps rather than overflowing: in the 300 px panel of a narrow window the
   two buttons need more than the indented row has, and a clipped Download
   with a sideways scrollbar is worse than a second line. */
.layer__sractions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  min-width: 0;
}

.layer__sractions .btn {
  white-space: nowrap;
}

.layer__srmeta {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}

.layer__meta {
  min-width: 0;
  cursor: pointer;
  background: transparent;
  border: 0;
  text-align: left;
  padding: 0;
  color: inherit;
}

.layer__name {
  font-size: var(--text-md);
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.layer__kind,
.layer__expiry {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  color: var(--color-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Retention is a fact, not an alarm — it only takes the warning colour once
   the raster has less than a day left and the operator can still act on it. */
.layer__expiry[data-urgent="true"] {
  color: var(--color-warning);
}

.layer__actions {
  display: flex;
  gap: 2px;
}

.layer[data-hidden="true"] .layer__name,
.layer[data-hidden="true"] .layer__kind {
  color: var(--color-text-muted);
}

.empty {
  padding: var(--space-6) var(--space-4);
  text-align: center;
  color: var(--color-text-muted);
  font-size: var(--text-md);
}

/* ---------------------------------------------------------------- bottom */

/* One stacked region along the bottom edge: transfers, then the selection
   controls, then a full-width status strip. */
.bottom {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  padding: 0 var(--space-4) 0;
  pointer-events: none;
}

.bottom > * {
  pointer-events: auto;
}

/* Camera zoom, pinned to the right edge of the stack. */
.zoomctl {
  display: flex;
  flex-direction: column;
  align-self: flex-end;
  background: var(--color-surface-overlay);
  border: 1px solid var(--color-border);
}

.zoomctl__btn {
  display: grid;
  place-items: center;
  width: var(--control-height-lg);
  height: var(--control-height-lg);
  background: transparent;
  border: 0;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background var(--motion-fast) var(--motion-ease), color var(--motion-fast) var(--motion-ease);
}

.zoomctl__btn + .zoomctl__btn {
  border-top: 1px solid var(--color-border);
}

.zoomctl__btn:hover:not(:disabled) {
  background: var(--color-surface-raised);
  color: var(--color-text);
}

.zoomctl__btn:active:not(:disabled) {
  background: var(--color-accent);
  color: var(--color-text-inverse);
}

.zoomctl__btn:disabled {
  color: var(--color-text-muted);
  cursor: not-allowed;
}

.zoomctl__btn .icon {
  width: 16px;
  height: 16px;
}

.analysis {
  display: flex;
  align-items: stretch;
  max-width: min(880px, 100%);
  background: var(--color-surface-overlay);
  border: 1px solid var(--color-border);
}

.analysis__identity {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  min-width: 0;
  max-width: 232px;
  padding: var(--space-2) var(--space-4);
  border-right: 1px solid var(--color-border);
}

.analysis__model,
.analysis__label {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
  white-space: nowrap;
}

.analysis__file {
  font-size: var(--text-lg);
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.analysis__group {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-4);
  border-right: 1px solid var(--color-border);
}

.analysis__area {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.analysis__size {
  font-family: var(--font-mono);
  font-size: var(--text-lg);
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
  white-space: nowrap;
  /* The readout swings from "600 × 600" to five digits a side when the whole
     raster is selected; a floor keeps the button from sliding as it does. */
  min-width: 12ch;
}

.analysis__full {
  min-height: 28px;
  padding-inline: var(--space-3);
  font-size: var(--text-md);
  white-space: nowrap;
}

.analysis__size[data-clamped="true"] {
  color: var(--color-warning);
}

.analysis__scales {
  display: flex;
  gap: var(--space-1);
}

.analysis__scales .btn {
  min-width: 46px;
  min-height: 28px;
  padding-inline: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-md);
}

.analysis__run {
  margin: var(--space-2);
  min-width: 132px;
}

.analysis__close {
  width: var(--control-height-lg);
  border: 0;
  border-left: 1px solid var(--color-border);
}

/* Status strip: full width, quiet, always present so the readout never moves. */
.statusbar {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  height: 32px;
  padding: 0 var(--space-4);
  background: var(--color-surface-base);
  border-top: 1px solid var(--color-border);
  font-family: var(--font-mono);
  font-size: var(--text-md);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-secondary);
}

.statusbar__cell {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  white-space: nowrap;
}

.statusbar__value {
  color: var(--color-text);
  min-width: 9ch;
}

.statusbar__spacer {
  margin-left: auto;
}

.statusbar__cell--lock {
  color: var(--color-warning);
}

.statusbar__credit {
  color: var(--color-text-muted);
  font-size: var(--text-xs);
}

/* --------------------------------------------------------- AOI selection */

.aoi {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  pointer-events: none;
}

.aoi__svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.aoi__shape {
  fill: var(--color-accent);
  fill-opacity: 0.18;
  stroke: var(--color-accent-hover);
  stroke-width: 1.5;
  pointer-events: auto;
  cursor: move;
}

.aoi__shape[data-dragging="true"] {
  fill-opacity: 0.26;
}

.aoi__handle {
  position: absolute;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: var(--radius-circle);
  background: var(--color-accent);
  border: 2px solid #fff;
  pointer-events: auto;
  cursor: nwse-resize;
  transition: transform var(--motion-fast) var(--motion-ease);
}

.aoi__handle[data-corner="ne"],
.aoi__handle[data-corner="sw"] {
  cursor: nesw-resize;
}

.aoi__handle:hover,
.aoi__handle[data-dragging="true"] {
  transform: scale(1.25);
}

.aoi__readout {
  position: absolute;
  transform: translate(-50%, -100%);
  margin-top: -10px;
  padding: 2px var(--space-2);
  background: var(--color-surface-overlay);
  border: 1px solid var(--color-accent-line);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* --------------------------------------------------------------- compare */

/* Between the globe and the chrome, like the selection. Only the divider
   takes pointers; everywhere else the globe pans and zooms as usual. */
.compare {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  pointer-events: none;
}

/* A 16 px grab zone around a 2 px line. */
.compare__divider {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 16px;
  margin-left: -8px;
  pointer-events: auto;
  cursor: ew-resize;
  touch-action: none;
}

.compare__divider::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 7px;
  width: 2px;
  background: var(--color-accent);
}

.compare__divider[data-dragging="true"]::before {
  background: var(--color-accent-hover);
}

.compare__handle {
  position: absolute;
  top: 50%;
  left: 50%;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  padding: 0;
  transform: translate(-50%, -50%);
  background: var(--color-accent);
  border: 0;
  color: #fff;
  cursor: ew-resize;
}

.compare__handle:hover,
.compare__divider[data-dragging="true"] .compare__handle {
  background: var(--color-accent-hover);
}

.compare__handle:focus-visible {
  outline: 2px solid var(--color-accent-hover);
  outline-offset: 2px;
}

/* The grip glyph is horizontal bars; turned, it reads as "slide sideways". */
.compare__handle .icon {
  width: 16px;
  height: 16px;
  transform: rotate(90deg);
}

/* Each side named, on its own side of the line, just above the handle. */
.compare__tag {
  position: absolute;
  top: calc(50% - 44px);
  max-width: 28vw;
  padding: 2px var(--space-2);
  background: var(--color-surface-overlay);
  border: 1px solid var(--color-border-strong);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

.compare__tag--left {
  right: calc(50% + var(--space-2));
}

.compare__tag--right {
  left: calc(50% + var(--space-2));
  border-color: var(--color-accent-line);
  color: var(--color-accent-hover);
}

.compare__exit {
  position: absolute;
  top: calc(50% + 24px);
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  background: var(--color-surface-overlay);
  cursor: pointer;
}

/* Compare's loading card. Well above the divider's labels (50% - 44px) so the
   two never collide, and it never takes the pointer. */
.loader {
  position: fixed;
  top: calc(50% - 140px);
  left: 50%;
  z-index: var(--z-overlay);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  width: min(360px, calc(100vw - 2 * var(--space-4)));
  padding: var(--space-3) var(--space-4) 0;
  transform: translateX(-50%);
  background: var(--color-surface-overlay);
  border: 1px solid var(--color-border-strong);
  pointer-events: none;
}

.loader__title {
  font-size: var(--text-md);
  font-weight: 500;
}

.loader__detail {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}

/* Full-bleed along the bottom edge of the card. */
.loader .progress {
  margin: var(--space-2) calc(-1 * var(--space-4)) 0;
}

/* ---------------------------------------------------------------- dialog */

.dialog {
  width: min(460px, 100%);
}

.dialog__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-4) var(--space-2);
}

.dialog__heading {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: 500;
}

.dialog__text {
  margin: 0;
  font-size: var(--text-md);
  color: var(--color-text-secondary);
}

/* The upload modal's fact list, without its outer padding. */
.dialog__facts {
  margin-top: var(--space-2);
  padding: 0;
}

.dialog__error {
  margin: var(--space-2) 0 0;
}

/* The wait between confirming and the answer: says what is happening, with
   the bar running along the bottom edge of the section. */
.dialog__busy {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4) 0;
}

.dialog__busytext {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
}

.dialog__busy .progress {
  margin-inline: calc(-1 * var(--space-4));
}

/* ---------------------------------------------------------- context menu */

.menu {
  position: fixed;
  z-index: var(--z-menu);
  min-width: 236px;
  background: var(--color-surface-overlay);
  border: 1px solid var(--color-border-strong);
}

.menu__head {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-border);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-secondary);
  display: grid;
  gap: 2px;
}

.menu__coord {
  color: var(--color-text);
  font-size: var(--text-md);
}

.menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  min-height: 30px;
  padding: 0 var(--space-3);
  background: transparent;
  border: 0;
  color: var(--color-text);
  font-size: var(--text-md);
  text-align: left;
  cursor: pointer;
}

.menu__item:hover {
  background: var(--color-accent-quiet);
}

.menu__item:disabled {
  color: var(--color-text-muted);
  cursor: not-allowed;
}

.menu__sep {
  height: 1px;
  background: var(--color-border);
  margin: var(--space-1) 0;
}

/* --------------------------------------------------------- progress rail */

.transfers {
  width: min(560px, 100%);
  background: var(--color-surface-overlay);
  border: 1px solid var(--color-border);
}

.transfer {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto auto;
  column-gap: var(--space-3);
  padding: var(--space-2) var(--space-4) var(--space-3);
  border-bottom: 1px solid var(--color-border-faint);
}

.transfer:last-child {
  border-bottom: 0;
}

.transfer__name {
  font-size: var(--text-md);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.transfer__pct {
  font-family: var(--font-mono);
  font-size: var(--text-md);
  font-variant-numeric: tabular-nums;
  color: var(--color-accent);
}

.transfer__detail {
  grid-column: 1 / -1;
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
  padding-bottom: var(--space-2);
}

.transfer__actions {
  display: flex;
  gap: var(--space-1);
}

.transfer .progress {
  grid-column: 1 / -1;
}

/* ---------------------------------------------------------- upload modal */

.scrim {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  background: rgba(4, 7, 13, 0.72);
  display: grid;
  place-items: center;
  padding: var(--space-4);
}

.modal {
  width: min(560px, 100%);
  background: var(--color-surface-overlay);
  border: 1px solid var(--color-border-strong);
}

.drop {
  display: grid;
  place-items: center;
  gap: var(--space-3);
  padding: var(--space-8) var(--space-6);
  margin: var(--space-4);
  border: 1px dashed var(--color-border-strong);
  text-align: center;
  cursor: pointer;
  transition: border-color var(--motion-fast) var(--motion-ease), background var(--motion-fast) var(--motion-ease);
}

.drop[data-active="true"] {
  border-color: var(--color-accent);
  background: var(--color-accent-quiet);
}

/* The dialog focuses this on open, so it needs a visible ring — otherwise a
   keyboard operator is somewhere with nothing to show for it. */
.drop:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.drop__title {
  font-size: var(--text-xl);
  font-weight: 500;
}

.drop__hint {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  max-width: 44ch;
}

.drop .icon {
  width: 28px;
  height: 28px;
  color: var(--color-accent);
}

.facts {
  display: grid;
  grid-template-columns: 132px minmax(0, 1fr);
  gap: var(--space-1) var(--space-3);
  padding: 0 var(--space-4) var(--space-4);
  font-size: var(--text-md);
}

.facts dt {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
  align-self: center;
}

.facts dd {
  margin: 0;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.notice {
  margin: 0 var(--space-4) var(--space-4);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-warning);
  color: var(--color-warning);
  font-size: var(--text-md);
}

.notice--bad {
  border-color: var(--color-danger);
  color: var(--color-danger);
}

/* ---------------------------------------------------------------- toasts */

.toasts {
  position: absolute;
  left: 50%;
  top: calc(var(--space-4) + 68px);
  transform: translateX(-50%);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--color-surface-overlay);
  border: 1px solid var(--color-border-strong);
  font-size: var(--text-md);
  max-width: 520px;
}

.toast--ok {
  border-color: var(--color-success);
}
.toast--bad {
  border-color: var(--color-danger);
}

/* --------------------------------------------------------------- console */

/* The console is a left-hand counterpart to the collection panel. */
.console {
  left: var(--space-4);
  right: auto;
  width: min(520px, calc(100vw - 2 * var(--space-4)));
  max-height: 340px;
}

.console__body {
  overflow-y: auto;
  padding: var(--space-2) var(--space-4) var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.console__line {
  display: grid;
  grid-template-columns: 62px minmax(0, 1fr);
  gap: var(--space-3);
  overflow-wrap: anywhere;
}

.console__time {
  color: var(--color-text-muted);
}

.console__line[data-level="ok"] .console__text {
  color: var(--color-success);
}
.console__line[data-level="bad"] .console__text {
  color: var(--color-danger);
}
.console__line[data-level="key"] .console__text {
  color: var(--color-accent);
}

/* ---------------------------------------------------------- narrow view */

@media (max-width: 1180px) {
  .tool span:not(.tool__count):not(.tool__dot) {
    /* Labels are the first thing to go, not the controls themselves. */
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .tool {
    padding-inline: var(--space-2);
  }
}

@media (max-width: 900px) {
  :root {
    --side-width: 300px;
  }

  .account__who {
    display: none;
  }

  .account__chip {
    padding-right: 0;
  }

  .analysis {
    flex-wrap: wrap;
  }

  .analysis__identity {
    max-width: none;
    flex: 1 0 100%;
    border-right: 0;
    border-bottom: 1px solid var(--color-border);
  }
}

@media (max-width: 700px) {
  .topbar__id {
    display: none;
  }

  .side,
  .account,
  .console {
    left: var(--space-4);
    right: var(--space-4);
    width: auto;
  }
}
