/*
 * App shell and layout skeleton.
 * The layout engine (I1) fills #dash-root with bands and columns; this file only
 * establishes the shell, the scroll discipline, and the coarse-pointer sizing.
 */

*, *::before, *::after { box-sizing: border-box; }

/* Widgets, columns and bands all set `display`, which outranks the UA sheet's
   `[hidden] { display: none }`. The engine hides inactive tab bodies with the `hidden`
   property, so that rule has to win here. */
[hidden] { display: none !important; }

html {
  /* Runtime scaling target. surface.js sets this to userFontSize / 0.75. */
  font-size: 16px;
}

body {
  margin: 0;
  height: 100dvh;
  overflow: hidden;              /* only widget bodies scroll */
  background: var(--bg-0);
  color: var(--text);
  font-family: var(--ff-ui);
  font-size: var(--fs-base);
  -webkit-text-size-adjust: none;
  overscroll-behavior: none;
}

#app {
  display: grid;
  /* Header, the first-run hint bar (F5; an empty auto row costs nothing), the SRV
     strip (hidden unless driving), the dash. */
  grid-template-rows: auto auto auto minmax(0, 1fr);
  /* `minmax(0, 1fr)`, not the implicit `auto`: an auto column is never narrower than its
     min-content, and the header's row of controls has a wide one — enough to push the
     whole shell past the viewport on a 900px screen, clipping the header and handing
     every widget a container wider than the window. */
  grid-template-columns: minmax(0, 1fr);
  height: 100dvh;
  min-height: 0;
}

/* Header bar: layout picker, surface name, text size, dev toggle (I1 fills it).
 *
 * The chrome is pinned to pixels while everything below it scales. The text-size slider
 * is what changes the root font-size, so a header sized in `rem` re-laid itself out on
 * every frame of the drag — the control moved out from under the finger holding it, and
 * the size jumped from one end to the other. Widgets scale; the instrument that scales
 * them holds still.
 */
#dash-header {
  --fs-xxs:  9px;
  --fs-xs:  10px;
  --fs-sm:  11px;
  --fs-base: 12px;
  --fs-md:  13px;
  --fs-lg:  15px;
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --header-h: 30px;
  font-size: 12px;
  display: flex;
  align-items: center;
  min-width: 0;
  overflow: hidden;              /* the controls scroll inside it; the page never does */
  gap: var(--sp-3);
  padding: var(--sp-1) var(--sp-3);
  min-height: var(--header-h);
  background: var(--bg-1);
  border-bottom: var(--hairline) solid var(--line);
}

#dash-header .brand {
  color: var(--orange);
  white-space: nowrap;
  font-family: var(--ff-display);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

#dash-header .spacer { flex: 1 1 auto; }

/* Identity: who you are and where, the way the legacy header reads. Empty until the
   first event names a commander, rather than showing placeholder dashes. */
#dash-header .identity {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  min-width: 0;
  overflow: hidden;
}
#dash-header .identity:empty { display: none; }
/* Where the ship is and where it is going: `supercruise → Greg's Legacy`. */
#dash-header .live { color: var(--info); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#dash-header .live:empty { display: none; }
#dash-header .live.is-alert { color: var(--bad); }
#dash-header .cmdr {
  color: var(--amber);
  font-family: var(--ff-display);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  white-space: nowrap;
}
#dash-header .where {
  color: var(--text);
  font-size: var(--fs-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#dash-header .where.dim { color: var(--text-dim); }

#dash-header .pledge {
  padding: 0 var(--sp-2);
  border-left: var(--hairline) solid var(--line-strong);
  color: var(--orange);
  font-size: var(--fs-sm);
  white-space: nowrap;
}
#dash-header .pledge:empty { display: none; }

/* Mount point for the layout engine. */
#dash-root {
  grid-row: 4;
  min-height: 0;
  min-width: 0;
  overflow: hidden;
  background: var(--bg-1);
}

/* ---- Layout tree ----
 * #dash-root becomes .layout: a grid of bands (rows). A band is a grid of columns.
 * A column is a stack (grid of rows) or a tabs group (strip plus one visible body).
 * Track sizes come from the layout JSON, set inline by the engine.
 */
.layout {
  display: grid;
  /* No row gap: the space between two bands is a gutter track, which can be dragged. */
  row-gap: 0;
  column-gap: var(--sp-1);
  min-height: 0;
  min-width: 0;
  padding: var(--sp-1);
}

.band {
  display: grid;
  row-gap: var(--sp-1);
  column-gap: 0;                 /* as above: the space between columns is a gutter */
  min-height: 0;
  min-width: 0;
}

.column {
  min-height: 0;
  min-width: 0;
}

.column.stack {
  display: grid;
  gap: var(--sp-1);
}

.column.tabs {
  display: grid;
  grid-template-rows: auto 1fr;
}

.tabs-body {
  display: grid;
  min-height: 0;
  min-width: 0;
}

/* A tabs group nested inside a stack is a column like any other. */
.column.stack > .column.tabs { min-height: 0; }

/* ---- Gutters ----
 * A gutter is a real grid track, 8px wide, between two bands or two columns. It is the
 * handle for resizing (js/layout/resize.js) and a drop target while dragging. The line
 * inside it is what you see; the track around it is what a finger can actually hit.
 */
.gutter {
  position: relative;
  touch-action: none;            /* a drag on a gutter resizes; it never scrolls */
}
.gutter::before {
  content: '';
  position: absolute;
  inset: 0;
  margin: auto;
  background: var(--line);
}
.gutter-row { cursor: row-resize; }
.gutter-row::before { height: var(--hairline); }
.gutter-column { cursor: col-resize; }
.gutter-column::before { width: var(--hairline); }
.gutter:hover::before,
.gutter:active::before { background: var(--orange); box-shadow: var(--glow); }
.gutter-row:hover::before, .gutter-row:active::before { height: 2px; }
.gutter-column:hover::before, .gutter-column:active::before { width: 2px; }

/* ---- Drag to dock ---- */
body.is-dragging { cursor: grabbing; }
body.is-editing .widget-header { cursor: grab; touch-action: none; }
.widget.is-dragged { opacity: .4; }

.dock-ghost {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 120;
  padding: var(--sp-1) var(--sp-2);
  background: var(--bg-2);
  border: var(--hairline) solid var(--orange);
  color: var(--orange);
  font-size: var(--fs-sm);
  white-space: nowrap;
  pointer-events: none;          /* the ghost must never answer elementFromPoint */
}

.dock-indicator {
  position: fixed;
  z-index: 119;
  background: var(--orange);
  box-shadow: var(--glow-strong);
  pointer-events: none;
}
.dock-indicator.is-fill {
  background: rgba(255, 113, 0, .18);
  border: 2px solid var(--orange);
  box-shadow: none;
}

@media (pointer: coarse) {
  #dash-header { padding: var(--sp-2) var(--sp-3); --header-h: 38px; }
  button, .tab, .picker-item { min-height: var(--touch-min); }
  /* A finger cannot land on 8px. The track stays 8px so the layout does not shift;
     the hit area grows around it. */
  .gutter-row::after, .gutter-column::after {
    content: '';
    position: absolute;
    inset: -0.5rem 0;
  }
  .gutter-column::after { inset: 0 -0.5rem; }
}

/* ---- Scanlines ----
 * A surface setting, off by default, because the effect is a matter of taste and some
 * panels moire badly with it. It sits above the layout and below the picker overlay, and
 * it never takes a pointer event.
 *
 * The lines do not move. A drifting overlay over a table of figures is hard to read and
 * would have to be switched off again under `prefers-reduced-motion`; a static one is
 * the same picture for everybody, so there is no motion here to reduce.
 */
body.has-scanlines::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.28) 0 1px,
    rgba(0, 0, 0, 0) 1px 3px);
}

/* Narrow surfaces: the header is one scrollable row. Control labels and the brand
   wordmark are the first things to go — the controls themselves stay reachable. */
@media (max-width: 700px) {
  #dash-header { gap: var(--sp-2); flex-wrap: nowrap; overflow: hidden; }
  #dash-header .brand { font-size: var(--fs-sm); }
  #dash-header .dot-group { font-size: 0; gap: 0.2rem; }
  #dash-header .identity { gap: var(--sp-2); }
  #dash-header .cmdr { font-size: var(--fs-sm); }
  #dash-header .where { font-size: var(--fs-xs); }
  .ctl-label { display: none; }
  .ctl-value { min-width: 2.2em; }
  .range { width: 4rem; }
  .header-controls { gap: var(--sp-2); }
  /* The controls are what cannot give. Scrolling them was "reachable" only to someone who
     knew to swipe a row with no scrollbar: on a 390px phone edit and settings sat off the
     edge. The identity gives way instead — the pledge first, then the names truncate —
     and the layout select takes what is left. */
  #dash-header .identity { flex: 1 1 0; }
  #dash-header .pledge { display: none; }
  #dash-header .spacer { display: none; }
  .header-controls { flex: 0 1 auto; overflow: visible; }
  .header-controls .ctl { min-width: 0; flex: 0 1 auto; }
  .header-controls .select { max-width: 120px; min-width: 72px; }
}
/* A phone has room for the ship's state or for the names, not both, and a name cut to
   one letter reads as a fault. Status says who and where on the phone's first tab. */
@media (max-width: 480px) {
  #dash-header .cmdr, #dash-header .where { display: none; }
  #dash-header .live { min-width: 0; font-size: var(--fs-xs); }
}
