/*
 * Cumulocity design system, applied.
 *
 * Every value below is lifted from the Cumulocity styleguide bundle
 * (cumulocity.com/codex/styles.css), light theme block `:root,.c8y-light-theme`
 * and dark theme block `.c8y-dark-theme`. The `--c8y-*` names are theirs and
 * carry their values verbatim; the short aliases underneath (--ink, --line,
 * --accent ...) are this app's own semantic layer, so a token change lands in
 * one place instead of 400 lines.
 *
 * Three deliberate departures, each noted where it happens:
 *   1. text green is green-30, not the brand green-40  -- contrast, see below
 *   2. headings are 600, not the token's 500           -- no 500 face ships
 *   3. status colours darken for text                  -- contrast again
 * WCAG 2.2 AA is the design system's own baseline, so where a brand colour
 * carries small text it moves one step darker on the same ramp rather than off
 * the palette.
 */

/* Public Sans, self-hosted from the styleguide's own latin subsets. No CDN:
   this app has to run inside a tenant, where an outbound font request may not
   be reachable. Weights shipped are 400/600/700 -- there is no 500 face. */
@font-face { font-family: "Public Sans"; font-style: normal; font-weight: 400; font-display: swap; src: url(fonts/public-sans-latin-400-normal.woff2) format("woff2"); }
@font-face { font-family: "Public Sans"; font-style: italic; font-weight: 400; font-display: swap; src: url(fonts/public-sans-latin-400-italic.woff2) format("woff2"); }
@font-face { font-family: "Public Sans"; font-style: normal; font-weight: 600; font-display: swap; src: url(fonts/public-sans-latin-600-normal.woff2) format("woff2"); }
@font-face { font-family: "Public Sans"; font-style: italic; font-weight: 600; font-display: swap; src: url(fonts/public-sans-latin-600-italic.woff2) format("woff2"); }
@font-face { font-family: "Public Sans"; font-style: normal; font-weight: 700; font-display: swap; src: url(fonts/public-sans-latin-700-normal.woff2) format("woff2"); }

:root {
  /* -- palette, light theme ------------------------------------------ */
  --c8y-palette-high: #fff;
  --c8y-palette-low: #000;
  --c8y-palette-gray-10: #212121;
  --c8y-palette-gray-20: #2a3846;
  --c8y-palette-gray-30: #4c5967;
  --c8y-palette-gray-40: #657381;
  --c8y-palette-gray-50: #8b96a0;
  --c8y-palette-gray-60: #d4d9de;
  --c8y-palette-gray-70: #e3e5e8;
  --c8y-palette-gray-80: #e8ebed;
  --c8y-palette-gray-90: #f0f2f4;
  --c8y-palette-gray-100: #f9fafb;

  --c8y-palette-green-10: #084708;
  --c8y-palette-green-20: #0a5c0a;
  --c8y-palette-green-30: #0f880f;
  --c8y-palette-green-40: #119d11;
  --c8y-palette-green-50: #6ac26a;
  --c8y-palette-green-60: #a5daa5;
  --c8y-palette-green-70: #d1ecd1;
  --c8y-palette-green-80: #eff9ef;

  --c8y-palette-blue-10: #033c79;
  --c8y-palette-blue-20: #044d9c;
  --c8y-palette-blue-30: #056ad6;
  --c8y-palette-blue-40: #559ae3;
  --c8y-palette-blue-50: #7fb3ea;
  --c8y-palette-blue-60: #b0d0f2;
  --c8y-palette-blue-70: #d7e7f8;
  --c8y-palette-blue-80: #f1f7fd;

  --c8y-palette-orange-20: #794000;
  --c8y-palette-orange-30: #b35f00;
  --c8y-palette-orange-50: #ff8800;
  --c8y-palette-orange-60: #ffc17a;
  --c8y-palette-orange-70: #ffe0bd;
  --c8y-palette-orange-80: #fff5e9;

  --c8y-palette-red-10: #7e0909;
  --c8y-palette-red-20: #a00b0b;
  --c8y-palette-red-30: #d70f0f;
  --c8y-palette-red-40: #e87373;
  --c8y-palette-red-50: #ee9797;
  --c8y-palette-red-60: #f5c0c0;
  --c8y-palette-red-70: #fadfdf;
  --c8y-palette-red-80: #fdf4f4;

  --c8y-palette-yellow-60: #ffbe00;

  --c8y-palette-status-info: #0092db;
  --c8y-palette-status-success: #71a112;
  --c8y-palette-status-warning: #e07800;
  --c8y-palette-status-danger: #dc5b68;
  --c8y-palette-status-realtime: #00bb00;

  /* -- brand --------------------------------------------------------- */
  --c8y-brand-primary: var(--c8y-palette-green-40);
  --c8y-brand-accent: var(--c8y-palette-yellow-60);
  --c8y-brand-dark: var(--c8y-palette-gray-20);

  /* -- type ---------------------------------------------------------- */
  --c8y-font-family-sans-serif: "Public Sans", Helvetica, Arial, sans-serif;
  --c8y-font-family-mono-spaced: Menlo, Monaco, Consolas, "Courier New", monospace;
  --c8y-font-size-base: 14px;
  --c8y-font-size-xs: calc(var(--c8y-font-size-base) * 0.7142857143);      /* 10px */
  --c8y-font-size-small: calc(var(--c8y-font-size-base) * 0.85714285714);  /* 12px */
  --c8y-font-size-large: calc(var(--c8y-font-size-base) * 1.214285714285); /* 17px */
  --c8y-font-size-h4: calc(var(--c8y-font-size-base) * 1.285714285715);    /* 18px */
  --c8y-font-size-h3: calc(var(--c8y-font-size-base) * 1.714285714285714); /* 24px */
  --c8y-font-size-h2: calc(var(--c8y-font-size-base) * 2.142857142854);    /* 30px */
  --c8y-font-size-h1: calc(var(--c8y-font-size-base) * 2.57142857143);     /* 36px */
  --c8y-line-height-base: 1.42857142857;
  --c8y-line-height-headings: 1.1;
  --c8y-font-weight-base: 400;

  /* -- geometry: everything is a multiple of one 8px unit ------------ */
  --c8y-unit-base: 8px;
  --c8y-border-radius-base: 0;   /* Cumulocity is square. Only buttons round. */
  --c8y-border-radius-large: 2px;
  --c8y-border-radius-focus: 4px;
  --c8y-btn-border-radius: 4px;
  --c8y-form-control-height-base: calc(var(--c8y-unit-base) * 4);  /* 32px */
  --c8y-table-cell-padding: 13px;
  --c8y-alert-border-width: 4px;

  /* -- elevation ----------------------------------------------------- */
  --c8y-elevation-color: 1, 31, 61;
  --c8y-elevation-sm: 0 0 1px 1px rgba(var(--c8y-elevation-color), .05), 0 2px 2px rgba(var(--c8y-elevation-color), .15);
  --c8y-elevation-hover:
    0 0 0 1px rgba(var(--c8y-elevation-color), .05), 0 4px 4px rgba(var(--c8y-elevation-color), .01),
    0 8px 8px rgba(var(--c8y-elevation-color), .01), 0 16px 16px rgba(var(--c8y-elevation-color), .03),
    0 32px 32px rgba(var(--c8y-elevation-color), .05), 0 16px 64px rgba(var(--c8y-elevation-color), .1);

  /* ================================================================= */
  /*  this app's semantic layer, mapped onto the tokens above           */
  /* ================================================================= */

  --bg: var(--c8y-palette-gray-100);
  --panel: var(--c8y-palette-high);
  --panel-2: var(--c8y-palette-gray-100);
  --ink: var(--c8y-palette-gray-10);
  --ink-mute: var(--c8y-palette-gray-30);
  --ink-faint: var(--c8y-palette-gray-40);
  --line: var(--c8y-palette-gray-70);
  --line-soft: var(--c8y-palette-gray-80);

  /* Brand green fills and borders; green-30 wherever the green becomes
     text or a small mark. #119d11 on white is 3.6:1 -- fine for a 3px
     border, short of AA for a 12px label. #0f880f is 4.6:1. */
  --brand: var(--c8y-brand-primary);
  /* Text on a filled brand surface. Light theme: white on green-30 is 4.6:1.
     Dark theme flips it, exactly as --c8y-btn-primary-color-default does. */
  --on-brand: var(--c8y-palette-high);
  --accent: var(--c8y-palette-green-30);
  --accent-soft: var(--c8y-palette-green-80);
  --accent-line: var(--c8y-palette-green-60);

  --error: var(--c8y-palette-red-20);
  --error-soft: var(--c8y-palette-red-80);
  --warn: var(--c8y-palette-orange-20);
  --warn-soft: var(--c8y-palette-orange-80);
  --suggest: var(--c8y-palette-blue-20);
  --suggest-soft: var(--c8y-palette-blue-80);
  --good: var(--c8y-palette-green-20);

  --mono: var(--c8y-font-family-mono-spaced);
  --sans: var(--c8y-font-family-sans-serif);

  --radius: var(--c8y-border-radius-large);
  --radius-btn: var(--c8y-btn-border-radius);
  --u: var(--c8y-unit-base);

  color-scheme: light;
}

/* Dark theme: the `.c8y-dark-theme` block's own values. The gray ramp
   inverts (gray-10 is the light end) and the brand steps up to green-60,
   because green-40 on near-black is unreadable.

   Keyed on an attribute rather than on `prefers-color-scheme`, because the
   header has a switch and a reader who picked one has to outrank the operating
   system. `main.tsx` resolves "follow the system" to a concrete value and sets
   the attribute, so there is one dark block here rather than two that drift. */
:root[data-theme='dark'] {
  color-scheme: dark;
  --c8y-palette-low: #141415;
  --c8y-palette-gray-10: #f2f2f3;
  --c8y-palette-gray-20: #ededee;
  --c8y-palette-gray-30: #b9babc;
  --c8y-palette-gray-40: #97999b;
  --c8y-palette-gray-50: #7d7f82;
  --c8y-palette-gray-60: #505253;
  --c8y-palette-gray-70: #434546;
  --c8y-palette-gray-80: #2d2e2f;
  --c8y-palette-gray-90: #1e1f1f;
  --c8y-palette-gray-100: #191a1a;

  --c8y-brand-primary: var(--c8y-palette-green-60);
  --c8y-elevation-color: 45, 46, 47;

  --bg: var(--c8y-palette-low);
  --panel: var(--c8y-palette-gray-100);
  --panel-2: var(--c8y-palette-gray-90);
  --ink: var(--c8y-palette-gray-10);
  --ink-mute: var(--c8y-palette-gray-30);
  --ink-faint: var(--c8y-palette-gray-40);
  --line: var(--c8y-palette-gray-70);
  --line-soft: var(--c8y-palette-gray-80);

  --accent: var(--c8y-palette-green-60);
  --on-brand: var(--c8y-palette-low);
  --accent-soft: color-mix(in srgb, var(--c8y-palette-green-10) 55%, var(--c8y-palette-gray-100));
  --accent-line: var(--c8y-palette-green-50);

  --error: var(--c8y-palette-red-50);
  --error-soft: color-mix(in srgb, var(--c8y-palette-red-10) 45%, var(--c8y-palette-gray-100));
  --warn: var(--c8y-palette-orange-60);
  --warn-soft: color-mix(in srgb, var(--c8y-palette-orange-20) 45%, var(--c8y-palette-gray-100));
  --suggest: var(--c8y-palette-blue-60);
  --suggest-soft: color-mix(in srgb, var(--c8y-palette-blue-10) 45%, var(--c8y-palette-gray-100));
  --good: var(--c8y-palette-green-60);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: var(--c8y-font-weight-base) var(--c8y-font-size-base) / var(--c8y-line-height-base) var(--sans);
  -webkit-font-smoothing: antialiased;
  /* Checkboxes, radios and range thumbs are native controls; this is the one
     line that brings them onto the brand. */
  accent-color: var(--brand);
}

/* The heading token is 500, but the shipped font has 400/600/700 only, so 500
   resolves to 400 and every heading flattens into its body text. 600 is the
   nearest face that still carries the hierarchy. */
h1, h2, h3, h4 { margin: 0; font-weight: 600; line-height: var(--c8y-line-height-headings); }
h1 { font-size: var(--c8y-font-size-h3); }
h2 { font-size: var(--c8y-font-size-h4); }
h3 { font-size: var(--c8y-font-size-large); }
h4 {
  font-size: var(--c8y-font-size-small); text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--ink-faint);
}
p { margin: 0 0 calc(var(--u) * 1.25); }
a { color: var(--accent); }

/* The page is the width; the shell is what is left of it beside the rail.
   The cap and the side padding moved up here from `.shell` so the rail sits
   inside the same 1240 px as the top bar and the step rail, rather than
   pushing the content off-centre. */
.page {
  max-width: 1240px; margin: 0 auto; padding: 0 calc(var(--u) * 2.5) calc(var(--u) * 2);
  display: flex; align-items: flex-start; gap: calc(var(--u) * 2);
}
.shell { flex: 1; min-width: 0; }

/* The scenario's description: present but quiet, so it does not compete with
   the step it sits above. It grows with what is typed in it, up to a cap past
   which it folds (StepFleet.tsx), so it is never resized by hand. */
.field.describe { margin-bottom: calc(var(--u) * 2); }
.field.describe textarea {
  width: 100%; resize: none; min-height: 44px;
  font: inherit; font-size: var(--c8y-font-size-small);
}
.field.describe .describe-toggle { align-self: flex-start; padding: 2px 0; margin-top: 2px; }

/* ---------------------------------------------------------------- top bar */

.topbar {
  position: sticky; top: 0; z-index: 20;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  box-shadow: var(--c8y-elevation-sm);
  margin-bottom: calc(var(--u) * 3);
}
.topbar-inner {
  max-width: 1240px; margin: 0 auto; padding: calc(var(--u) * 1.5) calc(var(--u) * 2.5);
  display: flex; gap: calc(var(--u) * 2.5); align-items: center; flex-wrap: wrap;
}
/* min-width:0 so the tagline can give way rather than pushing the language
   switch onto a second row: German is a fifth longer than English here, and the
   bar is capped at 1240px on every screen. */
/* The wordmark sits beside the brand block rather than inside it, so it stays
   vertically centred against the lines however long the scenario name gets. It
   is drawn in currentColor, so `--ink` carries it into either theme. */
.logo { color: var(--ink); display: flex; align-items: center; flex: none; }

.brand { display: flex; flex-direction: column; gap: 1px; margin-right: auto; min-width: 0; }
.wordmark {
  font-size: var(--c8y-font-size-xs); color: var(--ink-faint);
  text-transform: uppercase; letter-spacing: 0.07em; line-height: 1.2;
}
.brand small {
  color: var(--ink-faint); font-size: var(--c8y-font-size-small);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.brand input {
  border: 0; background: transparent; color: var(--ink);
  font: 600 var(--c8y-font-size-h3) / var(--c8y-line-height-headings) var(--sans);
  padding: 0; min-width: 260px;
}
.brand input:focus { outline: none; border-bottom: 2px solid var(--brand); }

.runner { display: flex; gap: calc(var(--u) * 2.5); align-items: baseline; }
.runner .fig { display: flex; flex-direction: column; align-items: flex-end; }
.runner .fig b { font: 600 var(--c8y-font-size-h4) / 1.15 var(--mono); font-variant-numeric: tabular-nums; }
.runner .fig span { font-size: var(--c8y-font-size-xs); color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.05em; }
.runner .fig.alt b { color: var(--ink-mute); font-size: var(--c8y-font-size-base); }

/* ------------------------------------------------------------------ panels */

.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-bottom: calc(var(--u) * 2);
  overflow: hidden;
}
.panel > header {
  padding: calc(var(--u) * 2);
  border-bottom: 1px solid var(--line-soft);
  display: flex; align-items: center; gap: calc(var(--u) * 1.5); flex-wrap: wrap;
}
.panel > header .sub { color: var(--ink-mute); font-size: var(--c8y-font-size-small); margin-left: auto; }
.panel .body { padding: calc(var(--u) * 2); }
.panel .body.tight { padding: 0; }

/* Cumulocity's alert: a 4px status bar down the left, no tinted fill. */
.note {
  background: var(--panel-2);
  border-left: var(--c8y-alert-border-width) solid var(--brand);
  padding: calc(var(--u) * 1.5) calc(var(--u) * 2);
  margin: 0 0 calc(var(--u) * 2);
  font-size: var(--c8y-font-size-small); color: var(--ink-mute);
  border-radius: 0 var(--radius) var(--radius) 0;
}
.note b, .note strong { color: var(--ink); font-weight: 600; }

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

label.field, div.field.describe { display: flex; flex-direction: column; gap: 4px; font-size: var(--c8y-font-size-small); color: var(--ink-faint); }
label.field > span, .field.describe > label { text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; font-size: var(--c8y-font-size-xs); }
input[type="text"], input[type="number"], select, textarea {
  font: var(--c8y-font-size-base) / var(--c8y-line-height-base) var(--sans); color: var(--ink);
  background: var(--panel); border: 1px solid var(--c8y-palette-gray-60);
  border-radius: var(--c8y-border-radius-base);
  min-height: var(--c8y-form-control-height-base);
  padding: 5px var(--u); width: 100%;
}
input::placeholder, textarea::placeholder { color: var(--ink-faint); font-style: italic; }
input[type="number"] { font-family: var(--mono); font-variant-numeric: tabular-nums; }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--brand) 22%, transparent);
}
input:disabled, select:disabled, textarea:disabled { opacity: .65; }

/* Cumulocity buttons are outlined and invert on hover: a filled primary goes
   white-on-green -> green-on-white, a default goes green-bordered -> tinted. */
button {
  font: var(--c8y-font-weight-base) var(--c8y-font-size-base) / var(--c8y-line-height-base) var(--sans);
  color: var(--c8y-brand-dark);
  background: var(--panel); border: 1px solid var(--brand);
  border-radius: var(--radius-btn);
  min-height: var(--c8y-form-control-height-base);
  padding: 5px calc(var(--u) * 1.5); cursor: pointer;
}
button:hover { color: var(--accent); background: var(--accent-soft); }
button:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
button.primary { background: var(--accent); border-color: var(--accent); color: var(--on-brand); }
button.primary:hover { background: var(--panel); color: var(--accent); }
/* The header verbs: quiet until pointed at. `.file` carries the same look on a
   <label>, which is the only way to front a file input without the browser's
   own button. Only `.danger` goes red -- four header buttons all turning the
   colour of a delete made every one of them read as one. */
.ghost, label.ghost.file {
  font: var(--c8y-font-weight-base) var(--c8y-font-size-base) / var(--c8y-line-height-base) var(--sans);
  background: transparent; border: 1px solid transparent; color: var(--ink-faint);
  border-radius: var(--radius-btn);
  padding: 4px 6px; min-height: 0; cursor: pointer;
  display: inline-flex; align-items: center;
}
.ghost:hover { color: var(--accent); background: transparent; border-color: transparent; }
.ghost.danger:hover { color: var(--error); }
/* Six verbs now. They wrap inside the group rather than splitting across the
   bar, so a narrow window gets two tidy rows of verbs instead of four of them
   on one line and two stranded under the scenario name. */
.verbs { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; justify-content: flex-end; }
label.ghost.file input { display: none; }
button:disabled { opacity: .65; cursor: default; }
.row { display: flex; gap: calc(var(--u) * 1.5); align-items: flex-end; flex-wrap: wrap; }
.spacer { margin-left: auto; }

/* ------------------------------------------------------------------ tables */

table { width: 100%; border-collapse: collapse; font-size: var(--c8y-font-size-base); }
th {
  text-align: left; font-weight: 600; font-size: var(--c8y-font-size-xs);
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-faint);
  padding: var(--u) var(--c8y-table-cell-padding); border-bottom: 1px solid var(--line);
}
td { padding: var(--u) var(--c8y-table-cell-padding); border-bottom: 1px solid var(--line-soft); vertical-align: top; }
tbody tr:last-child td { border-bottom: 0; }
td.num, th.num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }
th.num { font-family: var(--sans); }
tr.total td { font-weight: 600; border-top: 1px solid var(--line); background: var(--panel-2); }
td.cell { font-family: var(--mono); color: var(--ink-faint); font-size: var(--c8y-font-size-small); }
.scroll { overflow-x: auto; }

/* ------------------------------------------------------------- machine type */

/* A machine type: a <details> whose <summary> is the whole story when folded. */
.mt {
  border: 1px solid var(--line); border-radius: var(--radius);
  margin-bottom: calc(var(--u) * 2); background: var(--panel);
}
/* Four parts on one row: caret, name, what is inside, what it costs. A grid
   rather than flex, because with flex the first thing that does not fit drops
   to a second line and drags the caret and the figure with it -- here only the
   middle column is elastic, and it wraps inside itself. */
.mt > summary {
  display: grid;
  grid-template-columns: max-content max-content minmax(0, 1fr) max-content;
  align-items: center; gap: 0 calc(var(--u) * 2);
  padding: calc(var(--u) * 1.5) calc(var(--u) * 2);
  border-bottom: 1px solid transparent;
  cursor: pointer; list-style: none;
}
.mt[open] > summary { border-bottom-color: var(--line-soft); }
.mt > summary:hover { background: var(--panel-2); }
.mt > summary:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

/* Cumulocity's caret comes from its icon font, which this app does not load, so
   the disclosure triangle is drawn from borders. */
.mt > summary::-webkit-details-marker { display: none; }
.mt > summary::marker { content: ''; }
.mt > summary::before {
  content: ''; grid-column: 1; justify-self: center;
  width: 0; height: 0; border: 5px solid transparent; border-left: 7px solid var(--ink-faint);
  transition: transform .12s ease;
}
.mt[open] > summary::before { transform: rotate(90deg); }
.mt > summary:hover::before { border-left-color: var(--accent); }

.mt-head { grid-column: 2; display: flex; gap: calc(var(--u) * 1.5); align-items: baseline; flex-wrap: wrap; }
.mt-name { font-weight: 600; font-size: var(--c8y-font-size-large); line-height: var(--c8y-line-height-headings); }
/* Inside StepDiscrete's per-element panels the machine name is a row label, not
   the page's subject, so it does not compete with the panel heading. */
.panel.sub .mt-name { font-size: var(--c8y-font-size-base); }

.mt-sum-text {
  grid-column: 3; display: flex; flex-direction: column; gap: 1px; min-width: 0;
  font-size: var(--c8y-font-size-small); color: var(--ink-mute);
}
.mt-sum-mix { color: var(--ink-faint); }
.mt-sum-empty {
  grid-column: 3 / -1; text-align: right;
  font-size: var(--c8y-font-size-small); color: var(--ink-faint); font-style: italic;
}
.mt-sum-fig {
  grid-column: 4; display: flex; flex-direction: column;
  align-items: flex-end; text-align: right;
}
.mt-sum-fig b {
  font: 600 var(--c8y-font-size-large) / 1.15 var(--mono);
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.mt-sum-fig > span {
  font-size: var(--c8y-font-size-xs); color: var(--ink-faint);
  text-transform: uppercase; letter-spacing: 0.05em;
}

/* Narrow: the name keeps the first row, the summary takes the second. */
@media (max-width: 900px) {
  .mt > summary { grid-template-columns: max-content minmax(0, 1fr) max-content; }
  .mt-head { grid-column: 2 / -1; }
  .mt-sum-text { grid-column: 2; }
  .mt-sum-fig { grid-column: 3; }
  .mt-sum-empty { grid-column: 2 / -1; }
}

.mt .metrics { padding: var(--u) calc(var(--u) * 2) calc(var(--u) * 2); }
.mt-tag { font-family: var(--mono); font-size: var(--c8y-font-size-xs); color: var(--ink-faint); }

.metric {
  display: grid;
  grid-template-columns: minmax(150px, 1.4fr) 72px minmax(150px, 1fr) minmax(140px, 1fr) minmax(120px, 1fr) 28px;
  gap: var(--u); align-items: end;
  padding: var(--u) 0; border-bottom: 1px solid var(--line-soft);
}
.metric:last-child { border-bottom: 0; }
.metric .kind-hint { grid-column: 1 / -1; font-size: var(--c8y-font-size-small); color: var(--ink-faint); margin-top: -2px; }

/* Cumulocity's badge: pill, bold, small. */
.bundle-chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--accent-soft); color: var(--accent);
  border-radius: 10px; padding: 2px 10px;
  font: bold var(--c8y-font-size-small) / 1.35 var(--mono);
}

/* ---------------------------------------------------------------- findings */

.finding {
  display: flex; gap: calc(var(--u) * 1.5);
  padding: calc(var(--u) * 1.5) calc(var(--u) * 2);
  border-bottom: 1px solid var(--line-soft);
}
.finding:last-child { border-bottom: 0; }
.finding .rule {
  font: bold var(--c8y-font-size-xs) / 1.6 var(--mono);
  padding: 2px 8px; border-radius: 10px; height: fit-content; letter-spacing: 0.03em;
}
.finding.error .rule { background: var(--error-soft); color: var(--error); }
.finding.warning .rule { background: var(--warn-soft); color: var(--warn); }
.finding.suggestion .rule { background: var(--suggest-soft); color: var(--suggest); }
.finding .txt { flex: 1; }
.finding .txt b { display: block; margin-bottom: 3px; font-weight: 600; }
.finding .txt p { margin: 0; font-size: var(--c8y-font-size-small); color: var(--ink-mute); }
.finding .delta { font: 600 var(--c8y-font-size-small) var(--mono); white-space: nowrap; padding-top: 1px; }
.finding .delta.saves { color: var(--good); }
.finding .delta.costs { color: var(--warn); }
.clean { padding: calc(var(--u) * 2); color: var(--good); font-size: var(--c8y-font-size-base); }

/* ----------------------------------------------------------------- payload */

pre {
  margin: 0; font: var(--c8y-font-size-small) / 1.5 var(--mono);
  background: var(--c8y-palette-blue-80); color: var(--c8y-palette-blue-20);
  border: 1px solid var(--line-soft); border-radius: var(--radius);
  padding: calc(var(--u) * 1.5); overflow-x: auto;
}
.payload { padding: calc(var(--u) * 2) 0; border-bottom: 1px solid var(--line-soft); }
.payload:last-child { border-bottom: 0; }
.payload > header { display: flex; gap: calc(var(--u) * 1.5); align-items: center; margin-bottom: var(--u); }
.payload code.path {
  display: block; margin: 0 0 6px;
  font: var(--c8y-font-size-small) var(--mono); color: var(--c8y-palette-blue-20);
}
code.frag {
  font: 600 var(--c8y-font-size-small) var(--mono); color: var(--accent);
  background: var(--accent-soft); padding: 2px 8px; border-radius: 10px;
}

/* -------------------------------------------------------------------- misc */

.grid { display: grid; gap: calc(var(--u) * 2); }
.grid.two { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
/* Three stats rather than four: auto-fit still wraps them on a narrow screen,
   and the wider track keeps a 63.1 bn from breaking across two lines. */
.grid.three { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.grid.four { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }

.stat {
  background: var(--panel); border: 1px solid var(--line-soft);
  border-radius: var(--radius); padding: calc(var(--u) * 2);
}
.stat b {
  display: block; font: 600 var(--c8y-font-size-h3) / var(--c8y-line-height-headings) var(--mono);
  font-variant-numeric: tabular-nums;
}
.stat span { font-size: var(--c8y-font-size-xs); color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.05em; }
.stat small { display: block; margin-top: 5px; font-size: var(--c8y-font-size-small); color: var(--ink-mute); }

.bar { height: 6px; border-radius: 3px; background: var(--line-soft); overflow: hidden; margin-top: var(--u); }
.bar > i { display: block; height: 100%; background: var(--brand); }

.ramp { display: flex; gap: 3px; align-items: flex-end; height: 64px; margin-top: 6px; }
.ramp > i { flex: 1; background: var(--accent-line); border-radius: 1px 1px 0 0; min-height: 2px; }
.ramp > i.peak { background: var(--brand); }

.disclaimer {
  font-size: var(--c8y-font-size-small); color: var(--ink-faint);
  border-top: 1px solid var(--line); padding-top: calc(var(--u) * 2); margin-top: 6px;
}

/* The explainer's launcher: one line, right where the panel used to start, and
   quiet enough that nobody reading the step has to step over it. */
.explain-launch { margin: calc(var(--u) * -1) 0 calc(var(--u) * 2); text-align: right; }
.linkish {
  background: none; border: 0; padding: 0; min-height: 0;
  color: var(--accent); font: inherit; font-size: var(--c8y-font-size-small);
  cursor: pointer; text-decoration: underline; text-underline-offset: 3px;
}
.linkish:hover { background: none; color: var(--c8y-brand-dark); }

/* The explainer itself, in the top layer. Sized to the diagram rather than to
   the window: the drawing is 648 units wide and stops being legible stretched
   across a 27-inch screen, so it takes the width it needs and no more. */
dialog.explain {
  /* The body has to be at least as wide as the panel it replaced (~984 px in
     a 1240 px shell), or the slider and the two figures beside it wrap. */
  width: min(1060px, calc(100vw - var(--u) * 6));
  max-height: calc(100vh - var(--u) * 6);
  padding: 0; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); color: var(--ink);
  box-shadow: var(--c8y-elevation-lg, 0 18px 48px rgba(0, 0, 0, .38));
  overflow: auto;
}
dialog.explain::backdrop { background: rgba(0, 0, 0, .55); }
dialog.explain > header {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; gap: calc(var(--u) * 1.5);
  padding: calc(var(--u) * 1.5) calc(var(--u) * 2);
  background: var(--panel); border-bottom: 1px solid var(--line-soft);
}
dialog.explain > header h2 { margin: 0; font-size: var(--c8y-font-size-base); }
dialog.explain > header .ghost { margin-left: auto; font-size: 15px; line-height: 1; }
dialog.explain .body { padding: calc(var(--u) * 2); }

/* Segmented control, built from Cumulocity's button geometry. */
.toggle { display: inline-flex; border: 1px solid var(--brand); border-radius: var(--radius-btn); overflow: hidden; }
.toggle button { border: 0; border-radius: 0; background: transparent; padding: 5px calc(var(--u) * 1.5); }
.toggle button.on { background: var(--accent); color: var(--on-brand); }
.toggle button.on:hover { background: var(--accent); color: var(--on-brand); }

.empty { padding: calc(var(--u) * 4) calc(var(--u) * 2); text-align: center; color: var(--ink-faint); font-size: var(--c8y-font-size-base); }

/* ------------------------------------------------------------------ wizard */

.rail {
  max-width: 1240px; margin: 0 auto; padding: 0 calc(var(--u) * 2.5) calc(var(--u) * 1.5);
  display: flex; gap: 4px; flex-wrap: wrap;
}
/* Cumulocity nav-pills: 16px radius, no border until hovered. */
.rail-step {
  display: inline-flex; align-items: center; gap: 7px;
  border: 1px solid transparent; background: transparent; color: var(--ink-faint);
  padding: 5px calc(var(--u) * 1.5) 5px 6px; border-radius: calc(var(--u) * 2);
  font-size: var(--c8y-font-size-small); font-weight: 600; min-height: 0;
}
.rail-step i {
  font-style: normal; width: 19px; height: 19px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--line-soft); color: var(--ink-faint);
  font: bold var(--c8y-font-size-xs) / 1 var(--sans);
}
.rail-step:hover { color: var(--ink); border-color: var(--line); background: transparent; }
.rail-step.done { color: var(--ink-mute); }
.rail-step.done i { background: var(--accent-soft); color: var(--accent); }
.rail-step.on { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.rail-step.on i { background: var(--brand); color: var(--on-brand); }
.rail-step.on:hover { border-color: transparent; background: var(--accent-soft); }

.step-head { margin: var(--u) 0 calc(var(--u) * 3); }
.step-head h1 { font-size: var(--c8y-font-size-h2); margin-bottom: 4px; }
.step-head p { color: var(--ink-mute); font-size: var(--c8y-font-size-large); margin: 0; }

/* The step buttons stay on screen.
   Every step is taller than a screen -- the measurements table alone is -- so
   the way forward used to be somewhere you had to scroll to find, and on the
   long steps a reader could be forgiven for thinking there wasn't one. Sticky
   rather than fixed: it sits in the flow, so it cannot overlap the content
   above it and the page still ends where the page ends.
   An opaque background is not decoration here: without it the rows underneath
   scroll through the buttons. */
.nav {
  display: flex; gap: calc(var(--u) * 1.5); align-items: center; flex-wrap: wrap;
  position: sticky; bottom: 0; z-index: 5;
  margin-top: calc(var(--u) * 4);
  padding: calc(var(--u) * 2) 0;
  background: var(--bg); border-top: 1px solid var(--line);
}

/* The teaching block is an alert: 4px brand bar, no fill. */
.teach {
  background: var(--panel); border: 1px solid var(--line);
  border-left: var(--c8y-alert-border-width) solid var(--brand);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: calc(var(--u) * 2); margin: 0 0 calc(var(--u) * 2);
}
.teach > b { display: block; margin-bottom: 6px; font-size: var(--c8y-font-size-base); }
.teach p { font-size: var(--c8y-font-size-base); color: var(--ink-mute); margin: 0 0 var(--u); }
.teach p:last-child { margin-bottom: 0; }
.teach b, .teach strong { color: var(--ink); }
.teach code, .hint code, td code {
  font: var(--c8y-font-size-small) var(--mono);
  background: var(--c8y-palette-blue-80); color: var(--c8y-palette-blue-20);
  padding: 1px 4px; border-radius: 0;
}

.hint { font-size: var(--c8y-font-size-small); color: var(--ink-faint); margin: 6px 0 0; }
.lbl { font-size: var(--c8y-font-size-xs); color: var(--ink-faint); text-transform: uppercase; letter-spacing: .05em; font-weight: 600; }

.panel.sub { background: var(--panel-2); }
.panel.sub > header h3 { font-size: var(--c8y-font-size-large); }

table.dp td { padding: 5px var(--u) 5px 0; border-bottom: 0; vertical-align: top; }
table.dp th { padding: 6px var(--u) 6px 0; }
table.dp tr:not(:last-child) td { border-bottom: 1px solid var(--line-soft); }

.group-row {
  font-size: var(--c8y-font-size-xs); text-transform: uppercase; letter-spacing: .06em; font-weight: 600;
  color: var(--ink-faint); background: var(--panel-2); padding-top: calc(var(--u) * 1.5);
}

.check { display: inline-flex; gap: 6px; align-items: center; font-size: var(--c8y-font-size-small); color: var(--ink-mute); cursor: pointer; }
.check input { width: auto; min-height: 0; accent-color: var(--brand); }


.delta.saves { color: var(--good); }
.delta.costs { color: var(--warn); }

/* The shared Choice control: a dropdown, with a free field when "Other" is picked. */
.field select + span input { border-color: var(--brand); }
.field select { text-overflow: ellipsis; }
table.dp .field { gap: 0; }
table.dp .field > span:first-child { margin-bottom: 4px; }

/* Duration: a free number and a unit, reading as one control. Square corners
   let the two halves butt together the way a Cumulocity input group does. */
.duration { display: flex; }
.duration input { border-right: 0; min-width: 0; flex: 1; }
.duration select { width: auto; min-width: 68px; background: var(--panel-2); color: var(--ink-mute); }
.duration input:focus + select { border-color: var(--brand); }
.duration .prefix {
  font-style: normal; font-size: var(--c8y-font-size-small); color: var(--ink-faint);
  display: inline-flex; align-items: center; padding: 0 var(--u);
  background: var(--panel-2); border: 1px solid var(--c8y-palette-gray-60); border-right: 0;
  white-space: nowrap;
}

label.expert {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: var(--c8y-font-size-small); font-weight: 600; color: var(--ink-faint);
  border: 1px solid var(--line); border-radius: calc(var(--u) * 2);
  padding: 5px calc(var(--u) * 1.5);
  cursor: pointer; white-space: nowrap;
}
label.expert:hover { color: var(--ink); border-color: var(--brand); }
label.expert.on { color: var(--accent); border-color: var(--brand); background: var(--accent-soft); }
label.expert input { width: auto; min-height: 0; margin: 0; accent-color: var(--brand); }

/* The language switch, beside expert mode: same chip, but a select rather than a
   checkbox. Its label is for assistive technology only -- each language names
   itself in the list, which is the only labelling a reader who needs the switch
   can actually use. */
label.locale {
  display: inline-flex; align-items: center;
  border: 1px solid var(--line); border-radius: calc(var(--u) * 2);
  padding: 2px 4px 2px calc(var(--u) * 1.25);
  white-space: nowrap;
}
label.locale:hover { border-color: var(--brand); }
label.locale select {
  border: 0; background: transparent; min-height: 0; padding: 3px 2px;
  font-size: var(--c8y-font-size-small); font-weight: 600; color: var(--ink-faint);
  cursor: pointer;
}
label.locale select:hover { color: var(--ink); }

.sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* The volume diagram: four readings, one or four envelopes. */
.diagram {
  background: var(--panel); border: 1px solid var(--line-soft);
  border-radius: var(--radius); padding: calc(var(--u) * 1.5); margin: 4px 0 calc(var(--u) * 2);
}
.diagram svg {
  display: block;
  /*
   * Capped at the geometry's own width rather than stretched to the column.
   *
   * The viewBox is 648 units for the explainer and 700 for the configuration
   * diagram, and every font size inside them is chosen in those units. Left at
   * width:100% the whole picture scales with the content area, so on a wide
   * screen a 14px label renders at 28px and the diagram shouts. Capping at the
   * intrinsic width means it shrinks on a narrow screen and never grows past
   * the size it was drawn at.
   */
  width: 100%;
  max-width: 700px;
  height: auto;
}

.dg-cap {
  font: 600 10px var(--sans); fill: var(--ink-faint);
  letter-spacing: 0.09em; text-transform: uppercase;
}
.dg-sensor { fill: var(--panel-2); stroke: var(--line); stroke-width: 1; }
.dg-dot { fill: var(--brand); }
.dg-label { font: 12px var(--mono); fill: var(--ink-mute); }
.dg-wire { fill: none; stroke: var(--line); stroke-width: 1.5; }
.dg-msg { fill: var(--accent-soft); stroke: var(--brand); stroke-width: 1.5; }
.dg-msg-title { font: 600 14px var(--sans); fill: var(--accent); }
.dg-msg-sub { font: 10px var(--sans); fill: var(--ink-faint); }
.dg-total { font: 600 30px var(--mono); fill: var(--accent); }

@media (max-width: 620px) {
  .dg-label { font-size: 11px; }
}

/* The configuration diagram reuses the explainer's grammar. */
.dg-unit { fill: var(--ink-faint); font-size: 10px; }
.dg-more { font: italic 12px var(--sans); fill: var(--ink-faint); }
.dg-msg-solo { fill: var(--panel-2); stroke: var(--line); stroke-width: 1.5; }
.dg-frag { font: 600 12px var(--mono); fill: var(--accent); }
.dg-rate { font: 10px var(--sans); fill: var(--ink-faint); }
.dg-count { font: 600 14px var(--mono); fill: var(--ink-mute); }

.dg-legend {
  display: flex; gap: calc(var(--u) * 2); align-items: center; flex-wrap: wrap;
  margin-top: var(--u); padding-top: var(--u); border-top: 1px solid var(--line-soft);
  font-size: var(--c8y-font-size-small); color: var(--ink-faint);
}
.dg-legend b { color: var(--ink); font-family: var(--mono); }
/* The bold number inside the saving line, which the catalogue emphasises. */
.dg-legend .saves b { color: var(--good); }
.dg-legend > span { display: inline-flex; align-items: center; gap: 6px; }
.dg-legend i.key { width: 22px; height: 12px; border-radius: 2px; display: inline-block; }
.dg-legend i.key.shared { background: var(--accent-soft); border: 1.5px solid var(--brand); }
.dg-legend i.key.solo { background: var(--panel-2); border: 1.5px solid var(--line); }

/* The scenario rail: this build's answer to the shell's navigator, which it
   does not have. Quiet by design -- it is navigation, not a figure, and the
   top bar's job is the running total. It only earns its width once there is
   more than one scenario, so a single-scenario session looks as it always did. */
.library-rail {
  display: flex; flex-direction: column; gap: 2px;
  width: 160px; flex: none;
  padding-right: calc(var(--u) * 2); border-right: 1px solid var(--line);
}
.library-rail:empty, .library-rail:has(> :only-child) { display: none; }
.library-tab {
  display: block; width: 100%; text-align: left;
  padding: 7px 10px; border: 1px solid transparent; border-radius: 6px;
  background: transparent; color: var(--ink-mute);
  font: inherit; font-size: var(--c8y-font-size-small); cursor: pointer;
  /* One line each, because the name is the customer's and can be long; the
     full text stays in the title attribute the button already carries. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.library-tab:hover { color: var(--ink); border-color: var(--line); }
.library-tab.on {
  background: var(--accent-soft); color: var(--accent);
  border-color: transparent; font-weight: 600;
}

/* Measured, not guessed: at a shell of 833 px nothing on any step overflows,
   and by 712 the diagrams are spilling out of their boxes. The rail costs the
   shell 216 px -- its 160, the 16 of gap and the page's own 40 of padding --
   so at about 1050 px of viewport the rail is what pushes the step under that
   floor. Below it the rail stops being a column and lies down above the
   content as a row of pills, giving the step back the full width. Move the
   rail's width and this number moves with it. */
@media (max-width: 1050px) {
  .page { flex-direction: column; gap: var(--u); }
  .library-rail {
    flex-direction: row; flex-wrap: wrap; width: auto; align-self: stretch;
    padding: 0 0 var(--u); border-right: none; border-bottom: 1px solid var(--line);
  }
  .library-tab { width: auto; max-width: 220px; border-radius: 16px; }
  .shell { align-self: stretch; }
}
