/* ---- fonts, served from here ------------------------------------------
   Barlow Semi Condensed, DM Sans and JetBrains Mono, all SIL Open Font
   License 1.1, taken from Google Fonts and served from this origin instead.

   Not a preference: a customer in Amman loading these from fonts.googleapis.com
   and fonts.gstatic.com pays a DNS lookup, a TCP handshake and a TLS handshake
   to each, before a single byte of font arrives. At that distance the two extra
   origins cost more than the whole round trip to this server does. Corporate
   networks also block them, and the page then waits for a timeout.

   Only latin and latin-ext are kept. Everything this interface renders is
   latin, including the micro sign. DM Sans and JetBrains Mono are variable, so
   one file serves every weight of each.
   --------------------------------------------------------------------- */

/* Barlow Semi Condensed 500, latin-ext */
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/static/fonts/barlow-semi-condensed-500-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Barlow Semi Condensed 500, latin */
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/static/fonts/barlow-semi-condensed-500-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Barlow Semi Condensed 600, latin-ext */
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/static/fonts/barlow-semi-condensed-600-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Barlow Semi Condensed 600, latin */
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/static/fonts/barlow-semi-condensed-600-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Barlow Semi Condensed 700, latin-ext */
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/static/fonts/barlow-semi-condensed-700-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Barlow Semi Condensed 700, latin */
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/static/fonts/barlow-semi-condensed-700-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* DM Sans 400, latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/static/fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* DM Sans 400, latin */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/static/fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* DM Sans 500, latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/static/fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* DM Sans 500, latin */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/static/fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* DM Sans 700, latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/static/fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* DM Sans 700, latin */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/static/fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* JetBrains Mono 400, latin-ext */
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/static/fonts/jetbrains-mono-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* JetBrains Mono 400, latin */
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/static/fonts/jetbrains-mono-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* JetBrains Mono 500, latin-ext */
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/static/fonts/jetbrains-mono-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* JetBrains Mono 500, latin */
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/static/fonts/jetbrains-mono-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* JetBrains Mono 600, latin-ext */
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/static/fonts/jetbrains-mono-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* JetBrains Mono 600, latin */
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/static/fonts/jetbrains-mono-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Multitec machine dashboard.
 *
 * The look is a press control panel, not a SaaS dashboard: squared inset
 * panels with hairline rules instead of floating rounded cards, monospace
 * numerals like the counter on the machine, and legends set in small caps the
 * way machine panels label their gauges.
 *
 * The one genuinely domain-specific element is the WEB RIBBON: the day drawn
 * as a continuous strip of material, printed stretches against waste against
 * stopped time. That is literally what the machine produces, and it shows the
 * thing the product exists to show.
 *
 * Colours are status, not decoration, and the print/waste pair is validated
 * for colour blindness: teal vs orange separate at dE 15.3 for deuteranopia,
 * where the obvious green/amber managed 7.8 -- and that is exactly the pair an
 * operator must never confuse.
 */

:root{
  /* Taken from multitec.in: Barlow Semi Condensed headings, DM Sans body,
     JetBrains Mono for figures, and the Multitec red. The red is used for
     identity and interaction only -- never for status, so it cannot be
     mistaken for an alarm. */
  --brand:#CC211E;
  --brand-ink:#141417;
  --brand-ink-2:#57575F;
  --brand-rule:#E6E7E9;

  --void:#0A0C10;        /* behind everything */
  --panel:#111419;       /* inset panels */
  --panel-2:#161A21;     /* raised rows */
  --rule:#222833;        /* hairlines */
  --rule-bright:#2E3644;
  --ink:#E7EBF0;
  --ink-2:#98A3B3;       /* secondary */
  --ink-3:#5E6A7C;       /* legends */

  --print:#2DD4BF;       /* printing: counter on */
  --waste:#FB923C;       /* running, counter off */
  --idle:#64748B;        /* stopped */
  --alarm:#F43F5E;
  --link:#F26562;   /* Multitec red, lifted for legibility on dark */

  --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  --sans:'DM Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --display:'Barlow Semi Condensed','DM Sans',sans-serif;
}

*{box-sizing:border-box}
/* `.grid{display:grid}` overrides the hidden attribute, so pressing Grid left
   the cards on screen. An attribute that means "not displayed" has to win. */
[hidden]{display:none !important}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--void);color:var(--ink);font:16px/1.55 var(--sans);
  -webkit-font-smoothing:antialiased;
}
main{position:relative;max-width:1240px;margin:0 auto;padding:22px 20px 64px}
a{color:var(--link);text-decoration:none}
a:hover{color:#FF8A87}

/* ---- chrome ---------------------------------------------------------- */
/* The brand lives in white, as it does on multitec.in; the data lives in the
   dark panel below it. The red rule is the seam between the two. */
.bar{position:relative;display:flex;justify-content:space-between;align-items:center;
  padding:0 22px;height:58px;background:#fff;border-bottom:2px solid var(--brand)}
.brand{display:flex;align-items:center;gap:11px;min-width:0}
.brand img.co{height:26px;width:auto;display:block}
/* The maker, then the product. The rule between them is the same one the bar
   already used; the mark is the app icon at bar height. */
/* The rule belongs between the company and the product, once. `.brand span`
   used to carry it, which put a second one before the word as soon as the word
   got a span of its own. */
.brand .vue{display:flex;align-items:center;gap:7px}
.brand .word{border:0;padding:0}
.brand .mark{height:23px;width:23px;display:block}
.brand span{font-family:var(--display);font-weight:600;font-size:16px;letter-spacing:.04em;
  text-transform:uppercase;color:var(--brand-ink-2);border-left:1px solid var(--brand-rule);
  padding-left:11px}
.bar nav{display:flex;align-items:center;gap:18px;font-size:13px}
.bar nav a{color:var(--brand-ink)}
.bar nav a:hover{color:var(--brand)}
/* A person's name, not an identifier, so it wears the text face. */
.bar .who{color:var(--brand-ink-2);font-size:13px;font-weight:500}
.bar nav a.geared{display:inline-flex;align-items:center;gap:6px}
.bar nav a.geared svg{opacity:.75}
.bar nav a.geared:hover svg{opacity:1}
footer{position:relative;text-align:center;color:var(--ink-3);font-size:11px;
  font-family:var(--mono);padding:18px;letter-spacing:.04em}
.crumb{margin-bottom:14px;font-size:12px;font-family:var(--mono);letter-spacing:.04em}

h1{font-family:var(--display);font-size:34px;margin:0;font-weight:600;
  letter-spacing:.01em;line-height:1.1}
h1 small{color:var(--ink-2);font-weight:500;font-size:19px}
h2{font-family:var(--display);font-size:13px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-3);
  font-weight:600;margin:30px 0 10px;display:flex;align-items:center;gap:12px}
h2::after{content:'';flex:1;height:1px;background:var(--rule)}
h3{font-family:var(--display);margin:0 0 14px;font-size:12px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink-3);font-weight:600}
.muted{color:var(--ink-2)}
.small{font-size:12px;line-height:1.55}

/* ---- panels ---------------------------------------------------------- */
.card{background:var(--panel);border:1px solid var(--rule);border-radius:3px;padding:16px 18px}
.card.narrow{max-width:360px;margin:56px auto}
.note{padding:11px 14px;border-radius:3px;margin:14px 0;font-size:13px;border:1px solid}
.note.ok{background:rgba(45,212,191,.08);color:var(--print);border-color:rgba(45,212,191,.3);word-break:break-all}
.note.bad{background:rgba(251,146,60,.08);color:var(--waste);border-color:rgba(251,146,60,.3)}

form{display:flex;flex-direction:column;gap:9px;margin-top:14px}
input,button,select{font:inherit;padding:10px 12px;border-radius:3px;border:1px solid var(--rule-bright)}
input,select{background:#0B0E13;color:var(--ink)}
button{background:var(--ink);color:var(--void);border:0;font-weight:600;cursor:pointer;
  font-size:13px;letter-spacing:.02em}
button:hover{background:#fff}
button.ghost{background:transparent;border:1px solid var(--rule-bright);color:var(--ink-2)}
button.ghost:hover{color:var(--ink);border-color:var(--link);background:transparent}

/* ---- readouts: the machine's own counter language --------------------- */
.readout{font-family:var(--mono);font-variant-numeric:tabular-nums;font-weight:500;
  letter-spacing:-0.02em}
.stat{display:flex;flex-direction:column;gap:3px}
.stat .lab{font-family:var(--display);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-3);font-weight:600}
.stat .val{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:26px;font-weight:500}
/* The unit is part of the readout, not a footnote on it. Two thirds of the
   figure reads as "0 m" the way the counter on the machine would say it;
   at 11px it had shrunk to an annotation. */
.stat .val small{font-size:17px;font-weight:500;color:var(--ink-3);margin-left:5px;
  letter-spacing:.06em;font-family:var(--mono);text-transform:uppercase}
.statrow{display:grid;grid-template-columns:repeat(auto-fit,minmax(104px,1fr));gap:1px;
  background:var(--rule);border:1px solid var(--rule);border-radius:3px;overflow:hidden}
.statrow>div{background:var(--panel);padding:13px 15px}
.v-print{color:var(--print)}.v-waste{color:var(--waste)}.v-idle{color:var(--idle)}

/* ---- fleet ----------------------------------------------------------- */
.grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fill,minmax(268px,1fr))}
.machine{display:block;background:var(--panel);border:1px solid var(--rule);
  border-left:2px solid var(--idle);border-radius:3px;padding:15px 16px;transition:border-color .15s}
.machine:hover{border-color:var(--rule-bright);border-left-color:var(--brand)}
.machine.is-running{border-left-color:var(--print)}
.machine.is-stopped{border-left-color:var(--waste)}
.mhead{display:flex;justify-content:space-between;align-items:center}
.serial{font-family:var(--mono);font-weight:600;font-size:20px;letter-spacing:.06em}
.mname{color:var(--ink-3);font-size:12px;margin:1px 0 11px;letter-spacing:.03em}
.state{font-size:9px;letter-spacing:.14em;text-transform:uppercase;padding:3px 7px;
  border-radius:2px;background:#171C24;color:var(--ink-3);font-weight:700}
.state.run{background:rgba(45,212,191,.13);color:var(--print)}
.state.stop{background:rgba(251,146,60,.13);color:var(--waste)}
.state.warn{background:rgba(244,63,94,.13);color:var(--alarm)}
.speed{font-family:var(--mono);font-size:36px;font-weight:500;letter-spacing:-0.03em;
  font-variant-numeric:tabular-nums}
.speed small{font-size:10px;color:var(--ink-3);font-weight:400;margin-left:5px;letter-spacing:.08em}
.job{color:var(--ink-2);font-size:13px;margin:5px 0 11px;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.stats{display:flex;gap:18px;border-top:1px solid var(--rule);padding-top:10px}
.stats.wide{margin-top:13px}
.stats label{font-family:var(--display);display:block;font-size:11px;color:var(--ink-3);text-transform:uppercase;
  letter-spacing:.14em;font-weight:600}
.stats span{font-family:var(--mono);font-variant-numeric:tabular-nums;font-weight:500;font-size:16px}

/* ---- live panel ------------------------------------------------------ */
/* The state of the press, readable from across a room and without reading a
   word. The tag beside it says the same thing in letters, so this is never
   colour alone. */
.live{border-left:4px solid var(--idle);transition:border-left-color .4s}
/* Borrowed from the machine's own stack light, where steady means settled and
   flashing means it wants you: the press flashes red when it is stopped but
   ready to run. Offline is grey rather than red, because red should mean the
   machine is in trouble, not that the internet is. */
.live[data-state="offline"] {border-left-color:#3A4250}
.live[data-state="stopped"] {animation:edgeflash 1.5s steps(1,end) infinite}
.live[data-state="setup"]   {border-left-color:var(--waste)}
.live[data-state="printing"]{border-left-color:var(--print)}
@keyframes edgeflash{
  0%,49%  {border-left-color:#E5484D}
  50%,100%{border-left-color:#3A2024}
}
@media (prefers-reduced-motion:reduce){
  .live[data-state="stopped"]{animation:none;border-left-color:#E5484D}
}
.livehead{display:flex;align-items:center;gap:8px 10px;flex-wrap:wrap;margin-bottom:9px}
/* The job name takes the room; the pills give it up. */
.livehead strong{min-width:0;font-size:19px}
.chips{margin-left:auto;display:flex;align-items:center;gap:6px;flex-wrap:wrap;
  justify-content:flex-end}
.tag{background:var(--print);color:#04211D;font-size:10px;font-weight:800;padding:3px 7px;
  border-radius:2px;letter-spacing:.14em}
.tag.stopped{background:var(--waste);color:#2A1300}
.tag.off{background:#232A35;color:var(--ink-3)}
.chip{font-size:11px;color:var(--ink-2);border:1px solid var(--rule-bright);padding:2px 7px;
  border-radius:2px;letter-spacing:.06em;font-family:var(--mono)}
.chip.on{color:var(--print);border-color:rgba(45,212,191,.45)}
.chip:empty{display:none}
.bignum{font-family:var(--mono);font-size:50px;font-weight:500;letter-spacing:-0.04em;
  font-variant-numeric:tabular-nums;line-height:1.05}
.bignum small{font-size:11px;color:var(--ink-3);font-weight:400;margin-left:7px;letter-spacing:.1em}

/* ---- tables ---------------------------------------------------------- */
.tablewrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
table.jobs{width:100%;border-collapse:collapse;font-size:14px}
table.jobs th{font-family:var(--display);text-align:left;color:#D0CEC9;font-weight:600;font-size:13px;
  text-transform:uppercase;letter-spacing:.1em;padding:11px 8px 9px;
  border-bottom:1px solid var(--rule-bright);vertical-align:bottom}
table.jobs td{padding:10px;border-bottom:1px solid var(--rule)}
table.jobs td small{color:var(--ink-3)}
table.jobs tbody tr:hover{background:var(--panel-2)}
.r{text-align:right;font-family:var(--mono);font-variant-numeric:tabular-nums}
table.jobs th.r,th.r{text-align:right}
/* Figures sit centred under their heading rather than pushed to the column
   edge. With a unit on every value there is no decimal to align anyway, and
   right-aligning made each number drift away from the label above it. */
.c{text-align:center;font-family:var(--mono);font-variant-numeric:tabular-nums}
table.jobs th.c,th.c{text-align:center}
.strong{font-weight:600}
tr.clickable{cursor:pointer}

.flag{font-size:9px;padding:2px 6px;border-radius:2px;background:#1A202A;color:var(--ink-3);
  margin-right:3px;letter-spacing:.08em;text-transform:uppercase;font-weight:600}
.flag.warn{background:rgba(251,146,60,.14);color:var(--waste)}

/* ---- job detail ------------------------------------------------------ */
.jobgrid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));margin:16px 0}
/* A label that may wrap beside a value that must not. Grid rather than
   space-between flex: the value column sizes to its content, so the numbers in
   a card line up with each other instead of drifting with the label length. */
.kv{display:grid;grid-template-columns:1fr auto;align-items:baseline;gap:4px 14px;
  padding:8px 0;border-bottom:1px solid var(--rule);font-size:14px}
.kv:last-child{border-bottom:0}
.kv span{color:var(--ink-2);min-width:0}
.kv b{font-family:var(--mono);font-variant-numeric:tabular-nums;font-weight:500;
  white-space:nowrap;text-align:right}
/* Prose values (a reason, a material name) are allowed to wrap; they stay
   right-aligned so the column edge holds. */
.kv.txt b{white-space:normal}
.kv b.waste{color:var(--waste)}
/* When the card is too narrow for label and value side by side, stack them
   rather than break either one. */
@media (max-width:420px){
  .kv{grid-template-columns:1fr}
  .kv b{text-align:left}
}

/* A select is as wide as its longest option unless told otherwise, which is
   what pushed the admin forms past the edge of a 320px phone. */
input,select,textarea{max-width:100%}
.card,.cards2>*{min-width:0}

.timebar{display:flex;height:9px;border-radius:2px;overflow:hidden;margin:2px 0 12px;background:#0B0E13}
.timebar .seg{display:block;height:100%}
.seg.printing,.dot.printing{background:var(--print)}
.seg.setup,.dot.setup{background:var(--waste)}
.seg.idle,.dot.idle{background:#232A35}
.dot{display:inline-block;width:8px;height:8px;border-radius:1px;margin-right:7px;vertical-align:middle}
.chart{display:flex;align-items:flex-end;gap:1px;height:140px;padding:14px;overflow:hidden;
  background:var(--panel);border:1px solid var(--rule);border-radius:3px}
.chart .bar{flex:1 1 0;min-width:0;border-radius:1px 1px 0 0;background:var(--idle)}
.chart .bar.on{background:var(--print)}
.chart .bar.off{background:var(--waste)}
.chart .bar.idle{background:#1B212B}
.swatch{display:inline-block;width:9px;height:9px;border-radius:1px;margin:0 3px 0 10px;vertical-align:middle}
.swatch.on{background:var(--print)}.swatch.off{background:var(--waste)}.swatch.idle{background:#1B212B}

/* ---- admin ----------------------------------------------------------- */
form.inline{display:flex;flex-direction:row;gap:6px;margin:0;align-items:center}
form.inline input,form.inline select{padding:6px 8px;font-size:12px}
form.inline button{padding:6px 11px;font-size:12px}
.cards2{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(290px,1fr));margin:16px 0 8px}

body.disconnected .live{opacity:.5}
body.disconnected::after{content:"Connection lost \2014 retrying";position:fixed;bottom:0;
  left:0;right:0;background:var(--waste);color:#2A1300;text-align:center;font-size:11px;
  padding:7px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;z-index:9}

/* ---- phone: no sideways scrolling ------------------------------------ */
@media (max-width:820px){
  main{padding:16px 14px 56px}
  .grid{grid-template-columns:1fr}
  .bignum{font-size:40px}
}
@media (max-width:680px){
  .bar{padding:10px 14px}
  .bar .who{display:none}
  .bar nav{gap:14px}
  h1{font-size:19px}
  .cards2{grid-template-columns:1fr}
  form.inline{flex-wrap:wrap}
  button,input,select{min-height:44px}

  /* A jobs table on a phone becomes a stack of cards. Scrolling a table
     sideways to reach the wastage figure is the worst way to read it. */
  table.jobs{display:block}
  table.jobs thead{display:none}
  table.jobs tbody{display:block}
  table.jobs tr{display:grid;grid-template-columns:1fr 1fr;gap:5px 12px;
    background:var(--panel);border:1px solid var(--rule);border-radius:3px;
    padding:12px 14px;margin-bottom:9px}
  table.jobs td{display:flex;justify-content:space-between;align-items:baseline;
    border:0;padding:1px 0;font-size:13px}
  table.jobs td::before{content:attr(data-l);font-size:9px;letter-spacing:.12em;
    text-transform:uppercase;color:var(--ink-3);font-weight:600;margin-right:10px}
  table.jobs td.wide{grid-column:1/-1}
  table.jobs td.r{text-align:left}
  .tablewrap{overflow-x:visible}

  /* The admin tables borrow the jobs styling but hold controls, not figures.
     A select and a button will not sit beside their own label in half a phone
     width, so the label goes above the cell and the cell gets the full width,
     the same shape the kv rows take when they run out of room. */
  table.jobs.admin tr{grid-template-columns:1fr;gap:0}
  table.jobs.admin td{display:block;padding:9px 0;
    border-bottom:1px solid var(--rule);font-size:14px}
  table.jobs.admin tr td:last-child{border-bottom:0}
  table.jobs.admin td::before{display:block;margin:0 0 5px}
  table.jobs.admin td.r{text-align:left}
  /* Nothing inside a cell may be wider than the cell. */
  table.jobs.admin form.inline{flex-wrap:wrap;justify-content:flex-start}
  table.jobs.admin input,table.jobs.admin select{max-width:100%}
}

/* ---- fleet view toggle ------------------------------------------------ */
.viewtoggle{display:flex;gap:0;border:1px solid var(--rule-bright);border-radius:3px;overflow:hidden}
.viewtoggle button{border:0;border-radius:0;padding:7px 15px;font-size:10px;letter-spacing:.14em;
  text-transform:uppercase;font-weight:600;background:transparent;color:var(--ink-3);min-height:0}
.viewtoggle button.on{background:var(--brand);color:#fff}
.viewtoggle button:hover{color:var(--ink);background:var(--panel)}

/* Lifetime closes the page: the same figures, read as context rather than
   news. Quieter, but still real numbers -- not footer text. */
.statrow.quiet>div{background:transparent;padding:11px 15px}
.statrow.quiet{background:var(--rule);border-color:var(--rule)}
.statrow.quiet .val{font-size:15px;color:var(--ink-2);font-weight:400}
.statrow.quiet .val small{font-size:9px}
.statrow.quiet .v-print{color:#1F9E90}
.statrow.quiet .v-waste{color:#C2703A}

/* ---- radial readouts -------------------------------------------------- */
/* A press has a speed dial and a shift has a proportion of itself used.
   Both are bounded single values, which is what a radial form is actually
   for -- not decoration, and not used anywhere a bar would read better. */
.nowgrid{display:grid;grid-template-columns:auto 1fr;gap:28px;align-items:center}
.dial{position:relative;width:196px;height:196px;flex-shrink:0}
/* 135deg puts the start of the sweep at the lower left, so the 270-degree
   scale ends at the lower right with the gap at the bottom -- a gauge, not a
   pie. */
.gauge{width:100%;height:100%;transform:rotate(135deg)}
.gauge circle{fill:none;stroke-linecap:butt}
.gauge .track{stroke:#191F28;stroke-width:15}
.gauge .fill{stroke:var(--print);stroke-width:15;transition:stroke-dasharray .6s cubic-bezier(.2,.8,.2,1)}
.gauge .tick{stroke:var(--ink-3);stroke-width:2;opacity:.55}
.dialmin,.dialmax{position:absolute;bottom:14px;font-family:var(--mono);font-size:11px;
  color:var(--ink-3);font-variant-numeric:tabular-nums}
.dialmin{left:20px}.dialmax{right:16px}
/* The same end labels for the smaller card gauges. A gauge that does not say
   where its scale ends is decoration; "Scale 0-20%" in the key list below was
   reading as though it were data. */
.gmin,.gmax{position:absolute;bottom:16px;font-family:var(--mono);font-size:11px;
  color:var(--ink-3);font-variant-numeric:tabular-nums}
.gmin{left:16px}.gmax{right:14px}
.gauge .fill.waste{stroke:var(--waste)}
.dialtext{position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:1px}
.dialtext .bignum{font-size:56px;line-height:1}
.dialunit{font-family:var(--mono);font-size:11px;letter-spacing:.2em;color:var(--ink-3)}
.nowbody{min-width:0}
.nowstats{display:grid;grid-template-columns:repeat(auto-fit,minmax(96px,1fr));gap:18px;
  margin-top:18px;padding-top:16px;border-top:1px solid var(--rule)}

.ringwrap{position:relative;width:180px;height:180px;margin:2px auto 14px}
/* Every radial in the product is this one shape at this one weight. */
.gauge .seg-print,.gauge .seg-setup,.gauge .seg-waste{stroke-width:15}
.gauge .seg-print{stroke:var(--print)}
.gauge .seg-setup{stroke:var(--waste)}
.gauge .seg-waste{stroke:var(--waste)}
.ringtext{position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center}
.ringbig{font-family:var(--mono);font-size:34px;font-weight:500;letter-spacing:-.03em;
  font-variant-numeric:tabular-nums}
.ringlab{font-family:var(--mono);font-size:9px;letter-spacing:.18em;color:var(--ink-3);margin-top:3px}
.ringkey{display:flex;flex-direction:column;gap:7px;font-size:13px;color:var(--ink-2)}
.ringkey span{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
.ringkey b{font-family:var(--mono);color:var(--ink);font-weight:500}
.ringkey i{display:inline-block;width:9px;height:9px;border-radius:1px;margin-right:8px}
.k-print{background:var(--print)}.k-setup{background:var(--waste)}.k-idle{background:#232A35}

.todaygrid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
.todaygrid .wide{grid-column:1/-1}

/* ---- hourly bars ------------------------------------------------------ */

@media (max-width:820px){
  .nowgrid{grid-template-columns:1fr;gap:18px;justify-items:center;text-align:center}
  .nowbody{width:100%}
  .livehead{justify-content:center}
  .nowstats{text-align:left}
}

@media (max-width:680px){
  .bar{height:52px;padding:8px 14px}
  .brand img.co{height:22px}
  .brand span{font-size:13px;padding-left:9px}
  .brand .mark{height:19px;width:19px}
  h1{font-size:27px}
}

/* ---- jobs carry the page ---------------------------------------------- */
table.jobs tbody tr{border-left:2px solid transparent}
table.jobs tbody tr:hover{background:var(--panel-2);border-left-color:var(--brand)}
table.jobs td{padding:13px 8px}


/* ---- job detail ------------------------------------------------------- */
.jobhead{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;
  flex-wrap:wrap;margin-bottom:20px}
.jobflags{display:flex;gap:5px;flex-wrap:wrap}
.hero{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:1px;
  background:var(--rule);border:1px solid var(--rule);border-radius:3px;overflow:hidden;
  margin-bottom:14px}
.heronum{background:var(--panel);padding:20px 22px;display:flex;flex-direction:column;gap:4px}
.heronum .lab{font-family:var(--display);font-size:11px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--ink-3);font-weight:600}
.heronum .big{font-family:var(--mono);font-size:42px;font-weight:500;letter-spacing:-.035em;
  font-variant-numeric:tabular-nums;line-height:1.05}
.heronum .big small{font-size:13px;color:var(--ink-3);margin-left:5px;letter-spacing:.1em}
.heronum .sub{font-size:12px;color:var(--ink-3)}
/* Two readings of one fact, equally sized: the percentage answers "how bad",
   the metres answer "how much". Either alone invites the wrong question. */
.bigpair{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap}
.bigpair .big{display:inline-flex;align-items:baseline}
/* Without a divider "0.1% 0 M" reads as one number. */
.bigrule{width:1px;height:30px;align-self:center;background:rgba(255,255,255,.13)}
.wastebox{background:linear-gradient(180deg,rgba(251,146,60,.05),transparent)}
.wastebar{height:5px;background:#1B212B;border-radius:3px;overflow:hidden;margin-top:8px}
.wastebar span{display:block;height:100%;background:var(--waste)}
.ringwrap.sm{width:150px;height:150px}
.ringwrap.sm .ringbig{font-size:28px}
.ringwrap.sm .gmin,.ringwrap.sm .gmax{bottom:11px;font-size:10px}
.ringwrap.sm .gmin{left:11px}.ringwrap.sm .gmax{right:9px}

/* ---- timeline --------------------------------------------------------- */
.timeline{border-left:1px solid var(--rule);margin-left:62px;padding-left:0}
.tl{position:relative;display:flex;align-items:baseline;gap:14px;padding:9px 0 9px 20px}
.tltime{position:absolute;left:-62px;width:52px;text-align:right;font-family:var(--mono);
  font-size:11px;color:var(--ink-3)}
.tldot{position:absolute;left:-4px;top:15px;width:7px;height:7px;border-radius:50%;
  background:var(--ink-3);border:2px solid var(--void)}
.tl.stoppage .tldot{background:var(--waste)}
.tl.counter_reset_midjob .tldot{background:var(--brand)}
.tl.job_end .tldot{background:var(--print)}
.tlbody{font-size:14px;color:var(--ink-2)}
.tlbody strong{color:var(--ink);font-weight:600}

@media (max-width:680px){
  .heronum .big{font-size:34px}
  .timeline{margin-left:52px}
  .tltime{left:-52px;width:44px}
}

tr.revoked{opacity:.45}
tr.revoked td:first-child{text-decoration:line-through}
button.ghost.danger:hover{border-color:var(--brand);color:var(--brand)}
.chipform{display:inline-flex;margin:0 3px 3px 0}
.chip button{background:none;border:0;color:var(--ink-3);font-size:13px;line-height:1;
  padding:0 0 0 6px;min-height:0;cursor:pointer}
.chip button:hover{color:var(--brand);background:none}

/* Units, set small and muted beside the figure. Without them a column of bare
   numbers is ambiguous the moment the eye loses the header -- which is exactly
   what happened when the columns slipped. */
/* The unit belongs to the column, not to every value in it. Saying it once in
   the header frees the width the figures were spending on repetition -- which
   is what the job name needed. */
/* The unit is the only thing telling you what the figure below it means, so
   it has to be readable rather than merely present. Lower case and lighter
   than the label, but at a size you do not have to lean in for. */
table.jobs th b{display:block;font-weight:500;font-size:12px;letter-spacing:.03em;
  color:var(--ink-2);margin-top:4px;text-transform:none;font-family:var(--mono)}
table.jobs td.c,table.jobs td.r{white-space:nowrap}

/* ---- frozen table header ---------------------------------------------- */
/* The header pins and the rows flow under it, so a long job list never leaves
   you scrolling back up to remember which column is which. The container has
   to own the scrolling for sticky to have anything to stick to. */
.tablewrap{max-height:min(70vh,620px);overflow:auto}
table.jobs thead th{position:sticky;top:0;z-index:2;background:var(--panel);
  box-shadow:inset 0 -1px 0 var(--rule-bright)}
/* The header sits on the page background, not a panel, so give it one. */
.tablewrap{background:var(--panel);border:1px solid var(--rule);border-radius:3px}
/* Domains are user-entered and can be long; break rather than overflow. */
form.inline.endy{justify-content:flex-end}
.chipform{max-width:100%}
.chip{max-width:100%;overflow-wrap:anywhere}
table.jobs{margin:0}
table.jobs td:first-child,table.jobs th:first-child{padding-left:16px}
table.jobs td:last-child,table.jobs th:last-child{padding-right:16px}
table.jobs tbody tr:last-child td{border-bottom:0}

@media (max-width:680px){
  /* Rows are cards here, so there is nothing to freeze and nothing to scroll
     inside -- let the page do it. */
  .tablewrap{max-height:none;overflow:visible;background:transparent;border:0}
  table.jobs thead th{position:static}
}

/* ---- column families --------------------------------------------------
   Three related groups: how fast it ran, how much it made, how long it took.
   Tinting them lets the eye jump to a group without reading headers.
   Validated for colour blindness -- worst adjacent pair dE 22.6 (deutan),
   24.3 normal vision.
   Deliberately NOT the status colours: teal and orange mean printing and
   waste in the ribbon and the rings, and reusing them here would say
   something these columns do not mean. Stopped time in particular is not a
   fault, which is why it is no longer amber. */
.f-speed{color:#6FB6F2}
.f-qty  {color:#C3D96B}
.f-time {color:#C79BE8}
/* Slightly softened in the header so a tinted label and a plain one carry the
   same weight -- the colour groups the columns, it does not rank them. */
table.jobs th.f-speed{color:#6FB6F2}
table.jobs th.f-qty  {color:#C3D96B}
table.jobs th.f-time {color:#C79BE8}
/* units stay quiet so the figures carry the colour */


/* Fixed layout so the colgroup widths are honoured and the columns stop
   shifting as values change length. */
table.jobs{table-layout:fixed}
/* The admin tables carry selects and buttons of their own natural width, and
   a fixed layout gave every column an equal seventh, so the controls in one
   cell ran over the button in the next. */
table.jobs.admin{table-layout:auto}
table.jobs.admin td,table.jobs.admin th{white-space:nowrap}
.jobname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media (max-width:680px){
  /* rows are cards here; the fixed widths would fight the layout */
  table.jobs{table-layout:auto}
  .jobname{white-space:normal;overflow:visible}
}

/* The job is what a customer opens this page to read. It should win the row
   before any figure does, so it carries the display face at the size of a
   heading rather than of table text. */
.jobname{font-family:var(--display);font-size:22px;font-weight:600;color:var(--ink);
  letter-spacing:.01em;line-height:1.15}
table.jobs td:first-child a{font-family:var(--display);font-size:19px;font-weight:600;
  color:var(--ink-2)}
table.jobs tbody tr:hover td:first-child a{color:var(--brand)}
@media (max-width:680px){
  .jobname{font-size:20px}
  table.jobs td::before{align-self:center}
}

/* The day's output, read before the rings that explain its shortfall. */
.statrow.today{margin-bottom:12px}
.statrow.today .val{font-size:30px}

/* ---- period picker ---------------------------------------------------- */
h2 .periodpick{display:inline-flex;gap:0;border:1px solid var(--rule-bright);border-radius:3px;
  overflow:hidden;margin-left:4px}
h2 .periodpick a{padding:4px 11px;font-size:10px;letter-spacing:.14em;text-transform:uppercase;
  font-weight:600;color:var(--ink-3);background:transparent}
h2 .periodpick a:hover{color:var(--ink);background:var(--panel-2)}
h2 .periodpick a.on{background:var(--brand);color:#fff}

/* A count has no maximum, so it gets a figure rather than a dial. Sized to
   sit beside the rings without pretending to be one. */
.countwrap{height:180px;display:flex;flex-direction:column;align-items:center;
  justify-content:center;margin:2px 0 14px}
.countbig{font-family:var(--mono);font-size:76px;font-weight:500;letter-spacing:-.05em;
  line-height:1;font-variant-numeric:tabular-nums}
.countlab{font-family:var(--mono);font-size:9px;letter-spacing:.18em;color:var(--ink-3);
  margin-top:8px}
.gauge .peak{stroke:var(--ink);stroke-width:3}

/* ---- the narrowest phones (320px) -------------------------------------
   Written last so it beats the 680px layout above it. */
@media (max-width:370px){
  /* Two figures per row stop fitting once the job name is a heading. */
  table.jobs tr{grid-template-columns:1fr}
  /* The wordmark is the first thing to give way; the logo still says who we
     are, and the nav has to stay reachable. */
  /* The word gives way, never the mark: the mark still says which product
     this is, and it is the only thing that does once the wordmark goes. */
  .brand .word{display:none}
  .brand .vue{padding-left:9px}
  .bar nav{gap:12px;font-size:12px}
}
.bar nav a{white-space:nowrap}

/* ---- paging the job history ------------------------------------------
   Twenty at a time. A press doing ten jobs a day fills a page in two days, and
   an unbounded table pushes the period cards below the fold on every visit. */
.pager{display:flex;align-items:center;justify-content:space-between;gap:16px;
  flex-wrap:wrap;margin:12px 0 4px;font-size:13px}
.pager .count{color:var(--ink-3);font-family:var(--mono);font-size:12px}
.pager .pages{display:flex;align-items:center;gap:2px;
  border:1px solid var(--rule-bright);border-radius:3px;overflow:hidden}
.pager .pages a{padding:7px 14px;color:var(--ink-2);font-size:12px;
  letter-spacing:.04em;white-space:nowrap}
.pager .pages a:hover{background:var(--panel-2);color:var(--ink)}
.pager .pages a.off{color:var(--ink-3);opacity:.4;pointer-events:none}
.pager .where{padding:7px 12px;font-family:var(--mono);font-size:12px;
  color:var(--ink-3);border-left:1px solid var(--rule);
  border-right:1px solid var(--rule)}
@media (max-width:480px){
  .pager{justify-content:center}
  .pager .pages{width:100%;justify-content:space-between}
}

/* ---- settings: adding things -----------------------------------------
   A token is shown once and never again, so the panel that shows it has to be
   impossible to scroll past. */
.tokenbox{border:1px solid var(--brand);border-radius:3px;background:var(--panel);
  padding:16px 18px;margin:0 0 16px}
.tokenbox .lab{font-family:var(--display);font-size:11px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--ink-3);font-weight:600;margin-bottom:8px}
.tokenbox code{display:block;font-family:var(--mono);font-size:15px;color:var(--ink);
  background:var(--void);border:1px solid var(--rule);border-radius:3px;
  padding:11px 13px;overflow-wrap:anywhere;user-select:all}
.tokenbox p{margin:10px 0 0}
.addbox{margin:0 0 12px;border:1px solid var(--rule);border-radius:3px;
  background:var(--panel)}
.addbox summary{cursor:pointer;padding:11px 15px;font-family:var(--display);
  font-size:12px;letter-spacing:.14em;text-transform:uppercase;font-weight:600;
  color:var(--ink-2)}
.addbox summary:hover{color:var(--ink)}
.addbox[open] summary{border-bottom:1px solid var(--rule)}
.addform{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
  gap:12px;padding:15px}
.addform label{display:flex;flex-direction:column;gap:5px;font-size:12px;
  color:var(--ink-3);min-width:0}
.addform input,.addform select{width:100%}
.addform button{grid-column:1/-1;justify-self:start}
