/* ────────────────────────────────────────────────────────────────────────────
   Tessera operator console

   The visual idea is an instrument panel, not a dashboard. An operator opens
   this when something is wrong, so the job is to report the state of the estate
   truthfully and quickly.

   Two colours carry the whole semantic load, and they encode the distinction
   the architecture is built on:

     amber  = COMMITTED   what we have promised customers
     teal   = OBSERVED    what the hardware actually reports

   They are never used decoratively. If something is amber it is a promise; if
   it is teal it is a measurement. Everything else is greyscale.

   Type rule: if a machine produced the value, it is set in mono. Names,
   addresses, capacities, identifiers, fingerprints. Prose is Archivo. That
   division is the console's typographic signature and it is applied without
   exception.
   ──────────────────────────────────────────────────────────────────────────── */

@import url("fonts.css");

:root {
  --ground:      #0F141B;
  --surface:     #161D27;
  --surface-2:   #1C2531;
  --surface-3:   #232E3C;
  --rule:        #26313F;
  --rule-soft:   #1E2833;

  --ink:         #E4EBF3;
  --ink-dim:     #93A3B8;
  --ink-faint:   #64748B;

  --committed:   #E8A33D;
  --observed:    #45C9BC;
  --alert:       #F2555A;
  --alert-dim:   #7A2A2E;

  --focus:       #6FA8FF;

  --radius:      3px;
  --rail:        13.5rem;

  --font-ui:   "Archivo", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  color-scheme: dark;
}

/* An operator console is used in a dim room far more often than a bright one,
   so dark is the default and light is the deliberate opt-in. */
:root[data-theme="light"] {
  --ground:    #EEF1F5;
  --surface:   #FFFFFF;
  --surface-2: #F5F7FA;
  --surface-3: #E8ECF2;
  --rule:      #D3DAE3;
  --rule-soft: #E4E9EF;
  --ink:       #101720;
  --ink-dim:   #4A5768;
  --ink-faint: #7D8A9B;
  --committed: #A76708;
  --observed:  #0E7C72;
  --alert:     #C0272D;
  --alert-dim: #F3D3D4;
  color-scheme: light;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 1.5;
  font-feature-settings: "ss01";
  -webkit-font-smoothing: antialiased;
}

/* Every machine-produced value. */
.v, code, pre, input, select, textarea, .gauge-figure, td.num, th.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "zero" 1;
}

a { color: inherit; }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* ── Calibration marks ──────────────────────────────────────────────────────
   The bezel of a measuring instrument. Used on panel headers only, never as
   filler, so the device stays meaningful. */
.calibrated {
  position: relative;
}
.calibrated::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 5px;
  background-image: repeating-linear-gradient(
    to right,
    var(--rule) 0 1px,
    transparent 1px 9px
  );
  opacity: .7;
  pointer-events: none;
}

/* ── Shell ──────────────────────────────────────────────────────────────── */

.shell {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  min-height: 100vh;
}

.rail {
  background: var(--surface);
  border-right: 1px solid var(--rule);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
}

.brand {
  padding: 1.05rem 1.15rem .95rem;
  border-bottom: 1px solid var(--rule);
  display: flex;
  align-items: center;
  gap: .55rem;
}
.brand .mark { width: 26px; height: 26px; flex: none; }
.brand .naming { min-width: 0; }
.brand-mark {
  font-size: .95rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
}
.brand-sub {
  font-family: var(--font-mono);
  font-size: .66rem;
  color: var(--ink-faint);
  letter-spacing: .06em;
  margin-top: .18rem;
}

.nav { padding: .6rem .5rem; flex: 1; overflow-y: auto; }
.nav-group {
  font-size: .62rem;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding: .9rem .65rem .35rem;
  font-weight: 600;
}
.nav a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding: .42rem .65rem;
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--ink-dim);
  font-size: .8rem;
  border-left: 2px solid transparent;
}
.nav a:hover { background: var(--surface-2); color: var(--ink); }
.nav a[aria-current="page"] {
  background: var(--surface-2);
  color: var(--ink);
  border-left-color: var(--committed);
  font-weight: 600;
}
.nav .count {
  font-family: var(--font-mono);
  font-size: .68rem;
  color: var(--ink-faint);
}

.rail-foot {
  border-top: 1px solid var(--rule);
  padding: .7rem .9rem;
  font-size: .72rem;
  color: var(--ink-faint);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
}

.main { min-width: 0; display: flex; flex-direction: column; }

.topbar {
  border-bottom: 1px solid var(--rule);
  padding: 1rem 1.6rem;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  background: var(--ground);
  position: sticky;
  top: 0;
  z-index: 5;
}
.topbar h1 {
  margin: 0;
  font-size: 1.12rem;
  font-weight: 600;
  letter-spacing: -.01em;
}
.topbar .sub {
  font-family: var(--font-mono);
  font-size: .72rem;
  color: var(--ink-faint);
  margin-top: .12rem;
}

.content { padding: 1.4rem 1.6rem 4rem; }
.content > * + * { margin-top: 1.15rem; }

/* ── Panels ─────────────────────────────────────────────────────────────── */

.panel {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.panel-head {
  padding: .9rem 1.05rem .75rem;
  border-bottom: 1px solid var(--rule-soft);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.panel-head h2 {
  margin: 0;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.panel-body { padding: 1.05rem; }
.panel-body.flush { padding: 0; }

.grid { display: grid; gap: 1.15rem; }
.grid.two { grid-template-columns: repeat(auto-fit, minmax(21rem, 1fr)); }

/* ── The commitment gauge ───────────────────────────────────────────────────
   The console's signature instrument, and the one place the amber/teal pairing
   does real work.

     the track      the physical ceiling of the node
     amber fill     what we have committed to customers
     teal needle    what the hardware actually reports in use
     hatched zone   the region past 100% of physical, reachable only because
                    the operator allowed overcommit

   Reading it takes no legend: if the needle sits well left of the fill we have
   sold more than is being used, which is normal and is how the business works.
   If the needle crosses the fill, customers are using more than we sold them
   and the node is at risk. That is a glance, not a calculation. */

.gauge { display: block; }
.gauge + .gauge { margin-top: .85rem; }

.gauge-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .75rem;
  margin-bottom: .3rem;
}
.gauge-label {
  font-size: .68rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
}
.gauge-figure { font-size: .75rem; color: var(--ink-dim); }
.gauge-figure b { color: var(--committed); font-weight: 500; }
.gauge-figure i { color: var(--observed); font-style: normal; }

.gauge-track {
  position: relative;
  height: 15px;
  background: var(--surface-3);
  border: 1px solid var(--rule);
  border-radius: 2px;
  overflow: hidden;
}

/* calibration ticks every 10% of physical capacity */
.gauge-track::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(
    to right,
    transparent 0 calc(10% - 1px),
    var(--rule) calc(10% - 1px) 10%
  );
  pointer-events: none;
  z-index: 2;
}

.gauge-fill {
  position: absolute;
  inset: 0 auto 0 0;
  background: var(--committed);
  opacity: .8;
}
/* Capacity reachable only because overcommit was allowed. Hatched so it does
   not read as ordinary headroom, but deliberately NOT red: overcommit is the
   normal, intended state of a rental platform, and colouring three quarters of
   every bar as an alarm would train operators to ignore the one that matters. */
.gauge-over {
  position: absolute;
  inset: 0 auto 0 0;
  background-image: repeating-linear-gradient(
    45deg,
    var(--rule) 0 2px,
    transparent 2px 6px
  );
  opacity: .85;
  z-index: 1;
}

/* Where the promise stops. Drawn when the ceiling differs from physical in
   either direction -- an operator needs to see the limit they actually set. */
.gauge-ceiling {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--ink-faint);
  z-index: 2;
}
.gauge-ceiling::after {
  content: "";
  position: absolute;
  bottom: -1px;
  left: -2px;
  border-left: 2.5px solid transparent;
  border-right: 2.5px solid transparent;
  border-bottom: 4px solid var(--ink-faint);
}
.gauge-needle {
  position: absolute;
  top: -1px;
  bottom: -1px;
  width: 2px;
  background: var(--observed);
  z-index: 3;
  box-shadow: 0 0 0 1px var(--ground);
}
.gauge-needle::after {
  content: "";
  position: absolute;
  top: -3px;
  left: -2px;
  border-left: 3px solid transparent;
  border-right: 3px solid transparent;
  border-top: 4px solid var(--observed);
}
.gauge-scale {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: .6rem;
  color: var(--ink-faint);
  margin-top: .18rem;
}

.legend {
  display: flex;
  gap: 1.1rem;
  font-size: .7rem;
  color: var(--ink-faint);
  align-items: center;
  flex-wrap: wrap;
}
.legend span { display: inline-flex; align-items: center; gap: .35rem; }
.legend .sw { width: 11px; height: 3px; border-radius: 1px; }
.legend .sw.committed { background: var(--committed); }
.legend .sw.observed  { background: var(--observed); }

/* ── Tables ─────────────────────────────────────────────────────────────── */

table { width: 100%; border-collapse: collapse; font-size: .8rem; }
thead th {
  text-align: left;
  font-size: .62rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 700;
  padding: .6rem 1.05rem;
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}
tbody td {
  padding: .58rem 1.05rem;
  border-bottom: 1px solid var(--rule-soft);
  vertical-align: middle;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr.clickable { cursor: pointer; }
tbody tr.clickable:hover { background: var(--surface-2); }
th.num, td.num { text-align: right; }
.table-scroll { overflow-x: auto; }

/* ── Status ─────────────────────────────────────────────────────────────── */

.pill {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  font-family: var(--font-mono);
  font-size: .66rem;
  letter-spacing: .04em;
  padding: .12rem .45rem;
  border-radius: 2px;
  border: 1px solid var(--rule);
  color: var(--ink-dim);
  background: var(--surface-2);
  white-space: nowrap;
}
.pill::before {
  content: "";
  width: 5px; height: 5px;
  border-radius: 50%;
  background: currentColor;
}
.pill.ok      { color: var(--observed);  border-color: color-mix(in srgb, var(--observed) 35%, transparent); }
.pill.warn    { color: var(--committed); border-color: color-mix(in srgb, var(--committed) 35%, transparent); }
.pill.bad     { color: var(--alert);     border-color: color-mix(in srgb, var(--alert) 40%, transparent); }
.pill.idle    { color: var(--ink-faint); }
.pill.running { color: var(--focus);     border-color: color-mix(in srgb, var(--focus) 35%, transparent); }
.pill.running::before { animation: pulse 1.4s ease-in-out infinite; }

@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .25; } }

/* ── Forms ──────────────────────────────────────────────────────────────── */

.field { margin-bottom: 1.05rem; }
.field label {
  display: block;
  font-size: .7rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--ink-dim);
  margin-bottom: .3rem;
}
.field .hint {
  font-size: .74rem;
  color: var(--ink-faint);
  margin-top: .3rem;
  line-height: 1.45;
  font-family: var(--font-ui);
}
.field .err {
  font-size: .74rem;
  color: var(--alert);
  margin-top: .3rem;
  font-family: var(--font-ui);
}

input[type=text], input[type=email], input[type=password], input[type=number], select, textarea {
  width: 100%;
  padding: .5rem .6rem;
  background: var(--surface-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  color: var(--ink);
  font-size: .82rem;
}
input::placeholder { color: var(--ink-faint); }
input[aria-invalid="true"], select[aria-invalid="true"] { border-color: var(--alert); }

.check { display: flex; gap: .55rem; align-items: flex-start; }
.check input { margin-top: .2rem; width: auto; }
.check label { text-transform: none; letter-spacing: 0; font-size: .82rem; font-weight: 500; color: var(--ink); }

.btn {
  font-family: var(--font-ui);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .04em;
  padding: .48rem .95rem;
  border-radius: var(--radius);
  border: 1px solid var(--rule);
  background: var(--surface-2);
  color: var(--ink);
  cursor: pointer;
}
.btn:hover:not(:disabled) { background: var(--surface-3); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn.primary {
  background: var(--committed);
  border-color: var(--committed);
  color: #17120A;
}
.btn.primary:hover:not(:disabled) { filter: brightness(1.08); }
.btn.danger { color: var(--alert); border-color: color-mix(in srgb, var(--alert) 40%, transparent); }
.btn.small { font-size: .7rem; padding: .28rem .55rem; }
.btn-row { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }

/* ── Notices ────────────────────────────────────────────────────────────── */

.notice {
  border: 1px solid var(--rule);
  border-left: 2px solid var(--ink-faint);
  background: var(--surface-2);
  padding: .7rem .9rem;
  border-radius: var(--radius);
  font-size: .8rem;
  line-height: 1.5;
}
.notice.warn  { border-left-color: var(--committed); }
.notice.bad   { border-left-color: var(--alert); }
.notice.ok    { border-left-color: var(--observed); }
.notice h3 { margin: 0 0 .25rem; font-size: .82rem; font-weight: 700; }
.notice p  { margin: 0; color: var(--ink-dim); }
.notice + .notice { margin-top: .6rem; }

.empty {
  padding: 2.6rem 1.05rem;
  text-align: center;
  color: var(--ink-faint);
}
.empty h3 { margin: 0 0 .35rem; color: var(--ink-dim); font-size: .9rem; }
.empty p { margin: 0 auto 1.1rem; max-width: 34rem; font-size: .82rem; line-height: 1.55; }

/* ── Job log ────────────────────────────────────────────────────────────── */

.log {
  font-family: var(--font-mono);
  font-size: .72rem;
  line-height: 1.65;
  background: var(--ground);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: .7rem .85rem;
  max-height: 21rem;
  overflow-y: auto;
}
.log-line { display: flex; gap: .7rem; }
.log-line time { color: var(--ink-faint); flex: none; }
.log-line.warn  { color: var(--committed); }
.log-line.error { color: var(--alert); }

/* ── Sign in ────────────────────────────────────────────────────────────── */

.signin {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 1.5rem;
}
.signin-card {
  width: 100%;
  max-width: 22rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 1.6rem 1.5rem 1.5rem;
}
.signin-card .signin-mark { display: block; margin-bottom: .7rem; }
.signin-card .brand-mark { font-size: 1.05rem; }
.signin-card .brand-sub { margin-bottom: 1.5rem; }

/* ── Utility ────────────────────────────────────────────────────────────── */

.muted   { color: var(--ink-dim); }
.faint   { color: var(--ink-faint); }
.tiny    { font-size: .72rem; }
.nowrap  { white-space: nowrap; }
.stack   { display: flex; flex-direction: column; gap: .25rem; }
.row     { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.spread  { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.right   { margin-left: auto; }
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── Responsive ─────────────────────────────────────────────────────────── */

@media (max-width: 60rem) {
  .shell { grid-template-columns: 1fr; }
  .rail { position: static; height: auto; border-right: 0; border-bottom: 1px solid var(--rule); }
  .nav { display: flex; flex-wrap: wrap; gap: .2rem; }
  .nav-group { width: 100%; padding-top: .5rem; }
  .nav a { border-left: 0; border-bottom: 2px solid transparent; }
  .nav a[aria-current="page"] { border-left: 0; border-bottom-color: var(--committed); }
  .content, .topbar { padding-left: 1rem; padding-right: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ── Support banner ─────────────────────────────────────────────────────────
   Shown in the rail whenever the signed-in operator holds an open session
   inside a customer's organisation. Deliberately hard to ignore: acting inside
   someone else's account is not a state anyone should drift into and forget. */
.support-banner {
  display: block;
  margin: .6rem .5rem 0;
  padding: .5rem .65rem;
  border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--committed) 45%, transparent);
  background: color-mix(in srgb, var(--committed) 12%, transparent);
  color: var(--ink);
  text-decoration: none;
  font-size: .7rem;
  line-height: 1.35;
}
.support-banner:hover { background: color-mix(in srgb, var(--committed) 20%, transparent); }
.support-banner span { display: block; }
.support-banner span:first-child {
  color: var(--committed);
  font-weight: 700;
  letter-spacing: .04em;
}
.support-banner .v { color: var(--ink-dim); margin-top: .1rem; }

/* Textareas inherit the input treatment but need room to breathe. */
textarea {
  font-family: var(--font-ui);
  line-height: 1.5;
  resize: vertical;
  min-height: 4.5rem;
}

/* ── Fact list ──────────────────────────────────────────────────────────────
   A two-column label/value table for describing one thing in detail, as
   opposed to the row-per-thing tables above. Used by the image catalogue,
   where the interesting content is provenance -- a URL, a digest, a build
   identifier -- and a wide digest must be allowed to wrap rather than push the
   panel into a horizontal scroll. */
table.kv                { font-size: .78rem; }
table.kv tbody td,
table.kv tbody th       { padding: .3rem 0; border-bottom: 0; vertical-align: top; }
table.kv tbody th       { padding-right: 1.05rem; font-weight: 500; white-space: nowrap; }
table.kv code           { font-family: var(--mono, ui-monospace, monospace); }

/* ── Step-up prompt ──────────────────────────────────────────────────────────
   Built by /assets/js/stepup.js, which serves this console and the Operations
   sections inside the merged portal alike -- one gate, one prompt. The rules
   are stated again here rather than shared, because the two stylesheets do not
   share a token vocabulary (this one has --surface and --ink-dim where the
   portal has --card and --ink-muted) and a third file neither page loads would
   cost more than these twenty lines.

   --committed is the console's amber, the same voice the portal's --ops uses:
   what this prompt guards is always a platform action. */
dialog.stepup {
  border: 1px solid var(--rule);
  border-top: 2px solid var(--committed);
  border-radius: 10px;
  background: var(--surface);
  color: var(--ink);
  padding: 0;
  max-width: min(26rem, calc(100vw - 2rem));
  box-shadow: 0 18px 44px rgb(0 0 0 / .5);
}
dialog.stepup::backdrop { background: rgb(0 0 0 / .6); }
.stepup-form { padding: 1.25rem; }
.stepup-form h2 { margin: 0 0 .4rem; font-size: 1.05rem; color: var(--committed); }
.stepup-form p { margin: 0 0 .9rem; font-size: .87rem; color: var(--ink-dim); }
.stepup-code {
  width: 100%;
  box-sizing: border-box;
  font: 600 1.5rem/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  /* Wide tracking so six digits read as six digits and a transposed pair shows
     up while typing rather than after the code is refused. */
  letter-spacing: .38em;
  text-align: center;
  padding: .7rem .5rem;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: var(--ground);
  color: var(--ink);
}
.stepup-code:focus { outline: 2px solid var(--committed); outline-offset: 1px; }
.stepup-actions { display: flex; gap: .5rem; justify-content: flex-end; margin-top: 1rem; }

/* ── Payments (Stripe) ───────────────────────────────────────────────────────
   The operator's billing screen. Reuses panels, fields, tables and .notice; the
   pieces below are the test/live banner, the connection flags and the go-live
   checklist. Semantic tokens only (--observed ok, --committed warn, --alert),
   so light and dark both follow. */
.payments-banner {
  display: flex;
  align-items: center;
  gap: .8rem;
  padding: .7rem 1rem;
  border-radius: 8px;
  margin-bottom: 1.2rem;
  font-size: .9rem;
  border: 1px solid transparent;
}
.payments-banner strong { letter-spacing: .04em; }
.payments-banner.test {
  background: color-mix(in srgb, var(--committed) 14%, transparent);
  border-color: color-mix(in srgb, var(--committed) 45%, transparent);
  color: var(--ink);
}
.payments-banner.live {
  background: color-mix(in srgb, var(--alert) 14%, transparent);
  border-color: color-mix(in srgb, var(--alert) 45%, transparent);
  color: var(--ink);
}

.pay-flag { font-weight: 700; display: inline-block; width: 1.1em; text-align: center; }
.pay-flag.ok { color: var(--observed); }
.pay-flag.no { color: var(--alert); }

.pay-conn-row {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  padding: .4rem 0;
  border-bottom: 1px solid var(--surface-3);
}
.pay-conn-label { flex: 1; }

.pay-checklist { list-style: none; margin: 0; padding: 0; }
.pay-checklist li {
  display: flex;
  align-items: baseline;
  gap: .55rem;
  padding: .4rem 0;
  border-bottom: 1px solid var(--surface-3);
  color: var(--ink-dim);
}
.pay-checklist li.done { color: var(--ink); }
