/* Datalastic Sandbox
 *
 * The light palette is lifted from docs.datalastic.com so the two read as one
 * system. The dark palette is new: the docs have none, and the brand purple
 * (#6046AF) only reaches about 3:1 against the brand dark (#0B0A18), which is
 * below AA for text. Dark mode therefore lightens the accent rather than simply
 * swapping background and foreground.
 */

:root {
  /* Brand constants, identical in both themes */
  --brand-dark: #0B0A18;
  --brand-purple: #6046AF;
  --brand-cyan: #92DDEA;
  --brand-pink: #FFA5D8;
  --brand-soft-purple: #BE9DDF;
  --brand-blue: #7EB8DA;

  /* Light theme (matches the docs) */
  --bg: #ffffff;
  --surface: #f7f7fb;
  --surface-2: #eeeef6;
  --border: #ebebf5;
  --border-strong: #d8d8e8;
  --text: #1a1a2e;
  --text-muted: #666680;
  --accent: #6046AF;
  --accent-hover: #4e3894;
  --accent-contrast: #ffffff;

  --ok: #1b7f4d;
  --ok-bg: #e8f6ee;
  --err: #b3261e;
  --err-bg: #fdecea;
  --warn: #8a5a00;
  --warn-bg: #fff8e6;

  /* JSON syntax */
  --j-key: #6046AF;
  --j-str: #1b7f4d;
  --j-num: #b35c00;
  --j-bool: #0b6ea8;
  --j-null: #8a8aa0;
  --j-punct: #8a8aa0;

  /* Map */
  --sea: #dbe8f2;
  --land: #f5f3ed;
  --land-alt: #eceade;
  --road: #e4e0d6;
  --building: #e6e2d8;
  --boundary: rgba(26, 26, 46, 0.22);
  --label-place: #4a4a63;
  --label-water: #6a8fae;
  --label-poi: #6b6b85;
  --label-halo: rgba(255, 255, 255, 0.8);
  --sm-lane: #1f6f9f;
  --sm-boundary: #3f8cba;
  --sm-zone: #7fb0d8;
  --sm-caution: #b8791f;
  --sm-fairway: #4f97c0;
  --graticule: rgba(26, 26, 46, 0.10);
  --marker: #6046AF;
  --marker-stroke: #ffffff;
  --track: #6046AF;
  --route: #b35c00;
  --area: #6046AF;
  --track-start: #1b7f4d;
  --track-end: #b3261e;

  --sidebar-w: 248px;
  --form-w: 380px;
  --topbar-h: 56px;
  --radius: 6px;

  /* Lighter than muted text: a placeholder should not be mistaken for data. */
  --placeholder: #b0b0c2;
}

:root[data-theme="dark"] {
  --bg: #0B0A18;
  --surface: #141326;
  --surface-2: #1d1b34;
  --border: #262441;
  --border-strong: #363156;
  --text: #E8E7F2;
  --text-muted: #9B99B8;
  /* Lightened from #6046AF: the brand purple is unreadable on the brand dark. */
  --accent: #A48FE0;
  --accent-hover: #BCA9EE;
  --accent-contrast: #14122A;

  --ok: #5FD39B;
  --ok-bg: #12301F;
  --err: #FF8B82;
  --err-bg: #3A1614;
  --warn: #F0C36B;
  --warn-bg: #33270D;

  --j-key: #BE9DDF;
  --j-str: #92DDEA;
  --j-num: #FFA5D8;
  --j-bool: #7EB8DA;
  --j-null: #6E6B8C;
  --j-punct: #6E6B8C;

  --sea: #101B2B;
  --land: #1B2030;
  --land-alt: #212739;
  --road: #2A3145;
  --building: #2E354A;
  --boundary: rgba(232, 231, 242, 0.20);
  --label-place: #C6C4DC;
  --label-water: #7796B8;
  --label-poi: #9B99B8;
  --label-halo: rgba(11, 10, 24, 0.85);
  --sm-lane: #6FC3F0;
  --sm-boundary: #5AA9D6;
  --sm-zone: #3E6E96;
  --sm-caution: #F0C36B;
  --sm-fairway: #58A6CC;
  --graticule: rgba(232, 231, 242, 0.08);
  --marker: #FFA5D8;
  --marker-stroke: #0B0A18;
  --track: #92DDEA;
  --route: #FFA5D8;
  --area: #92DDEA;
  --track-start: #5FD39B;
  --track-end: #FF8B82;
  /* Darker than muted text here: on a dark ground, receding means dimmer. */
  --placeholder: #55527a;
}

@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] {
    --bg: #0B0A18;
    --surface: #141326;
    --surface-2: #1d1b34;
    --border: #262441;
    --border-strong: #363156;
    --text: #E8E7F2;
    --text-muted: #9B99B8;
    --accent: #A48FE0;
    --accent-hover: #BCA9EE;
    --accent-contrast: #14122A;

    --ok: #5FD39B;
    --ok-bg: #12301F;
    --err: #FF8B82;
    --err-bg: #3A1614;
    --warn: #F0C36B;
    --warn-bg: #33270D;

    --j-key: #BE9DDF;
    --j-str: #92DDEA;
    --j-num: #FFA5D8;
    --j-bool: #7EB8DA;
    --j-null: #6E6B8C;
    --j-punct: #6E6B8C;

    --sea: #101B2B;
    --land: #1B2030;
    --land-alt: #212739;
    --road: #2A3145;
    --building: #2E354A;
    --boundary: rgba(232, 231, 242, 0.20);
    --label-place: #C6C4DC;
    --label-water: #7796B8;
    --label-poi: #9B99B8;
    --label-halo: rgba(11, 10, 24, 0.85);
    --sm-lane: #6FC3F0;
    --sm-boundary: #5AA9D6;
    --sm-zone: #3E6E96;
    --sm-caution: #F0C36B;
    --sm-fairway: #58A6CC;
    --graticule: rgba(232, 231, 242, 0.08);
    --marker: #FFA5D8;
    --marker-stroke: #0B0A18;
    --track: #92DDEA;
    --route: #FFA5D8;
    --area: #92DDEA;
    --track-start: #5FD39B;
    --track-end: #FF8B82;
    /* Darker than muted text here: on a dark ground, receding means dimmer. */
    --placeholder: #55527a;
  }
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body { height: 100%; }

body {
  font-family: 'Khula', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-size: 15px;
  line-height: 1.6;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

h1, code, pre, .mono, .method-badge, .endpoint-path, .brand-name {
  font-family: 'Inconsolata', ui-monospace, SFMono-Regular, Menlo, monospace;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---------- Top bar ---------- */

.topbar {
  position: fixed; inset: 0 0 auto 0; height: var(--topbar-h);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  z-index: 300;
}

.brand { display: flex; align-items: baseline; gap: 8px; color: var(--text); }
.brand:hover { text-decoration: none; }
.brand-mark { color: var(--accent); font-size: 13px; }
.brand-name { font-weight: 700; font-size: 19px; letter-spacing: -0.5px; }
.brand-sub { color: var(--text-muted); font-size: 13px; letter-spacing: 0.06em; text-transform: uppercase; }

.topbar-right { display: flex; align-items: center; gap: 12px; }

.pill {
  font-size: 12px; padding: 2px 8px; border-radius: 999px;
  border: 1px solid var(--border-strong); color: var(--text-muted);
}
.pill-warn { background: var(--warn-bg); color: var(--warn); border-color: transparent; }

.theme-toggle {
  width: 32px; height: 32px; border-radius: var(--radius);
  border: 1px solid var(--border-strong); background: var(--bg);
  color: var(--text-muted); cursor: pointer; font-size: 15px; line-height: 1;
}
.theme-toggle:hover { color: var(--accent); border-color: var(--accent); }

/* ---------- Layout ---------- */

.app { display: flex; padding-top: var(--topbar-h); height: 100%; }

.sidebar {
  width: var(--sidebar-w); flex: 0 0 var(--sidebar-w);
  background: var(--surface);
  border-right: 1px solid var(--border);
  overflow-y: auto;
  padding: 16px 0 40px;
}

.nav-group { margin-bottom: 18px; }
.nav-group-title {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--text-muted); padding: 0 18px 6px;
}
.nav-link {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding: 6px 18px; color: var(--text); font-size: 14px;
  border-left: 2px solid transparent;
}
.nav-link:hover { background: var(--surface-2); text-decoration: none; }
.nav-link.active { border-left-color: var(--accent); color: var(--accent); background: var(--surface-2); }
.nav-cost { font-size: 11px; color: var(--text-muted); white-space: nowrap; }
.nav-cost.free { color: var(--ok); }

.main { flex: 1; display: flex; min-width: 0; height: calc(100vh - var(--topbar-h)); }

.pane { overflow-y: auto; }
.pane-form {
  width: var(--form-w); flex: 0 0 var(--form-w);
  border-right: 1px solid var(--border);
  padding: 24px 22px 60px;
}
.pane-out { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }

/* ---------- Endpoint header ---------- */

.ep-head h1 { font-size: 22px; letter-spacing: -0.5px; margin-bottom: 10px; }

.method-row { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
.method-badge {
  background: var(--brand-purple); color: #fff;
  font-size: 11px; font-weight: 700; letter-spacing: 0.05em;
  padding: 2px 7px; border-radius: 3px;
}
:root[data-theme="dark"] .method-badge { background: var(--accent); color: var(--accent-contrast); }
.endpoint-path { font-size: 13px; color: var(--text-muted); word-break: break-all; }

.ep-summary { font-size: 14px; color: var(--text-muted); margin-bottom: 10px; }
.ep-cost { font-size: 13px; color: var(--text-muted); margin-bottom: 18px; }
.cost-warn { color: var(--warn); }

/* ---------- Form ---------- */

.field { margin-bottom: 14px; }
.field label {
  display: block; font-size: 13px; font-weight: 600; margin-bottom: 4px;
}
.req {
  font-size: 10px; font-weight: 400; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--err); margin-left: 5px;
}
.field input, .field select {
  width: 100%; padding: 7px 10px;
  font-family: 'Inconsolata', ui-monospace, monospace; font-size: 14px;
  color: var(--text); background: var(--bg);
  border: 1px solid var(--border-strong); border-radius: var(--radius);
}
.field input:focus, .field select:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
}
.field-help { display: block; font-size: 12px; color: var(--text-muted); margin-top: 3px; }
.field-error { display: block; font-size: 12px; color: var(--err); margin-top: 3px; }
.field-error:empty { display: none; }
.field:has(.field-error:not(:empty)) input { border-color: var(--err); }

.oneof-note {
  font-size: 12px; color: var(--text-muted);
  background: var(--surface); border-left: 2px solid var(--border-strong);
  padding: 7px 10px; border-radius: 0 var(--radius) var(--radius) 0;
  margin-bottom: 16px;
}

.btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 16px; border-radius: var(--radius); border: 1px solid transparent;
  font-family: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
}
.btn-primary { background: var(--accent); color: var(--accent-contrast); }
.btn-primary:hover { background: var(--accent-hover); }
.btn:disabled { opacity: 0.6; cursor: progress; }

.btn-spinner { display: none; width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  animation: spin 0.7s linear infinite; }
.btn.htmx-request .btn-spinner { display: inline-block; }
@keyframes spin { to { transform: rotate(360deg); } }

.live-note { font-size: 12px; color: var(--text-muted); margin-top: 10px; }

/* ---------- Map ---------- */

.map-wrap { position: relative; height: 44%; flex: 0 0 44%; border-bottom: 1px solid var(--border); }
#map { position: absolute; inset: 0; background: var(--sea); }
.map-note {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  font-size: 13px; color: var(--text-muted); pointer-events: none;
  background: var(--surface); border: 1px solid var(--border);
  padding: 6px 12px; border-radius: var(--radius); z-index: 5;
}
.map-note[hidden] { display: none; }

.maplibregl-popup-content {
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  font-family: 'Khula', sans-serif; font-size: 13px; padding: 8px 12px;
}
.maplibregl-popup-tip { border-top-color: var(--border-strong) !important; }
.popup-title { font-weight: 700; margin-bottom: 2px; }
.popup-row { color: var(--text-muted); font-size: 12px; }

/* ---------- Result ---------- */

.result { flex: 1; overflow-y: auto; padding: 16px 20px 60px; min-height: 0; }

.alert { padding: 9px 12px; border-radius: var(--radius); font-size: 13px; margin-bottom: 14px; }
.alert-error { background: var(--err-bg); color: var(--err); }

.result-bar { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.status-pill {
  font-family: 'Inconsolata', monospace; font-size: 12px; font-weight: 700;
  padding: 2px 8px; border-radius: 3px;
}
.status-pill.ok { background: var(--ok-bg); color: var(--ok); }
.status-pill.err { background: var(--err-bg); color: var(--err); }
.result-meta { font-size: 12px; color: var(--text-muted); }

.billing {
  display: flex; flex-wrap: wrap; gap: 18px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 10px 14px; margin-bottom: 12px;
}
.billing-item { display: flex; flex-direction: column; }
.billing-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); }
.billing-value { font-family: 'Inconsolata', monospace; font-size: 14px; }
.billing-value.strong { font-weight: 700; color: var(--accent); }

.req-url { margin-bottom: 12px; }
.req-url-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); display: block; }
.req-url code { font-size: 12px; color: var(--text-muted); word-break: break-all; }

.json-block { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
pre.json {
  font-family: 'Inconsolata', ui-monospace, monospace; font-size: 13px; line-height: 1.55;
  padding: 12px 14px; overflow-x: auto; white-space: pre;
}
pre.json.raw { color: var(--text-muted); }

.j-key { color: var(--j-key); }
.j-str { color: var(--j-str); }
.j-num { color: var(--j-num); }
.j-bool { color: var(--j-bool); }
.j-null { color: var(--j-null); font-style: italic; }
.j-punct { color: var(--j-punct); }

/* ---------- Narrow screens ---------- */

@media (max-width: 1000px) {
  .app { display: block; padding-top: var(--topbar-h); height: auto; }
  .sidebar { width: 100%; flex: none; border-right: none; border-bottom: 1px solid var(--border); }
  .main { display: block; height: auto; }
  .pane-form { width: 100%; flex: none; border-right: none; }
  .pane-out { display: block; }
  .map-wrap { height: 340px; flex: none; }
  .result { overflow: visible; }
}

/* ---------- Map status bar ---------- */

.map-status {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 6;
  display: flex; align-items: center; gap: 14px;
  padding: 3px 10px;
  font-family: 'Inconsolata', ui-monospace, monospace;
  font-size: 12px; line-height: 1.5;
  color: var(--text-muted);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border-top: 1px solid var(--border);
  pointer-events: none;
}
.map-status .ms-item { display: inline-flex; align-items: baseline; gap: 4px; white-space: nowrap; }
.map-status .ms-label { opacity: 0.65; }
.map-status .ms-value {
  color: var(--text);
  /* Tabular figures stop the readout jittering as the cursor moves. */
  font-variant-numeric: tabular-nums;
  min-width: 4.5em;
}
.map-status .ms-item:last-of-type .ms-value { min-width: 2.5em; }
.map-status .ms-spacer { flex: 1; }
.map-status .ms-attr { opacity: 0.8; pointer-events: auto; }
.map-status .ms-attr a { color: inherit; text-decoration: underline; }

/* Keep the placeholder note clear of the status bar. */
.map-note { margin-top: -12px; }

/* MapLibre's own controls sit above the status bar. */
.maplibregl-ctrl-bottom-left, .maplibregl-ctrl-bottom-right { display: none; }

/* ---------- Traffic separation overlay ---------- */

.map-overlays {
  position: absolute; left: 10px; top: 10px; z-index: 6;
  display: flex; flex-direction: column; gap: 6px;
}
.overlay-toggle {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 10px; border-radius: var(--radius);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  border: 1px solid var(--border-strong);
  font-size: 12px; color: var(--text); cursor: pointer; user-select: none;
}
.overlay-toggle:hover { border-color: var(--accent); }
.overlay-toggle input { accent-color: var(--accent); cursor: pointer; margin: 0; }
.overlay-toggle input:disabled { cursor: progress; }

/* ---------- Picking coordinates off the map ---------- */

.map-hint {
  align-self: flex-start;
  padding: 4px 9px; border-radius: var(--radius);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  border: 1px dashed var(--border-strong);
  font-size: 12px; color: var(--text-muted);
  margin: 0; pointer-events: none; user-select: none;
}
.map-hint[hidden] { display: none; }

/* The menu only appears where a point could mean more than one thing, which
   today is the sea route's two ends. */
.pick-menu {
  position: absolute; z-index: 8; min-width: 150px;
  padding: 4px; margin: 0; list-style: none;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius); box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  font-size: 13px;
}
.pick-menu li { margin: 0; }
.pick-menu button {
  display: block; width: 100%; text-align: left;
  padding: 6px 10px; border: 0; border-radius: calc(var(--radius) - 2px);
  background: transparent; color: var(--text);
  font: inherit; cursor: pointer;
}
.pick-menu button:hover, .pick-menu button:focus-visible {
  background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent);
  outline: none;
}
.pick-menu .pick-menu-coords {
  padding: 4px 10px 6px; margin: 0;
  font-family: 'Inconsolata', ui-monospace, monospace;
  font-size: 12px; color: var(--text-muted);
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}

/* A field that was just filled from the map, so the change is visible even
   when it is scrolled away from the pointer. */
@keyframes pick-flash {
  from { background: color-mix(in srgb, var(--accent) 18%, transparent); border-color: var(--accent); }
  to   { background: var(--bg); border-color: var(--border-strong); }
}
.field-picked input { animation: pick-flash 1.1s ease-out; }

.seamark-note {
  /* Sits above the status bar so the disclaimer is visible with the data,
     not buried somewhere the user has to go looking for it. */
  position: absolute; left: 10px; right: 10px; bottom: 30px; z-index: 6;
  padding: 7px 11px; border-radius: var(--radius);
  background: var(--warn-bg); color: var(--warn);
  border-left: 3px solid var(--warn);
  font-size: 12px; line-height: 1.45;
}
.seamark-note[hidden] { display: none; }
.seamark-note strong { font-weight: 700; }

/* ---------- Cost confirmation ---------- */

.cost-confirm {
  background: var(--warn-bg); border-left: 3px solid var(--warn);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 9px 12px; margin: 4px 0 16px;
}
.cost-confirm label { display: flex; gap: 9px; align-items: flex-start; cursor: pointer; }
.cost-confirm input { margin-top: 3px; accent-color: var(--warn); cursor: pointer; flex: 0 0 auto; }
.cost-confirm span { font-size: 12.5px; line-height: 1.5; color: var(--warn); font-weight: 400; }
.cost-confirm strong { font-weight: 700; }
.cost-confirm em { display: block; margin-top: 3px; font-style: normal; opacity: 0.85; }

.popup-role {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--accent); margin-bottom: 4px;
}

/* ---------- Collapsible JSON ---------- */

.json-block {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius);
}
.json-head {
  display: flex; align-items: baseline; gap: 10px;
  padding: 8px 12px; cursor: pointer; user-select: none;
  font-size: 12px; list-style: none;
}
/* The default disclosure triangle is replaced with one that matches the
   tree nodes below, so the panel and its contents read as one control. */
.json-head::-webkit-details-marker { display: none; }
.json-head::before {
  content: '▸'; color: var(--text-muted); font-size: 10px;
  transition: transform 0.12s ease; display: inline-block;
}
.json-block[open] > .json-head::before { transform: rotate(90deg); }
.json-head:hover { color: var(--accent); }
.json-title { font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted); }
.json-info { color: var(--text-muted); font-family: 'Inconsolata', monospace; }

.json-views { border-top: 1px solid var(--border); }

.json-tabs { display: flex; gap: 2px; padding: 8px 12px 0; }
.jv-radio { position: absolute; opacity: 0; pointer-events: none; }
.jv-tab {
  font-size: 11px; padding: 3px 10px; border-radius: var(--radius) var(--radius) 0 0;
  color: var(--text-muted); cursor: pointer; border: 1px solid transparent;
}
.jv-tab:hover { color: var(--accent); }
#jv-tree:checked ~ .json-tabs .jv-tab-tree,
#jv-raw:checked ~ .json-tabs .jv-tab-raw {
  color: var(--accent); background: var(--bg);
  border-color: var(--border); border-bottom-color: transparent;
}
.jv-radio:focus-visible ~ .json-tabs .jv-tab { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Only the checked view is shown; no script involved. */
.json-tree, .raw-view { display: none; }
#jv-tree:checked ~ .json-tree { display: block; }
#jv-raw:checked ~ .raw-view { display: block; }

.json-tree {
  font-family: 'Inconsolata', ui-monospace, monospace;
  font-size: 13px; line-height: 1.55;
  padding: 10px 14px 14px; overflow-x: auto;
}

.j-node { display: block; }
.j-summary {
  cursor: pointer; list-style: none; display: block;
  border-radius: 3px; margin-left: -4px; padding-left: 4px;
}
.j-summary::-webkit-details-marker { display: none; }
.j-summary::before {
  content: '▸'; display: inline-block; width: 1em;
  color: var(--text-muted); font-size: 10px;
  transition: transform 0.12s ease;
}
.j-node[open] > .j-summary::before { transform: rotate(90deg); }
.j-summary:hover { background: var(--surface-2); }

/* Indent one level per nesting depth, in place of literal whitespace. */
.j-body { padding-left: 1.25em; border-left: 1px solid var(--border); margin-left: 0.35em; }
.j-line { white-space: pre; padding-left: 1em; }
.j-close { padding-left: 1em; }

/* The count stands in for the contents while a node is collapsed. */
.j-preview {
  color: var(--text-muted); font-size: 11px; opacity: 0.8;
  margin: 0 6px; font-style: italic;
}
.j-node[open] > .j-summary .j-preview,
.j-node[open] > .j-summary .j-when-closed { display: none; }
.j-node:not([open]) > .j-summary .j-preview { display: inline; }

pre.raw-view {
  font-family: 'Inconsolata', ui-monospace, monospace; font-size: 13px;
  line-height: 1.55; padding: 12px 14px; overflow-x: auto; white-space: pre;
  color: var(--text);
}

/* ---------- Repeatable inputs ---------- */

.field textarea {
  width: 100%; padding: 7px 10px; resize: vertical;
  font-family: 'Inconsolata', ui-monospace, monospace; font-size: 14px;
  color: var(--text); background: var(--bg);
  border: 1px solid var(--border-strong); border-radius: var(--radius);
}
.field textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
}

.check-row { display: flex; flex-wrap: wrap; gap: 6px 14px; padding: 3px 0; }
.check-item {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; font-weight: 400; cursor: pointer; margin: 0;
}
.check-item input { accent-color: var(--accent); cursor: pointer; margin: 0; }

/* ---------- Demo mode ---------- */

.pill-demo { background: var(--ok-bg); color: var(--ok); border-color: transparent; }

.btn-small {
  padding: 3px 10px; font-size: 12px; border-radius: var(--radius);
  border: 1px solid var(--border-strong); color: var(--text);
}
.btn-small:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }

/* A locked endpoint is listed but muted: visible enough to show the data
   exists, quiet enough not to look like the way in. */
.nav-link.locked .nav-link-name { opacity: 0.65; }
.nav-lock { font-size: 11px; opacity: 0.5; }

.locked-note {
  background: var(--surface); border: 1px dashed var(--border-strong);
  border-radius: var(--radius); padding: 14px; margin-top: 4px;
}
.locked-note p { font-size: 13px; color: var(--text-muted); margin-bottom: 12px; }
.locked-note strong { color: var(--text); }

.alert-locked {
  background: var(--surface); border: 1px solid var(--border-strong);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 12px 14px; font-size: 13px; color: var(--text-muted);
}
.alert-locked strong { color: var(--text); }
.alert-locked p + .alert-actions { margin-top: 10px; }
.alert-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* What today's demo will answer for this endpoint. Plain links, so choosing
   one prefills the form the same way a shared URL does. */
.demo-examples {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin: 0 0 16px;
}
.demo-examples.inline { margin: 6px 0 0; }
.demo-examples-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--text-muted); margin-right: 2px;
}
.demo-chip {
  display: inline-block; max-width: 100%;
  padding: 3px 9px; border-radius: 999px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  font-size: 12px; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
a.demo-chip:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
/* The one already in the form is shown, not offered. */
.demo-chip.current {
  border-color: var(--accent); color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.alert-sub { margin-top: 10px; font-size: 12px; color: var(--text-muted); }

/* One line saying what may be changed on this endpoint. The chips below are
   somewhere to start; this describes the whole space. */
.demo-scope {
  display: flex; gap: 7px; align-items: baseline;
  margin: 0 0 10px; padding: 8px 11px;
  background: var(--surface); border-left: 2px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 12.5px; color: var(--text-muted); line-height: 1.5;
}
.demo-scope-mark { color: var(--accent); font-size: 9px; }

/* ---------- Placeholders and form actions ---------- */

/* Placeholders were reading as prefilled values: same monospace face, same
   size, and on the identifier fields the content is a plausible MMSI. Italic
   and much lighter separates "here is the shape of an answer" from "here is
   an answer". The requirement itself is in the help text under each field, so
   nothing load-bearing depends on reading these. */
.field input::placeholder,
.field textarea::placeholder {
  font-style: italic;
  color: var(--placeholder);
  opacity: 1; /* Firefox dims placeholders by default; the colour already does it */
}

.form-actions { display: flex; align-items: center; gap: 8px; }

/* Quiet next to Send: clearing the form is a step back, not the point of the
   page. */
.btn-ghost {
  background: transparent; color: var(--text-muted);
  border-color: var(--border-strong);
}
.btn-ghost:hover { color: var(--accent); border-color: var(--accent); }

/* --- standalone notice page -----------------------------------------
 * Sign-in outcomes that cannot continue into the app: no plan on the
 * account, an expired flow, an issuer that refused. Deliberately plain —
 * it is a dead end with one or two ways out, not a place to linger.
 */
.notice-page {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 8vh 20px 40px;
}

.notice-card {
  width: 100%;
  max-width: 560px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  padding: 28px 28px 24px;
}

.notice-card h1 {
  /* h1 is monospace globally because it names an endpoint. This one is a
     sentence, so it goes back to the body face. */
  font-family: 'Khula', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  margin: 0 0 10px;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.notice-detail {
  margin: 0 0 22px;
  color: var(--text-muted);
  line-height: 1.55;
}

.notice-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}

/* Signed in with the customer's own key: their credits, their plan's limits. */
.pill-ok { background: var(--ok-bg); color: var(--ok); border-color: transparent; }

/* Sign-out is a POST so it can be CSRF-checked, which means a form in the
   topbar. It should sit in the row like any other control. */
.signout { display: inline-flex; margin: 0; }

/* ===================================================================== *
 * Welcome screen
 *
 * The scene's palette follows the sun at the place today's demo is set,
 * not the visitor's theme: the Bosphorus at seven in the evening is dusk
 * whoever is looking. data-sky is stamped on <html> by the server.
 *
 * The cards keep following the theme, so the two are independent.
 * ===================================================================== */

:root {
  --sky-top: #8ecbec; --sky-bot: #e9f6fd; --sun: #ffd978; --sun-ray: #ffe9ad;
  --cloud: #ffffff; --cloud-2: #eaf4fb;
  --sea-far: #57adde; --sea-mid: #3d92cb; --sea-near: #2b78ae; --wave: #ffffff;
  --rock: #8b93a8; --rock-dk: #6d7590;
  --tower-lt: #fdfdff; --tower-dk: #e35d5d; --lamp: #ffd35b; --beam: #ffe9ad;
  --wood: #c08f5c; --wood-dk: #9a6f45; --hull: #37456b; --deckhouse: #f4f6fb;
  --bird: #4a5570; --star: transparent;
  --beam-o: 0.18; --ray-o: 0.55; --glow-o: 0.35;
  --octo: #a175d8; --octo-lt: #b891e4; --octo-arm: #8b5fc9; --octo-suck: #d8bdf2;
  --sand: #f0dcaa; --sand-dk: #e0c68b; --shell: #d9b7c6; --crab: #e0685f;
  --hero-ink: #14294a; --hero-ink-muted: #26436d; --hero-shadow: none;
  --octo-k: 1;
}

:root[data-sky="dusk"] {
  --sky-top: #44357e; --sky-bot: #f7b487; --sun: #ffcf6e; --sun-ray: #ffd9a0;
  --cloud: #d9c0ea; --cloud-2: #c3a5dd;
  --sea-far: #3f5f9e; --sea-mid: #314a83; --sea-near: #25386a; --wave: #9dc0ea;
  --rock: #4c4770; --rock-dk: #3a3559;
  --tower-lt: #f4efff; --tower-dk: #dd6360; --lamp: #ffe08a; --beam: #ffe3a5;
  --wood: #7b6291; --wood-dk: #5e4a72; --hull: #221f45; --deckhouse: #e8e1f6;
  --bird: #2e2952;
  --beam-o: 0.3; --ray-o: 0.7; --glow-o: 0.5;
  --octo: #8c62c4; --octo-lt: #a37ad6; --octo-arm: #75509f; --octo-suck: #c4a4e4;
  --sand: #dfb794; --sand-dk: #c89a76; --shell: #c79bb4; --crab: #d2605c;
  --hero-ink: #f7f4ff; --hero-ink-muted: #d7d0ee;
  --hero-shadow: 0 2px 22px rgba(10, 6, 30, 0.55);
}

:root[data-sky="night"] {
  --sky-top: #0B0A18; --sky-bot: #241e4e; --sun: #ded8f0; --sun-ray: #8f86c4;
  --cloud: #2b2554; --cloud-2: #221c46;
  --sea-far: #182448; --sea-mid: #111c3a; --sea-near: #0c142b; --wave: #41609e;
  --rock: #2b2845; --rock-dk: #201e36;
  --tower-lt: #ccc6e4; --tower-dk: #9b4e57; --lamp: #ffe08a; --beam: #ffe3a5;
  --wood: #3d3460; --wood-dk: #2c2549; --hull: #161334; --deckhouse: #b9b1d8;
  --bird: #3b3566; --star: #ffffff;
  --beam-o: 0.4; --ray-o: 0.25; --glow-o: 0.75;
  --octo: #6d4fa6; --octo-lt: #805fbc; --octo-arm: #5a3f8a; --octo-suck: #9f83c9;
  --sand: #332d54; --sand-dk: #282246; --shell: #4b4070; --crab: #9b4e57;
  --hero-ink: #f7f4ff; --hero-ink-muted: #d7d0ee;
  --hero-shadow: 0 2px 22px rgba(10, 6, 30, 0.55);
}

/* --- hero ------------------------------------------------------------ */

.welcome .hero { position: relative; }

/* No fixed height and no crop: the band keeps its natural 1600:520, so
   nothing is ever sliced off whatever the window is doing. */
.welcome .hero-art { display: block; width: 100%; height: auto; }

.welcome .hero-copy {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center;
  padding-top: 4.5vw; text-align: center; pointer-events: none;
}
.welcome .hero-copy h1 {
  font-family: 'Khula', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  margin: 0; font-size: clamp(26px, 3.6vw, 52px); font-weight: 700;
  letter-spacing: -0.01em; color: var(--hero-ink); text-shadow: var(--hero-shadow);
}
.welcome .hero-copy p {
  margin: 10px 0 0; font-size: clamp(14px, 1.35vw, 19px); max-width: 38ch;
  color: var(--hero-ink-muted); text-shadow: var(--hero-shadow);
}

/* --- the two cards --------------------------------------------------- */

.welcome .choice {
  position: relative; z-index: 3;
  max-width: 1120px; margin: -18px auto 0; padding: 0 24px 64px;
  display: grid; grid-template-columns: 1fr 1fr;
  column-gap: 46px; row-gap: 0; align-items: stretch;
}

.welcome .panel-wrap { position: relative; display: flex; }
.welcome .panel-wrap > .panel { flex: 1; z-index: 2; }

.welcome .panel {
  position: relative; background: var(--surface);
  border: 1px solid var(--border-strong); border-radius: 16px;
  padding: 26px 26px 24px; display: flex; flex-direction: column;
}
.welcome .panel-anon { border-top: 3px solid var(--accent); }
.welcome .panel-key { border-top: 3px solid var(--ok); }

.welcome .eyebrow {
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.09em;
  color: var(--text-muted); font-weight: 600; margin: 0 0 6px;
}
.welcome .panel h2 {
  font-family: 'Khula', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  margin: 0 0 8px; font-size: 22px; font-weight: 700; letter-spacing: -0.01em;
}
.welcome .lede { margin: 0 0 18px; color: var(--text-muted); font-size: 15px; line-height: 1.55; }

.welcome .today {
  border: 1px solid var(--border); border-radius: 12px; padding: 14px 15px 15px;
  background: var(--bg); margin-bottom: 18px;
}
.welcome .today-head {
  display: flex; align-items: center; gap: 8px; margin-bottom: 12px;
  font-size: 13px; color: var(--text-muted); flex-wrap: wrap;
}
.welcome .today-head .dot {
  width: 7px; height: 7px; border-radius: 99px; background: var(--ok); flex: none;
}
.welcome .today-place { font-weight: 700; color: var(--text); font-size: 15px; }
.welcome .today-row { display: flex; gap: 9px; margin-bottom: 10px; font-size: 14px; }
.welcome .today-row:last-child { margin-bottom: 0; }
.welcome .today-key { color: var(--text-muted); flex: none; width: 62px; padding-top: 3px; }
.welcome .today-val { display: flex; flex-wrap: wrap; gap: 6px; }
.welcome .plain { color: var(--text-muted); font-size: 14px; padding-top: 2px; }

.welcome .chip {
  display: inline-block; font-family: 'Inconsolata', ui-monospace, monospace;
  font-size: 13px; padding: 2px 9px; border-radius: 99px; text-decoration: none;
  border: 1px solid var(--border-strong); color: var(--text); background: var(--surface);
}
.welcome .chip:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }

.welcome .ticks { list-style: none; margin: 0 0 20px; padding: 0; }
.welcome .ticks li {
  position: relative; padding-left: 24px; margin-bottom: 9px;
  font-size: 14.5px; color: var(--text-muted); line-height: 1.5;
}
.welcome .ticks li::before {
  content: "\2713"; position: absolute; left: 2px; top: -1px;
  color: var(--ok); font-weight: 700;
}

.welcome .spacer { flex: 1; }
.welcome .panel .btn { width: 100%; justify-content: center; padding: 11px 20px; font-size: 15px; }
.welcome .sub { margin: 10px 0 0; text-align: center; font-size: 13px; color: var(--text-muted); }
.welcome .fineprint {
  margin: 14px 0 0; font-size: 12.5px; color: var(--text-muted); line-height: 1.5;
}

/* The two cards share the parent's row tracks, so every band is the height of
   the taller side and the buttons stay level whatever the copy does. Padding
   one out with a blank line would hold only until the text reflowed. */
@media (min-width: 901px) {
  @supports (grid-template-rows: subgrid) {
    .welcome .choice { grid-template-rows: auto auto auto auto 1fr auto auto; }
    .welcome .panel-wrap,
    .welcome .panel-wrap > .panel,
    .welcome .choice > .panel {
      grid-row: 1 / -1; display: grid; grid-template-rows: subgrid;
    }
  }
}

/* --- the octopus ------------------------------------------------------ *
 * Split across the card's z-order: the head sits behind it and is cropped
 * by its top edge, the gripping arms are drawn in front. He is hauling
 * himself out of the water onto the card, and he is small on purpose.
 */
.welcome .octo { position: absolute; pointer-events: none; }
.welcome .octo-body {
  z-index: 1; right: calc(24px * var(--octo-k));
  top: calc(-68px * var(--octo-k)); width: calc(78px * var(--octo-k));
}
.welcome .octo-arms {
  z-index: 3; right: calc(24px * var(--octo-k));
  top: calc(-14px * var(--octo-k)); width: calc(78px * var(--octo-k));
}

/* --- the beach -------------------------------------------------------- */
.welcome .beach { display: block; width: 100%; height: auto; }

@media (max-width: 900px) {
  .welcome .choice { grid-template-columns: 1fr; margin-top: 8px; row-gap: 22px; }
  /* Below this width the band is too short to carry type over it, so the
     headline moves out of the overlay rather than shrinking into nothing. */
  .welcome .hero-copy { position: static; padding: 26px 20px 12px; }
  .welcome .hero-copy h1 { color: var(--text); text-shadow: none; }
  .welcome .hero-copy p { color: var(--text-muted); text-shadow: none; }
  :root { --octo-k: 0.82; }
}

/* The topbar is fixed, and only .app reserves room for it. Standalone pages
   have to do the same or their first element slides underneath. */
body.welcome,
body.notice { padding-top: var(--topbar-h); height: auto; }

/* --- motifs ----------------------------------------------------------- *
 * Line art for a panel with nothing in it. Deliberately quiet: it shares
 * the space with a map and a JSON tree, and has to vanish from attention
 * the moment a real response lands next to it.
 */
.motif {
  display: flex; flex-direction: column; align-items: center;
  padding: 52px 24px 56px; text-align: center; color: var(--text-muted);
}
.motif svg { width: 88px; height: auto; opacity: 0.55; }
.motif p {
  margin: 18px 0 0; font-size: 14px; line-height: 1.55; max-width: 40ch;
}

/* On the notice pages the motif is the page's subject rather than a filler,
   so it carries a little more weight. */
.notice-card .motif { padding: 4px 0 18px; }
.notice-card .motif svg { width: 72px; opacity: 0.6; }

/* A demo chip that combines rather than replaces: pressed means the value is
   in the field. The pair reads as one control language — a chip is either on
   or off — while the behaviour follows the parameter: pick one vessel on most
   endpoints, pick several on bulk tracking. */
.demo-chip.toggle[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
}
.demo-chip.toggle[aria-pressed="true"]:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: var(--accent-contrast);
}
