/* ============================================================
   PROJECT CPU — UI v2 stylesheet
   Single source of truth for tokens + components.
   Loaded by index.html (the app) and design-system.html (the docs),
   so the documentation can never drift from the product.

   Sections:
     1. Tokens
     2. Shape
     3. Base
     4. Typography utilities
     5. Chrome — topbar
     6. Chrome — statusbar
     7. Canvas + widget chrome
     8. Components — buttons
     9. Components — form controls
    10. Components — data display
    11. Components — feedback
    12. Responsive
   ============================================================ */

/* ============================================================
   1. TOKENS
   ============================================================ */
:root {
  /* --- surfaces --- */
  --bg:#000000;        /* page / deepest well */
  --bg2:#0a0a0a;       /* chrome + widget surface */
  --bg3:#101010;       /* raised row, hover fill */
  --bg-well:#1e1e1c;   /* inset track (progress bars, sliders) */

  /* --- type. High contrast by default (Coinbase-style): body text
     ~17:1 on black, secondary never below ~8:1. Pure white is the
     normal colour for a value or a title, not an exception. --- */
  --fg:#e6e6df;        /* body text            16.7:1 */
  --fg-bright:#ffffff; /* values, titles       21:1   */
  --label:#c6c6bd;     /* keys, captions       13.8:1 */
  --muted:#a0a098;     /* de-emphasis floor     8:1 — darkest readable grey */
  --ghost:#6b6b64;     /* disabled only — deliberately sub-threshold, ~3.9:1 */

  /* --- accent — Robinhood lime --- */
  --accent:#ccff00;
  --accent-hi:#ddff44;              /* hover */
  --accent-dim:#7a9900;             /* pressed / low-emphasis text */
  --accent-glow:rgba(204,255,0,0.07);
  --on-accent:#000;                 /* ink on any accent/semantic fill */

  /* --- semantic: meaning is fixed, never used decoratively --- */
  --green:#00e08a;     /* up, online, settled */
  --red:#ff4455;       /* down, destructive, failed */
  --amber:#ffaa00;     /* warning, pending, degraded */

  /* --- categorical: meaning-free hues for chart series and entity
     coding (resources, buildings, transports, syndicates).
     Series order: lime → cyan → purple → magenta → blue.
     Picked to sit at similar luminance on black (~8:1). --- */
  --cyan:#45d8c0;
  --purple:#b18cff;
  --magenta:#ff6ad5;
  --blue:#58a6ff;

  /* --- lines --- */
  --border:#1f1f1c;         /* every hairline in the app */
  --border-accent:#2b3610;  /* hairline on an accent-adjacent surface */

  /* --- type scale: seven roles, integer px. Sizes live only here —
     components reference the role, so a global bump is a one-line
     change instead of a hunt. --- */
  --fs-micro:11px;    /* tracked uppercase keys, tags */
  --fs-caption:12px;  /* widget titles, captions, table headers */
  --fs-small:13px;    /* secondary text, inputs, table cells */
  --fs-value:14px;    /* metric values, group labels */
  --fs-body:15px;     /* body default, buttons */
  --fs-heading:18px;  /* section heading inside a body */
  --fs-display:22px;  /* brand lockup */

  /* --- tracking. Uppercase mono needs air; sentence case does not. --- */
  --ls-0:0;
  --ls-1:1.2px;   /* small uppercase */
  --ls-2:1.6px;   /* titles, brand */
  --ls-3:2px;     /* display */

  /* --- spacing: 2px base, nine steps, nothing in between --- */
  --sp-1:2px;  --sp-2:4px;  --sp-3:6px;  --sp-4:8px;  --sp-5:10px;
  --sp-6:12px; --sp-7:16px; --sp-8:20px; --sp-9:24px;

  /* --- grid system — the whole layout derives from these numbers.
     --row is overwritten by JS so it always equals one column's width:
     the module is a square, never a rectangle. --- */
  --cols:24;           /* canvas columns */
  --row:56px;          /* fallback until syncSquareGrid() runs */
  --gap:8px;           /* gutter between widgets inside the canvas */
  --chrome-gap:2px;    /* hairline seam between the three chrome blocks */
  --topbar-h:52px;     /* fixed chrome, never part of the grid */
  --statusbar-h:52px;  /* fixed chrome, never part of the grid */

  /* --- fonts --- */
  --font-sys:'IBM Plex Mono',ui-monospace,monospace;
  --font-accent:'Chakra Petch',sans-serif;

  /* --- motion: one duration for state, one for surfaces --- */
  --t-state:.12s;
  --t-surface:.2s;

  /* --- logo geometry — the mark's corner chamfer is 9.91 / 84.18 of its
     side. Reused as the shape language for every accent surface. --- */
  --chamfer:6px;
}

/* ============================================================
   2. SHAPE
   The mark's silhouette, applied to any accent element.
   Nothing in this UI is rounded; the chamfer is the only
   deviation from a right angle.
   ============================================================ */
.chamfered {
  clip-path:polygon(
    var(--chamfer) 0, calc(100% - var(--chamfer)) 0,
    100% var(--chamfer), 100% calc(100% - var(--chamfer)),
    calc(100% - var(--chamfer)) 100%, var(--chamfer) 100%,
    0 calc(100% - var(--chamfer)), 0 var(--chamfer)
  );
}

/* ============================================================
   3. BASE
   ============================================================ */
* { box-sizing:border-box; margin:0; padding:0; }

/* Only the app shell locks its height to the viewport. A plain document
   page (the design system, any future long page) must keep the normal
   document scroll — inheriting the lock leaves `body` overflowing at
   `height:100%` and makes the scroll container ambiguous. */
html:has(body.app), body.app { height:100%; }

body {
  background:var(--bg); color:var(--fg);
  font-family:var(--font-sys); font-size:var(--fs-body); line-height:1.5;
  font-variant-numeric:tabular-nums;
  -webkit-font-smoothing:antialiased;
}

a { color:inherit; text-decoration:none; }

/* The app shell: three edge-to-edge blocks stacked with a hairline seam.
   Only the middle one (the canvas) scrolls. */
body.app {
  display:flex; flex-direction:column;
  gap:var(--chrome-gap);
  overflow:hidden;
}

/* Scrollbars are chrome, not content: hairline track, no arrows. */
* { scrollbar-width:thin; scrollbar-color:color-mix(in srgb, var(--fg) 18%, var(--bg)) transparent; }
*::-webkit-scrollbar { width:8px; height:8px; }
*::-webkit-scrollbar-track { background:transparent; }
*::-webkit-scrollbar-thumb { background:color-mix(in srgb, var(--fg) 18%, var(--bg)); }
*::-webkit-scrollbar-thumb:hover { background:color-mix(in srgb, var(--fg) 28%, var(--bg)); }

/* Focus is always the accent, never a browser default. */
:focus-visible { outline:1px solid var(--accent); outline-offset:1px; }

/* ============================================================
   4. TYPOGRAPHY UTILITIES
   ============================================================ */
.t-display {
  font-family:var(--font-accent); font-weight:700;
  font-size:var(--fs-display); letter-spacing:var(--ls-2); text-transform:uppercase;
  color:var(--fg-bright);
}
.t-heading {
  font-family:var(--font-accent); font-weight:700;
  font-size:var(--fs-heading); letter-spacing:var(--ls-2); text-transform:uppercase;
  color:var(--fg-bright);
}
.t-title {           /* widget titles and anything at panel scale */
  font-size:var(--fs-caption); letter-spacing:var(--ls-2);
  text-transform:uppercase; color:var(--fg-bright);
}
.t-key {             /* the key half of a key/value pair */
  font-size:var(--fs-micro); letter-spacing:var(--ls-1);
  text-transform:uppercase; color:var(--label);
}
.t-value { font-size:var(--fs-value); font-weight:500; color:var(--fg-bright); }
.t-body  { font-size:var(--fs-body); color:var(--fg); }
.t-sub   { font-size:var(--fs-caption); color:var(--muted); }

.n-up   { color:var(--green); }
.n-dn   { color:var(--red); }
.n-warn { color:var(--amber); }

/* ============================================================
   5. CHROME — TOPBAR
   Two fixed bars: identity + actions on top, market telemetry at
   the bottom. Neither is a widget: they cannot be moved, resized
   or removed. The grid lives between them.
   ============================================================ */

/* Top-level blocks run edge to edge — no outer margin. They are
   separated from each other by a seam, not framed individually. */
.block {
  background:var(--bg2);
  border:none;
  border-top:1px solid var(--border);
}
.block:first-child { border-top:none; }

.topbar {
  flex:0 0 var(--topbar-h);
  display:flex; align-items:stretch;
  padding-left:var(--sp-7);
}

.brand {
  display:flex; align-items:center; gap:11px;
  padding-right:var(--sp-9);
  font-family:var(--font-accent); font-weight:700; font-size:var(--fs-display);
  color:var(--fg-bright); letter-spacing:var(--ls-2); white-space:nowrap;
  text-transform:uppercase;
}
.brand .mark { width:30px; height:30px; display:block; flex:0 0 auto; }
.brand .mark .plate { fill:var(--accent); }
.brand .mark .cut   { fill:var(--bg2); }

/* --- right side: view-as, socials, CTA --- */
.tb-right {
  margin-left:auto; display:flex; align-items:center; gap:var(--sp-6);
  padding:0 var(--sp-6) 0 var(--sp-7);
}

/* Icons rest light and hover lime. Grey icons on black read as
   dirt, not as chrome — rest state stays near body-text brightness.
   Per-icon size comes from the registry (optical alignment): a filled
   circle must overshoot a sparse letterform to read the same size. */
.socials { display:flex; gap:var(--sp-2); }
.soc {
  width:30px; height:30px; display:flex; align-items:center; justify-content:center;
  color:var(--fg); transition:color var(--t-state);
}
.soc:hover { color:var(--accent); }   /* colour only — no box, no background */
.soc svg { width:17px; height:17px; fill:currentColor; display:block; }
/* Raster mark tinted like the vectors: the artwork is a glyph on
   transparency, so the default alpha mask keeps its shape whatever its
   colour, then currentColor paints it. (Never mask by luminance here —
   a dark glyph would mask itself out and vanish.) */
.soc .raster {
  width:17px; height:17px; display:block; background:currentColor;
  -webkit-mask:var(--src) center/contain no-repeat;
          mask:var(--src) center/contain no-repeat;
}

/* ============================================================
   6. CHROME — STATUSBAR
   Market telemetry, grouped by subject. Each metric is a
   two-line key/value cell, exchange style.
   ============================================================ */
.statusbar {
  flex:0 0 var(--statusbar-h);
  display:flex; align-items:stretch;
  overflow:hidden;
}

/* dividers separate groups only — never individual metrics, and
   they stop short of the edges instead of running full height */
.grp { display:flex; align-items:stretch; min-width:0; }
.grp + .grp, .sb-right { position:relative; }
.grp + .grp::before, .sb-right::before {
  content:''; position:absolute; left:0; top:22%; bottom:22%;
  width:1px; background:var(--border);
}

/* group name: $CPU / NFT LAND */
.grp-l {
  display:flex; align-items:center;
  padding:0 var(--sp-7);
  font-family:var(--font-accent); font-weight:700; font-size:var(--fs-value);
  letter-spacing:var(--ls-2); color:var(--accent); white-space:nowrap;
  text-transform:uppercase;
}

/* one metric = key on top, value below */
.stat {
  display:flex; flex-direction:column; justify-content:center; gap:var(--sp-1);
  padding:0 var(--sp-7);
  white-space:nowrap;
}
.stat .k {
  font-size:var(--fs-micro); letter-spacing:var(--ls-1); color:var(--label);
  text-transform:uppercase;
}
.stat .v {
  font-size:var(--fs-value); font-weight:500; color:var(--fg-bright);
  display:flex; align-items:center; gap:var(--sp-4);
}
.stat .sub { font-size:var(--fs-caption); color:var(--muted); font-weight:400; }
.delta { font-size:var(--fs-small); font-weight:500; }
.delta.up { color:var(--green); }
.delta.dn { color:var(--red); }

/* online lamp, pinned right */
.sb-right {
  margin-left:auto; display:flex; align-items:center; gap:var(--sp-4);
  padding:0 var(--sp-9);
  font-size:var(--fs-small); letter-spacing:var(--ls-1); color:var(--fg);
}
.lamp {
  width:7px; height:7px; border-radius:50%;
  background:var(--green); box-shadow:0 0 7px color-mix(in srgb, var(--green) 80%, transparent);
  animation:pulse 2.4s ease-in-out infinite;
}
.lamp.warn { background:var(--amber); box-shadow:0 0 7px color-mix(in srgb, var(--amber) 80%, transparent); }
.lamp.down { background:var(--red);   box-shadow:0 0 7px color-mix(in srgb, var(--red) 80%, transparent); }
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.35} }

/* ============================================================
   7. CANVAS + WIDGET CHROME
   Widgets are NOT draggable yet (phase 2). The point of phase 1
   is that every widget is already size-agnostic, so turning drag
   on later is a library swap, not a rewrite.
   ============================================================ */
.canvas {
  flex:1; min-height:0; position:relative;
  display:grid;
  grid-template-columns:repeat(var(--cols),minmax(0,1fr));
  grid-auto-rows:var(--row);
  gap:var(--gap);
  padding:var(--gap);
  overflow:auto;
}

/* Grid guide overlay — one dot on each module's top-left corner, so a
   dot marks exactly where a widget placed at that module will start.
   Dev aid, key G.

   Alignment is load-bearing: the tile pitch must equal the module pitch
   (column width + gutter) and the origin must equal the canvas padding.
   The dot is drawn 1px into the tile, so the offset backs that out. */
.canvas.guides::before {
  content:''; position:absolute; inset:0; pointer-events:none; z-index:0;
  background-image:radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--accent) 16%, transparent) 1px, transparent 1.6px);
  background-size:calc(var(--row) + var(--gap)) calc(var(--row) + var(--gap));
  background-position:calc(var(--gap) - 1px) calc(var(--gap) - 1px);
  background-repeat:repeat;
}

/* --- widget chrome: every panel in the app is one of these --- */
/* corners cut with the logo's chamfer — the mark's silhouette applied
   at panel scale (see .chamfered) */
.w {
  --chamfer:11px;
  grid-column:span var(--cs,6);
  grid-row:span var(--rs,4);
  min-width:0; min-height:0;            /* size-agnostic: never push the grid */
  display:flex; flex-direction:column;
  position:relative; z-index:1;
  /* the outer fill IS the 1px frame; ::after lays the surface on top,
     inset by 1px. A plain border cannot follow a clip-path. */
  background:var(--border);
}
.w::after {
  content:''; position:absolute; inset:1px; z-index:0;
  background:var(--bg2); pointer-events:none;
  clip-path:polygon(
    calc(var(--chamfer) - 1px) 0, calc(100% - var(--chamfer) + 1px) 0,
    100% calc(var(--chamfer) - 1px), 100% calc(100% - var(--chamfer) + 1px),
    calc(100% - var(--chamfer) + 1px) 100%, calc(var(--chamfer) - 1px) 100%,
    0 calc(100% - var(--chamfer) + 1px), 0 calc(var(--chamfer) - 1px)
  );
}
/* Every child, not just head/body: ::after paints the surface over the
   frame, so anything left at the default z-index disappears beneath it.
   Scoping this to the two known children made a third one silently
   invisible — the widget must not care what you put in it. */
.w > * { position:relative; z-index:1; }
.w-head {
  flex:0 0 auto; display:flex; align-items:center; gap:var(--sp-4);
  padding:7px var(--sp-5);
  border-bottom:1px solid var(--border);
  cursor:default;                        /* becomes the drag handle in phase 2 */
}
.w-dot { width:8px; height:8px; background:var(--accent); flex:0 0 auto; }
.w-title {
  font-size:var(--fs-caption); letter-spacing:var(--ls-2); text-transform:uppercase;
  color:var(--fg-bright); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.w-acts { margin-left:auto; display:flex; gap:var(--sp-1); color:var(--label); }
.w-body { flex:1; min-height:0; overflow:auto; padding:var(--sp-5); }

.stub {
  height:100%; display:flex; align-items:center; justify-content:center;
  color:var(--muted); font-size:var(--fs-caption); letter-spacing:var(--ls-2);
  text-transform:uppercase;
}

/* empty state */
.empty {
  grid-column:1 / -1; grid-row:span 4;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:var(--sp-5);
  color:var(--muted); font-size:var(--fs-small); letter-spacing:var(--ls-2);
  text-align:center;
}
.empty .big {
  font-family:var(--font-accent); font-size:var(--fs-heading);
  color:var(--label); letter-spacing:var(--ls-3);
}

/* ============================================================
   8. COMPONENTS — BUTTONS
   Three weights only. Primary is the single lime surface on a
   screen; if two are visible, one of them is wrong.
   ============================================================ */
.btn {
  display:inline-flex; align-items:center; justify-content:center; gap:var(--sp-3);
  font-family:var(--font-accent); font-weight:700;
  font-size:var(--fs-body); letter-spacing:var(--ls-1); text-transform:uppercase;
  padding:9px var(--sp-7); border:1px solid transparent; background:none;
  color:var(--fg-bright); cursor:pointer; white-space:nowrap;
  transition:background var(--t-state), color var(--t-state), border-color var(--t-state);
}
.btn:disabled { color:var(--ghost); border-color:var(--border); background:none; cursor:not-allowed; }

.btn-primary { background:var(--accent); color:var(--on-accent); }
.btn-primary:hover:not(:disabled)  { background:var(--accent-hi); }
.btn-primary:active:not(:disabled) { background:var(--accent-dim); }

.btn-secondary { border-color:var(--border); color:var(--fg-bright); }
.btn-secondary:hover:not(:disabled)  { border-color:var(--accent-dim); color:var(--accent); }
.btn-secondary:active:not(:disabled) { background:var(--bg3); }

.btn-ghost { color:var(--label); padding-left:var(--sp-5); padding-right:var(--sp-5); }
.btn-ghost:hover:not(:disabled) { color:var(--accent); }

.btn-danger { border-color:color-mix(in srgb, var(--red) 30%, black); color:var(--red); }
.btn-danger:hover:not(:disabled) { background:var(--red); color:var(--on-accent); border-color:var(--red); }

.btn-sm { font-size:var(--fs-small); padding:5px var(--sp-5); }
/* full-width call to action — a form's submit, never a toolbar button */
.btn-block { display:flex; width:100%; padding-top:14px; padding-bottom:14px; letter-spacing:var(--ls-3); }
/* A block button keeps a surface when disabled. The outline-only disabled
   of a toolbar button reads as a stray rule at this width, not a control. */
.btn-block:disabled { background:var(--bg3); border-color:var(--border); }
.btn-block.running { background:var(--accent-dim); color:var(--fg-bright); cursor:progress; }

/* The topbar CTA is a primary button locked to the chamfered silhouette. */
.cta {
  --chamfer:6px;
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--accent); border:none; color:var(--on-accent);
  font-family:var(--font-accent); font-weight:700;
  font-size:var(--fs-body); letter-spacing:var(--ls-1); text-transform:uppercase;
  padding:9px 17px; cursor:pointer; white-space:nowrap;
  transition:background var(--t-state);
}
.cta:hover  { background:var(--accent-hi); }
.cta:active { background:var(--accent-dim); }

/* icon-only action, used in .w-acts */
.icon-btn {
  width:20px; height:20px; display:inline-flex; align-items:center; justify-content:center;
  background:none; border:none; color:var(--fg); cursor:pointer;
  transition:color var(--t-state);
}
.icon-btn:hover { color:var(--accent); }
.icon-btn svg { width:12px; height:12px; fill:currentColor; display:block; }

/* ============================================================
   9. COMPONENTS — FORM CONTROLS
   A control is a hairline box on the deepest surface. Focus
   turns the hairline lime; nothing glows, nothing rounds.
   ============================================================ */
.field {
  display:flex; align-items:stretch;
  border:1px solid var(--border); background:var(--bg);
}
.field label, .field .prefix {
  font-size:var(--fs-micro); letter-spacing:var(--ls-1); color:var(--label);
  padding:0 9px; border-right:1px solid var(--border);
  display:flex; align-items:center; text-transform:uppercase; white-space:nowrap;
}
.field input, .field select {
  background:none; border:none; outline:none;
  font-family:inherit; font-size:var(--fs-small); color:var(--fg-bright);
  padding:var(--sp-3) var(--sp-5); min-width:0; width:100%;
}
.field input::placeholder { color:var(--muted); }
.field:focus-within { border-color:var(--accent-dim); }
.field.invalid { border-color:var(--red); }

/* The topbar's VIEW AS box is a .field (the markup carries both classes);
   all it adds is a fixed input width — an address is a known length. */
.viewas input { width:188px; }

/* Stacked field — label above, control, then one line of help that the
   error message replaces. The inline .field above is for a terminal
   control with a prefix; this is the form variant, where the label has to
   be readable on its own and a mistake needs somewhere to be explained. */
.field-stack { display:block; }
.field-stack > label {
  display:block; margin-bottom:var(--sp-3);
  font-size:var(--fs-micro); letter-spacing:var(--ls-1);
  text-transform:uppercase; color:var(--label);
}
.field-stack > label .req { color:var(--accent); }
.field-stack input {
  width:100%; background:var(--bg); border:1px solid var(--border); color:var(--fg-bright);
  font-family:inherit; font-size:var(--fs-body); padding:11px var(--sp-6);
  outline:none; transition:border-color var(--t-state), background var(--t-state);
}
.field-stack input::placeholder { color:var(--muted); }
.field-stack input:focus { border-color:var(--accent); }
.field-stack .hint   { font-size:var(--fs-micro); color:var(--muted); margin-top:5px; line-height:1.5; }
.field-stack .errmsg { font-size:var(--fs-micro); color:var(--red); margin-top:5px; line-height:1.5; display:none; }
/* validity is a class on the wrapper, so the whole group answers together */
.field-stack.ok  input { border-color:var(--accent-dim); }
.field-stack.err input { border-color:var(--red); }
.field-stack.err .errmsg { display:block; }
.field-stack.err .hint   { display:none; }

/* Checkbox — the native input stays in the DOM for keyboard and forms and
   is painted over by the box, so focus and :checked keep working. */
.check { display:flex; align-items:flex-start; gap:var(--sp-5); padding:7px 0; cursor:pointer; }
.check + .check { border-top:1px solid var(--border); }
.check input { position:absolute; opacity:0; width:0; height:0; }
.check .box {
  flex:0 0 16px; width:16px; height:16px; margin-top:2px;
  border:1px solid var(--accent-dim);
  display:flex; align-items:center; justify-content:center;
  font-size:var(--fs-caption); color:var(--accent);
  transition:background var(--t-state), border-color var(--t-state);
}
.check input:checked + .box { background:color-mix(in srgb, var(--accent) 14%, transparent); border-color:var(--accent); }
.check input:checked + .box::after { content:'×'; font-size:15px; line-height:1; }
.check input:focus-visible + .box { outline:1px solid var(--accent); outline-offset:2px; }
.check .tt { flex:1; font-size:var(--fs-small); color:var(--fg-bright); line-height:1.4; }
.check .tt a { color:var(--accent); border-bottom:1px solid var(--accent-dim); }
.check .tt a:hover { border-color:var(--accent); }

/* segmented control — the only tab pattern in the app */
.seg { display:inline-flex; border:1px solid var(--border); }
.seg > button {
  background:none; border:none; cursor:pointer;
  font-family:inherit; font-size:var(--fs-caption); letter-spacing:var(--ls-1);
  text-transform:uppercase; color:var(--label);
  padding:var(--sp-3) var(--sp-6);
  transition:color var(--t-state), background var(--t-state);
}
.seg > button + button { border-left:1px solid var(--border); }
.seg > button:hover { color:var(--fg-bright); }
.seg > button[aria-selected="true"] { background:var(--accent); color:var(--on-accent); }

/* toggle */
.toggle {
  width:30px; height:14px; background:var(--bg-well); border:1px solid var(--border);
  position:relative; cursor:pointer; flex:0 0 auto;
  transition:background var(--t-state), border-color var(--t-state);
}
.toggle::after {
  content:''; position:absolute; top:1px; left:1px; width:10px; height:10px;
  background:var(--label); transition:transform var(--t-state), background var(--t-state);
}
.toggle[aria-pressed="true"] { border-color:var(--accent-dim); }
.toggle[aria-pressed="true"]::after { transform:translateX(16px); background:var(--accent); }

kbd {
  font-family:inherit; font-size:var(--fs-caption); border:1px solid var(--border);
  padding:1px var(--sp-3); color:var(--fg); background:var(--bg);
}

/* ============================================================
   10. COMPONENTS — DATA DISPLAY
   ============================================================ */

/* progress bar, inline inside a value line */
.bar {
  display:inline-block;   /* so it also renders outside a flex row */
  height:4px; background:var(--bg-well); width:52px; flex:0 0 auto;
  position:relative; overflow:hidden;
}
.bar > i { display:block; height:100%; background:var(--accent); }
.bar.alt > i,
.bar.cyan > i    { background:var(--cyan); }   /* second series (land vs token) */
.bar.purple > i  { background:var(--purple); }
.bar.magenta > i { background:var(--magenta); }
.bar.blue > i    { background:var(--blue); }
.bar.warn > i    { background:var(--amber); }
.bar.danger > i  { background:var(--red); }
.bar.wide { width:100%; }

/* tag / badge — status or entity class, never decoration.
   Border is the hue sunk to ~30% so the outline reads as a tint,
   not a second border colour to maintain. */
.tag {
  display:inline-flex; align-items:center; gap:var(--sp-2);
  font-size:var(--fs-micro); letter-spacing:var(--ls-1); text-transform:uppercase;
  padding:2px var(--sp-3); border:1px solid var(--border); color:var(--label);
}
.tag.on      { border-color:color-mix(in srgb, var(--accent) 30%, black);  color:var(--accent); }
.tag.good    { border-color:color-mix(in srgb, var(--green) 30%, black);   color:var(--green); }
.tag.warn    { border-color:color-mix(in srgb, var(--amber) 30%, black);   color:var(--amber); }
.tag.bad     { border-color:color-mix(in srgb, var(--red) 30%, black);     color:var(--red); }
.tag.cyan    { border-color:color-mix(in srgb, var(--cyan) 30%, black);    color:var(--cyan); }
.tag.purple  { border-color:color-mix(in srgb, var(--purple) 30%, black);  color:var(--purple); }
.tag.magenta { border-color:color-mix(in srgb, var(--magenta) 30%, black); color:var(--magenta); }
.tag.blue    { border-color:color-mix(in srgb, var(--blue) 30%, black);    color:var(--blue); }

/* key/value row — the default way to show a labelled number in a widget body */
.kv { display:flex; align-items:baseline; justify-content:space-between; gap:var(--sp-6); padding:var(--sp-3) 0; }
.kv + .kv { border-top:1px solid var(--border); }
.kv .k { font-size:var(--fs-caption); letter-spacing:var(--ls-1); text-transform:uppercase; color:var(--label); }
.kv .v { font-size:var(--fs-value); font-weight:500; color:var(--fg-bright); }

/* table — hairline rows, no vertical rules, sticky head */
.tbl { width:100%; border-collapse:collapse; }
.tbl th {
  position:sticky; top:0; background:var(--bg2); text-align:left;
  font-size:var(--fs-micro); font-weight:500; letter-spacing:var(--ls-1);
  text-transform:uppercase; color:var(--label);
  padding:var(--sp-3) var(--sp-4); border-bottom:1px solid var(--border);
}
.tbl td {
  padding:var(--sp-3) var(--sp-4); font-size:var(--fs-small); color:var(--fg);
  border-bottom:1px solid var(--border);
}
.tbl td.num { text-align:right; color:var(--fg-bright); }
.tbl tbody tr:hover td { background:var(--bg3); }

/* ============================================================
   11. COMPONENTS — FEEDBACK
   ============================================================ */
.note {
  display:flex; gap:var(--sp-5); padding:var(--sp-5) var(--sp-6);
  border:1px solid var(--border); border-left-width:2px;
  font-size:var(--fs-small); color:var(--fg); background:var(--bg);
}
.note.info   { border-left-color:var(--accent); }
.note.good   { border-left-color:var(--green); }
.note.warn   { border-left-color:var(--amber); }
.note.bad    { border-left-color:var(--red); }

/* skeleton — loading placeholder at the shape of the eventual content */
.skel {
  background:linear-gradient(90deg,
    color-mix(in srgb, var(--fg) 5%, var(--bg2)) 25%,
    color-mix(in srgb, var(--fg) 9%, var(--bg2)) 50%,
    color-mix(in srgb, var(--fg) 5%, var(--bg2)) 75%);
  background-size:200% 100%; animation:skel 1.4s linear infinite;
  height:10px; width:100%;
}
@keyframes skel { from{background-position:200% 0} to{background-position:-200% 0} }

/* ============================================================
   12. RESPONSIVE
   Metrics drop by priority, then the grid stops being a grid.
   Drag/resize is desktop-only by design.
   ============================================================ */
@media (max-width:1680px) { .stat.opt-3 { display:none; } }
@media (max-width:1460px) { .stat.opt-2 { display:none; } .viewas input { width:132px; } }
@media (max-width:1240px) { .stat.opt-1 { display:none; } }
@media (max-width:900px) {
  body.app { overflow:auto; }
  .topbar, .statusbar { flex-wrap:wrap; height:auto; flex:0 0 auto; }
  .tb-right { margin-left:0; }
  .canvas { grid-template-columns:1fr; }
  .w { grid-column:1 / -1; }
}

/* ============================================================
   WORLD MAP WIDGET
   ============================================================ */
.w-body.flush { padding:0; }

.globe-host { position:relative; width:100%; height:100%; overflow:hidden; }
.globe-host canvas { display:block; width:100%; height:100%; cursor:grab; }

.globe-status {
  position:absolute; inset:0; pointer-events:none;
  display:flex; align-items:center; justify-content:center;
  font-size:var(--fs-micro); letter-spacing:var(--ls-1);
  color:var(--label); text-transform:uppercase;
}

/* --- widget: CELL — camera plan over a detail readout --- */
.cd { display:flex; flex-direction:column; height:100%; min-height:0; }

/* the camera keeps a fixed share of the widget so the readout always
   has room; both halves scroll independently of the grid */
.cd-cam {
  flex:0 0 42%; min-height:0; position:relative;
  border-bottom:1px solid var(--border); background:#070707;
}
.cam-canvas { display:block; width:100%; height:100%; }

.cd-data { flex:1; min-height:0; overflow-y:auto; padding:var(--sp-5) var(--sp-6); }

.cd-row {
  display:flex; justify-content:space-between; align-items:baseline; gap:var(--sp-6);
  padding:var(--sp-2) 0;
}
.cd-row .k {
  flex:0 0 auto; font-size:var(--fs-micro); letter-spacing:var(--ls-1);
  text-transform:uppercase; color:var(--label);
}
.cd-row .v { text-align:right; font-size:var(--fs-small); color:var(--fg-bright); }
.cd-row .v.dim  { color:var(--muted); }
.cd-row .v.warn { color:var(--amber); }
.cd-row .v.you  { color:var(--accent); }

/* section rule — separates deposits and structure from the header block */
.cd-sec {
  margin:var(--sp-6) 0 var(--sp-3);
  padding-top:var(--sp-3);
  border-top:1px solid var(--border);
  font-size:var(--fs-micro); letter-spacing:var(--ls-1);
  text-transform:uppercase; color:var(--label);
}

.cd-res {
  display:flex; justify-content:space-between; gap:var(--sp-6);
  padding:var(--sp-2) 0;
  font-size:var(--fs-small);
}
.cd-res .n { color:var(--fg); }
.cd-res .q { color:var(--fg-bright); font-variant-numeric:tabular-nums; }

/* explains an absence — used where the chain genuinely has no data */
.cd-note {
  padding:var(--sp-3) 0;
  font-size:var(--fs-caption); line-height:1.6; color:var(--muted);
}

.cd-empty {
  height:100%; display:flex; align-items:center; justify-content:center;
  font-size:var(--fs-caption); color:var(--muted);
}

.cd-foot {
  margin-top:var(--sp-6); padding-top:var(--sp-4);
  border-top:1px solid var(--border);
  font-size:var(--fs-micro); letter-spacing:var(--ls-1); text-transform:uppercase;
}
.cd-foot a { color:var(--label); }
.cd-foot a:hover { color:var(--accent); }

/* ============================================================
   WIDGET — MARKET INDEX
   Dense list, no grouping — one flat sort. Columns collapse by
   priority via container queries, so the widget's own width drives
   the layout, not the viewport's. The 7D chart is pinned last and
   never collapses; the middle columns go first.
   ============================================================ */
.mk {
  container-type:inline-size;
  height:100%; display:flex; flex-direction:column; min-height:0;

  /* floor: asset, price, direction, chart */
  --tpl:minmax(72px,1.4fr) minmax(48px,1fr) 58px 62px;
}
@container (min-width:360px) {
  .mk { --tpl:minmax(72px,1.4fr) minmax(48px,1fr) 58px minmax(46px,.9fr) 62px; }
  .mk .c4 { display:block; }
}
@container (min-width:450px) {
  .mk { --tpl:minmax(72px,1.4fr) minmax(48px,1fr) 58px minmax(46px,.9fr) minmax(48px,.9fr) 62px; }
  .mk .c5 { display:block; }
}
@container (min-width:540px) {
  .mk { --tpl:minmax(72px,1.4fr) minmax(48px,1fr) 58px minmax(46px,.9fr) minmax(48px,.9fr) minmax(44px,.7fr) 62px; }
  .mk .c6 { display:block; }
}
.mk .c4, .mk .c5, .mk .c6 { display:none; }

/* toolbar */
.mk-bar {
  flex:0 0 auto; display:flex; align-items:center; gap:var(--sp-4);
  padding:var(--sp-3) var(--sp-4);
  border-bottom:1px solid var(--border);
}

/* header and rows share one template so they cannot drift */
.mk-head, .mk-row {
  display:grid; grid-template-columns:var(--tpl);
  align-items:center; gap:var(--sp-3);
  padding:0 var(--sp-4);
  border-left:2px solid transparent;   /* mirrors the row's selection bar */
}
.mk-head {
  flex:0 0 auto; height:22px;
  border-bottom:1px solid var(--border);
}
.mk-h {
  background:none; border:none; padding:0; cursor:pointer;
  font-family:inherit; font-size:var(--fs-micro); letter-spacing:var(--ls-1);
  text-transform:uppercase; color:var(--label); text-align:left;
  display:flex; align-items:center; gap:2px;
  transition:color var(--t-state);
}
.mk-h[disabled] { cursor:default; }
.mk-h:not([disabled]):hover { color:var(--fg-bright); }
.mk-h.on { color:var(--accent); }
.mk-h.mk-num { justify-content:flex-end; }
.mk-h.c7 { justify-content:flex-end; }
.mk-h i { font-style:normal; font-size:9px; }

.mk-scroll { flex:1; min-height:0; overflow-y:auto; }

.mk-row {
  width:100%; height:24px; background:none; border:none; cursor:pointer;
  font-family:inherit; text-align:left;
  border-bottom:1px solid var(--border);
  border-left:2px solid transparent;
  transition:background var(--t-state);
}
.mk-row:hover { background:var(--bg3); }
.mk-row.on { background:var(--accent-glow); border-left-color:var(--accent); }

.mk-name {
  font-size:var(--fs-small); color:var(--fg-bright);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.mk-num { font-size:var(--fs-small); color:var(--fg); text-align:right; }
.mk-sub { color:var(--muted); }

/* Uncoloured on purpose: 24H % already carries up/down, and a
   matching green line would encode it twice. */
.mk-chart { width:62px; height:15px; display:block; margin-left:auto; }
.mk-chart polyline { fill:none; stroke:var(--muted); stroke-width:1; vector-effect:non-scaling-stroke; }

/* ============================================================
   COMPONENT — DROPDOWN
   A native <select> hands its option list to the OS, which ignores
   every token here — light plate, system blue highlight, system
   font. So the listbox is ours.
   ============================================================ */
.dd { position:relative; }
.dd-btn {
  display:flex; align-items:center; gap:var(--sp-3);
  font-family:inherit; font-size:var(--fs-micro); letter-spacing:var(--ls-1);
  text-transform:uppercase; cursor:pointer;
  padding:3px var(--sp-4); background:var(--bg);
  border:1px solid var(--border); color:var(--fg-bright);
  transition:border-color var(--t-state);
}
.dd-btn:hover { border-color:color-mix(in srgb, var(--accent) 40%, black); }
.dd-btn i { font-style:normal; font-size:9px; color:var(--muted); }
.dd.open .dd-btn { border-color:var(--accent); color:var(--accent); }
.dd.open .dd-btn i { color:var(--accent); }

.dd-menu {
  display:none; position:absolute; z-index:20; top:calc(100% + 2px); left:0;
  min-width:100%; background:var(--bg2);
  border:1px solid color-mix(in srgb, var(--accent) 30%, black);
}
.dd.open .dd-menu { display:block; }
.dd-opt {
  display:block; width:100%; text-align:left;
  font-family:inherit; font-size:var(--fs-micro); letter-spacing:var(--ls-1);
  text-transform:uppercase; cursor:pointer; white-space:nowrap;
  padding:5px var(--sp-5); background:none; border:none; color:var(--fg);
  transition:background var(--t-state), color var(--t-state);
}
.dd-opt:hover { background:var(--bg3); color:var(--fg-bright); }
.dd-opt.on { color:var(--accent); background:var(--accent-glow); }

/* caption in a widget header — states what the numbers mean */
.w-note {
  font-size:var(--fs-micro); letter-spacing:var(--ls-1);
  color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
