/* =====================================================================
   Econaut — STP Digitalisation
   Overlay on assets/base-openrouter.css.

   The base file is the visual system from the IC Common Frame (Booth
   BUSN 43800), itself re-based on OpenRouter's live tokens. It is loaded
   unchanged so the two apps stay in step. This file changes exactly what
   Econaut branding requires and nothing else:

     1. the accent, from OpenRouter grape #7624f4 to Econaut teal #77bcc0,
        sampled from the wordmark in Product Development Collateral
     2. the brand lockup in the header
     3. components the IC app never needed: the process twin, sparklines,
        the channel and register table

   Two base rules are kept and are load-bearing here too:
     Provenance is never colour alone. Every mark ships a text label.
     Both themes are first-class.
   ===================================================================== */

/* No text selection anywhere in the app — it's a dashboard, not a document.
   Restore for form elements so the user can still select/copy their own input. */
*, *::before, *::after { user-select: none; -webkit-user-select: none; }
input, textarea, [contenteditable], select { user-select: text; -webkit-user-select: text; }

:root {
  --accent:        #3f9298;          /* teal, darkened for AA on --plane */
  --accent-fg:     #ffffff;
  --accent-subtle: #eaf5f6;
  --brand-teal:    #77bcc0;          /* the wordmark colour, as sampled */
}
:root[data-theme='dark'] {
  --accent:        #77bcc0;          /* the wordmark reads well on ink */
  --accent-fg:     #03080a;
  --accent-subtle: #10201f;
}
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme='light'])) {
    --accent: #77bcc0; --accent-fg: #03080a; --accent-subtle: #10201f;
  }
}

/* ---------------------------------------------------------- brand lockup */
header.app .brand { display: flex; flex-direction: row; align-items: center; gap: 14px; margin-right: auto; }
header.app .brand img { height: 26px; width: auto; display: block; flex: none; }
header.app .brand .lockup { display: flex; flex-direction: column; gap: 2px; }
header.app .brand h1 { font: var(--t-title); margin: 0; letter-spacing: -0.01em; }
header.app .brand p  { font: var(--t-meta); color: var(--muted); margin: 0; }
.brand-rule { width: 1px; height: 26px; background: var(--rule); }

/* -------------------------------------------------------- plant selector */
/* 22px horizontal padding matches header.app and nav.tabs, so the plant
   selector lines up with the brand above it and the tabs below it rather
   than running into the left edge of the window. */
.plantbar {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 12px 22px 12px; background: var(--surface-1);
  border-bottom: 1px solid var(--border); margin-bottom: 0;
}
.plantbar select {
  font: var(--t-ui); color: var(--ink); background: var(--surface-1);
  border: 1px solid var(--border); border-radius: var(--radius-md); padding: 7px 10px;
}
.livechip {
  display: inline-flex; align-items: center; gap: 7px; font: var(--t-meta);
  padding: 5px 11px; border-radius: var(--radius-full);
  background: var(--verifiable-wash); color: var(--verifiable-text);
  border: 1px solid var(--verifiable-edge);
}
.livechip.warn { background: #fdf6e6; color: var(--warning-text); border-color: rgba(229,160,0,.25); }
:root[data-theme='dark'] .livechip.warn { background: #241d07; }
.livechip .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }

/* ------------------------------------------------------------ process twin */
.twin-wrap { position: relative; background: var(--surface-1); border: 1px solid var(--border);
             border-radius: var(--radius); padding: 10px; overflow-x: auto; }
.twin { display: block; width: 100%; min-width: 900px; height: auto; }
.twin .vessel { fill: var(--accent-subtle); stroke: var(--rule); stroke-width: 1.5; }
.twin .liquid  { fill: var(--brand-teal); opacity: .34; }
.twin .pipe    { fill: none; stroke: var(--rule); stroke-width: 2.5; stroke-linecap: round; }
.twin .pipe.on { stroke: var(--accent); opacity: .85; }
.twin .lbl     { font: 500 11px/1 var(--sans); fill: var(--ink-2); }
.twin .val     { font: 600 13px/1 var(--mono); fill: var(--ink); }
.twin .val.na  { fill: var(--muted); font-weight: 450; }
.twin .val.alarm { fill: var(--critical-text); }
.twin .tagbox  { fill: var(--surface-2); stroke: var(--border); stroke-width: 1; rx: 4; }
.twin .machine { fill: var(--surface-2); stroke: var(--rule); stroke-width: 1.5; }
.twin .machine.running { fill: var(--accent-subtle); stroke: var(--accent); }

/* --------------------------------------------------------------- channels */
.chan-table { width: 100%; border-collapse: collapse; font: var(--t-body); }
.chan-table th {
  text-align: left; font: 600 11px/1.3 var(--sans); text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted); padding: 8px 10px;
  border-bottom: 1px solid var(--rule); white-space: nowrap;
}
.chan-table td { padding: 9px 10px; border-bottom: 1px solid var(--grid); vertical-align: middle; }
.chan-table td.num, .chan-table th.num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }
.chan-table tr:hover td { background: var(--wash); }
.reg { font: 450 12px/1 var(--mono); color: var(--muted); }

/* status marks always carry a word, never colour alone */
.mark { display: inline-flex; align-items: center; gap: 6px; font: var(--t-meta); white-space: nowrap; }
.mark .sq { width: 8px; height: 8px; border-radius: 2px; flex: none; }
.mark.ok   { color: var(--verifiable-text); } .mark.ok   .sq { background: var(--verifiable); }
.mark.na   { color: var(--muted); }           .mark.na   .sq { background: var(--track); }
.mark.gap  { color: var(--warning-text); }    .mark.gap  .sq { background: var(--warning); }
.mark.high { color: var(--critical-text); }   .mark.high .sq { background: var(--critical); }

/* -------------------------------------------------------------- sparkline */
.spark { display: block; width: 100%; height: 40px; }
.spark path.line { fill: none; stroke: var(--accent); stroke-width: 1.6; stroke-linejoin: round; }
.spark path.area { fill: var(--accent); opacity: .10; stroke: none; }
.spark rect.gap  { fill: var(--warning); opacity: .16; }

/* Height follows width so the viewBox scales uniformly. It used to be pinned
   at 260px with preserveAspectRatio="none", which fills the card but scales x
   and y by different factors, and an SVG scales its TEXT along with everything
   else: the axis numbers and the consent label came out visibly stretched. */
.chart { display: block; width: 100%; height: auto; }
.chart .axis { stroke: var(--grid); stroke-width: 1; }
.chart .axis-text { font: 450 10px/1 var(--mono); fill: var(--muted); }
.chart .series { fill: none; stroke: var(--accent); stroke-width: 1.8; stroke-linejoin: round; }
.chart .limit { stroke: var(--critical); stroke-width: 1.2; stroke-dasharray: 4 3; opacity: .7; }
.chart .gapband { fill: var(--warning); opacity: .13; }

/* ------------------------------------------------------------------ misc */
.grid2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); gap: 14px; }
.note-inline { font: var(--t-meta); color: var(--muted); margin-top: 8px; }
/* A clock time with its day under it, small and muted ("07:02 AM" / "Sat 26 Sep").
   A night shift's clock-out is filed under the clock-in day, so the time alone
   read as the wrong day (26 Sep 2026). Used for every in/out time in Field. */
.tcell { display: inline-block; white-space: nowrap; vertical-align: top; }
.tday  { display: block; font: var(--t-meta); color: var(--muted); margin-top: 1px; white-space: nowrap; }
.callout {
  border: 1px solid var(--border); border-left: 3px solid var(--warning);
  background: var(--wash); border-radius: var(--radius-md); padding: 12px 14px; margin: 14px 0;
}
.callout h3 { font: var(--t-heading); margin: 0 0 4px; }
.callout p { font: var(--t-body); color: var(--ink-2); margin: 0; }
.callout.info { border-left-color: var(--info); }
.btn.primary { background: var(--accent); color: var(--accent-fg); border-color: transparent; }
.logrow { border-bottom: 1px solid var(--grid); padding: 10px 0; }
.logrow:last-child { border-bottom: 0; }
.logrow .when { font: var(--t-meta); color: var(--muted); }
.logrow .what { font: var(--t-body); color: var(--ink); }

/* ---------------------------------------------------------- field log tab
   The head-office view of what operators filed from their phones. Location
   is a first-class column here, so it gets a first-class token: green only
   where the fix actually falls inside the site geofence, amber otherwise,
   grey where there is no fix at all. Never colour alone: each pill carries
   the distance in words as well. */
.field-kpis {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 1px; background: var(--grid);
  border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; margin-bottom: 16px;
}
.pill {
  display: inline-block; font: var(--t-meta); padding: 2px 9px;
  border-radius: var(--radius-full); background: var(--wash); color: var(--ink-2);
  white-space: nowrap;
}
.pill.ok   { background: var(--verifiable-wash); color: var(--verifiable-text); }
.pill.warn { background: color-mix(in srgb, var(--asserted) 14%, var(--surface-1)); color: var(--warning-text); }
.na   { color: var(--muted); font-style: italic; }
.mono { font-family: var(--mono); }
.btn.ghost { background: transparent; border-color: var(--border); padding: 4px 10px; font: var(--t-meta); }
.field-controls { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }
.field-cards { display: grid; gap: 12px; margin-top: 14px;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
/* A demo fix gets its own colour. It must never borrow the green that means
   a real GPS reading fell inside the site geofence. */
.pill.sim { background: var(--info-wash); color: var(--info-text); }
.pill.bad { background: color-mix(in srgb, var(--critical) 12%, var(--surface-1)); color: var(--critical-text); }
/* A machine translation, shown as clearly secondary to the operator's own
   words. Indented and ruled rather than merely smaller, so nobody mistakes it
   for what the person actually wrote. */
.translation {
  margin: 10px 0 0; padding: 8px 0 0 11px;
  border-left: 2px solid var(--rule);
}
.translation .tlabel {
  font: var(--t-meta); color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em; margin-bottom: 5px;
}
.translation p { font: var(--t-body); color: var(--ink-2); margin: 0; white-space: pre-wrap; }
/* Indic scripts need their own fallback here too or they clip in the cards. */
[lang='kn'] { font-family: 'Plus Jakarta Sans', 'Noto Sans Kannada', 'Kannada Sangam MN', sans-serif; line-height: 1.6; }
[lang='hi'], [lang='mr'] { font-family: 'Plus Jakarta Sans', 'Noto Sans Devanagari', 'Devanagari Sangam MN', sans-serif; line-height: 1.6; }
[lang='ta'] { font-family: 'Plus Jakarta Sans', 'Noto Sans Tamil', 'Tamil Sangam MN', sans-serif; line-height: 1.6; }
[lang='te'] { font-family: 'Plus Jakarta Sans', 'Noto Sans Telugu', 'Telugu Sangam MN', sans-serif; line-height: 1.6; }

/* ------------------------------------------------------------- sign in
   Covers the whole page until we know who is looking, because the plant
   dropdown cannot be built until then. */
.gate {
  position: fixed; inset: 0; z-index: 80;
  display: flex; align-items: center; justify-content: center;
  background: var(--plane);
}
.gate[hidden] { display: none !important; }
.gate-card {
  width: 100%; max-width: 380px; padding: 30px 26px;
  display: flex; flex-direction: column; gap: 14px;
}
.gate-card .mark { height: 28px; width: auto; align-self: flex-start; }
.gate-card .sub {
  font: var(--t-meta); color: var(--muted); letter-spacing: .09em;
  text-transform: uppercase; margin: -6px 0 12px;
}
.gate-card label { display: flex; flex-direction: column; gap: 6px; font: var(--t-meta); color: var(--ink-2); }
.gate-card input {
  font: 450 15px/1.4 var(--sans); color: var(--ink); background: var(--surface-1);
  border: 1px solid var(--rule); border-radius: var(--radius-md);
  padding: 11px 12px; width: 100%;
}
.gate-card input:focus { outline: none; border-color: var(--focus-border); box-shadow: var(--focus-shadow); }
.gate-card .err { font: var(--t-meta); color: var(--critical-text); min-height: 16px; margin: 0; }
.whoami { font: var(--t-meta); color: var(--muted); margin-left: auto; }
header.app .linkbtn {
  background: none; border: none; cursor: pointer;
  font: var(--t-meta); color: var(--ink-2);
  text-decoration: underline; text-underline-offset: 3px; padding: 4px 2px;
}
/* SVG icon buttons in the management app header — same style as operator app. */
.icon-btn {
  background: none; border: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink-2); padding: 4px; border-radius: 4px; flex: none;
}
.icon-btn:hover { color: var(--ink); background: var(--surface-2); }
.icon-btn:focus-visible { outline: 2px solid var(--focus-border); outline-offset: 2px; }
.no-telemetry { padding: 26px; }

/* Who is in the plant right now. Large and first, because this is the number
   somebody dials when something is wrong. */
.onsite { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 14px; }
.onsite-card {
  border: 1px solid var(--border); border-left: 3px solid var(--verifiable);
  border-radius: var(--radius); padding: 13px 16px; min-width: 200px;
  background: var(--surface-1);
}
.onsite-card .who  { font: var(--t-heading); }
.onsite-card .what { font: var(--t-meta); color: var(--muted); margin-top: 2px; }
.onsite-card .num  { font: 600 17px/1.3 var(--mono); margin-top: 8px; }
.onsite-card .num a { color: var(--accent); text-decoration: none; }
.chan-table tfoot td { border-top: 2px solid var(--rule); padding: 10px; }
/* Live banner. Deliberately a different colour from the synthetic one: which
   of the two is on screen is the most important thing on the page. */
.synthetic-banner.live { background: var(--verifiable-wash); border-color: var(--verifiable-edge); }
.synthetic-banner.live .pill.ok { background: var(--verifiable); color: #fff; font-weight: 600; }

/* ---------------------------------------------------------- monthly reports
   Grouped by month, because a month is the unit people ask for. The class
   badge (MR/PM/LR/SR) is kept because operations staff use those letters, and
   the full label sits beside it for everyone else. */
.rep-grid { display:grid; gap:18px; margin-top:14px; }
.rep-month { border-top:1px solid var(--border); padding-top:12px; }
.rep-period { font:var(--t-heading); margin:0 0 8px; color:var(--ink); }
.rep-list { list-style:none; margin:0; padding:0; display:grid; gap:2px; }
.rep-row { display:flex; align-items:center; gap:12px; padding:7px 8px; border-radius:6px; }
.rep-row:hover { background:var(--surface-2); }
.rep-link { display:flex; align-items:center; gap:10px; flex:1; min-width:0;
            text-decoration:none; color:inherit; }
.rep-cls { flex:none; width:46px; text-align:center; font-family:var(--mono); font-size:11px;
           letter-spacing:.04em; padding:2px 0; border-radius:4px;
           background:var(--wash); color:var(--ink-2); }
.rep-label { font:var(--t-body); color:var(--ink); overflow:hidden;
             text-overflow:ellipsis; white-space:nowrap; }
.rep-link:hover .rep-label { text-decoration:underline; }
/* A report the app knows about but does not hold. It is a real filing and it
   is not openable here, so it must not underline on hover or take a pointer:
   an affordance that leads nowhere reads as a broken link, not as information. */
.rep-filed { cursor:default; }
.rep-filed:hover .rep-label { text-decoration:none; }
.rep-filed .rep-label { color:var(--ink-2); }
.rep-size { flex:none; font:var(--t-meta); color:var(--muted); font-variant-numeric:tabular-nums; }
.rep-size.heavy { color:var(--warning); }
@media (max-width:520px){ .rep-label{ font-size:.92em } .rep-cls{ width:40px } }

/* ------------------------------------------------------------- pdf viewer
   Full-screen sheet with our own close control. Sits above everything and
   respects the notch, because in standalone mode there is no browser chrome
   to push it down. */
.pdfview { position:fixed; inset:0; z-index:900; background:var(--surface-1);
           display:flex; flex-direction:column; }
body.pdf-open { overflow:hidden; }
.pdfbar { flex:none; display:flex; align-items:center; gap:12px;
          padding:calc(env(safe-area-inset-top) + 10px) 14px 10px;
          border-bottom:1px solid var(--border); background:var(--surface-2); }
.pdfclose, .pdfout { flex:none; font:var(--t-body); background:none; border:0;
          color:var(--accent); cursor:pointer; padding:6px 2px; text-decoration:none;
          /* 44px is Apple's minimum touch target; smaller gets mis-tapped */
          min-height:44px; display:flex; align-items:center; }
.pdftitle { flex:1; min-width:0; font:var(--t-body); color:var(--ink);
            text-align:center; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.pdfbody { flex:1; overflow:auto; -webkit-overflow-scrolling:touch;
           padding:12px 12px calc(env(safe-area-inset-bottom) + 12px);
           background:var(--wash); }
.pdfpage { display:block; margin:0 auto 12px; max-width:1200px;
           box-shadow:0 1px 4px rgba(0,0,0,.18); background:#fff; }
.pdfmsg { font:var(--t-body); color:var(--ink-2); text-align:center; padding:40px 20px; }

/* ------------------------------------------------- standalone (installed) app
   In a browser there is a URL bar and a status bar. Installed to the home
   screen there is neither, so the app owns the full screen including the area
   behind the notch and the home indicator. Without these insets the header
   sits under the clock and the last tab under the gesture bar. */
@media (display-mode: standalone) {
  body { padding-top:env(safe-area-inset-top); }
  .tabs { padding-left:max(14px, env(safe-area-inset-left));
          padding-right:max(14px, env(safe-area-inset-right)); }
  .panel { padding-bottom:calc(env(safe-area-inset-bottom) + 24px); }
}

/* Five tabs, and all five must be on screen at once on a phone. A strip you
   scroll sideways hides half the application from anyone who does not think to
   try, which is most people. So on narrow screens the tabs share the width
   equally and shrink to fit; on desktop they sit at their natural width. */
.tabs { display:flex; gap:2px; overflow:visible; flex-wrap:nowrap; }
.tabs [role=tab] { flex:0 0 auto; white-space:nowrap; min-height:44px; }

@media (max-width: 760px) {
  .tabs { gap:0; }
  .tabs [role=tab] {
    flex:1 1 0; min-width:0; padding-left:4px; padding-right:4px;
    font-size:13px; letter-spacing:-0.01em; text-align:center;
    overflow:hidden; text-overflow:ellipsis;
  }
}
@media (max-width: 380px) { .tabs [role=tab] { font-size:12px; } }



@media (max-width:640px) {
  /* Stop iOS zooming the page when a control is focused: it does that for any
     font-size below 16px and then never zooms back out, so the reader has to
     pinch out by hand after every use.

     ⚠️ A bare `input` selector is NOT enough, and this rule silently failed for
     a year because of it. Anything with higher specificity that sets `font`
     wins no matter where it sits in the file: `.plantbar input#plant-pick`
     (id + class + element) declares `font: inherit`, which inherits --t-body at
     14px, and `.subbar select` takes var(--t-body) the same way. The shorthand
     `font:` sets font-size, so it overrides this line completely.

     So every control that sets `font` or `font-size` elsewhere has to be named
     here too. If you add one, add it to this block. */
  input, select, textarea, button { font-size:16px; }
  .table-scroll { -webkit-overflow-scrolling:touch; }
  /* The specific controls are handled at the END of this file, not here. A
     media query adds NO specificity, so a later equal-specificity rule such as
     `.plantbar input#plant-pick { font: inherit }` beats anything written here
     purely by source order. See "iOS focus zoom" at the bottom. */
}

/* ------------------------------------------------------------- sub-tab strip
   Second level, visually quieter than the top strip so the hierarchy reads at
   a glance rather than looking like two rows of equal tabs. */
.subtabs { display:flex; gap:2px; overflow-x:auto; -webkit-overflow-scrolling:touch;
           scrollbar-width:none; margin:-2px 0 14px;
           padding-left:max(0px, env(safe-area-inset-left));
           padding-right:max(0px, env(safe-area-inset-right)); }
.subtabs::-webkit-scrollbar { display:none; }
.subtabs [role=tab] { flex:none; white-space:nowrap; min-height:38px;
           background:none; border:0; cursor:pointer; padding:6px 12px;
           font:var(--t-body); color:var(--muted); border-radius:6px; }
.subtabs [role=tab][aria-selected="true"] { color:var(--ink); background:var(--surface-2); }
.subtabs [role=tab]:hover { color:var(--ink); }

/* ------------------------------------------------------------- equipment
   Group cards carry the worst state inside them. Grey (not recorded) is a
   state in its own right and must not read as a pass. */
.eq-grid { display:grid; gap:8px; margin-top:12px;
           grid-template-columns:repeat(auto-fill, minmax(240px, 1fr)); }
/* Name and count share a line; the state sits under it. A tile per group with
   the number on its own row was three times taller for the same three facts. */
.eq-card { display:grid; grid-template-columns:1fr auto; gap:2px 10px;
           text-align:left; cursor:pointer; padding:10px 12px;
           border-radius:8px; border:1px solid var(--border);
           background:var(--surface-2); color:inherit; border-left-width:4px; }
.eq-card:hover { background:var(--wash); }
.eq-name  { font:var(--t-body); color:var(--ink); font-weight:600; align-self:center; }
.eq-count { font:var(--t-body); font-weight:600; color:var(--ink);
            font-variant-numeric:tabular-nums; align-self:center; }
.eq-state { font:var(--t-meta); color:var(--muted); grid-column:1 / -1; }
.eq-bad  { border-left-color:var(--critical); }
.eq-warn { border-left-color:var(--warning); }
.eq-ok   { border-left-color:var(--verifiable); }
.eq-na   { border-left-color:var(--absent); }
.eq-bad  .eq-state { color:var(--critical); }
.eq-warn .eq-state { color:var(--warning); }
.unset { color:var(--muted); font-style:italic; }


/* The sub-strip WRAPS rather than scrolls. A second row costs a few pixels; a
   hidden entry costs the feature, and a horizontally scrolled strip had
   already clipped "Process twin" to "ss twin" on a phone.
   This block sits at the end of the file deliberately: an earlier copy of it
   was placed above the base .subtabs rule and lost on source order. */
@media (max-width: 760px) {
  .subtabs { flex-wrap:wrap; overflow:visible; row-gap:4px; }
  .subtabs [role=tab] { flex:0 1 auto; padding:6px 10px; font-size:13px; }
}


/* ------------------------------------------------------- mobile decluttering
   The header carried a wordmark, a title, a subtitle, an identity line, a sign
   out and a three-way theme switch, stacked, before a single number appeared.
   On a phone that was most of the first screen. None of it is removed; it is
   just made to cost one row instead of four. */
@media (max-width: 760px) {
  header.app { display:grid; grid-template-columns:auto 1fr auto; align-items:center;
               gap:8px 10px; padding:10px 14px; }
  /* THE CHROME WAS FOUR ROWS DEEP before any data (Janardhan, 14 Sep 2026).
     "Field app" wrapped onto two lines because the three link buttons were
     auto-flowing into whatever grid cell was free, so one of them got a narrow
     column. Placed explicitly, on one row, and "Change password" is now just
     "Password": the button sits next to Sign out, nobody reads it as anything
     else. */
  header.app { grid-template-columns:auto 1fr auto; }
  /* THE NAME SITS BESIDE THE LOGO, not on a row of its own, and the product
     name comes off (Janardhan, 14 Sep 2026). "STP Digitalisation" is the answer
     to a question nobody asks twice, and it was costing a row on every screen
     along with the line under it. The wordmark still says what this is. Desktop
     keeps the lockup: it costs nothing there. */
  /* `header.app .brand .lockup` sets display:flex and is THREE classes deep, so
     a two-class rule loses however late it appears and whatever media query it
     sits in. A media query adds no specificity. This is the second time today
     the same trap has bitten (the other was a 13px input beating the 16px iOS
     zoom guard); it is written in IoT/CLAUDE.md and is still easy to walk into,
     so the selector below deliberately matches the original depth. */
  header.app .brand .lockup, header.app .brand-rule { display:none; }
  header.app .brand { grid-column:1; margin-right:0; }
  header.app .brand img { height:18px; }
  /* ONE ROW (Janardhan, 14 Sep 2026): logo, name, Password, Sign out and the
     theme icon side by side. The "Field app" link and the Light / Dark / Auto
     switch are gone, and they were the second and third rows. The name gives
     way first, with an ellipsis, because it is the one thing on the row the
     reader already knows. */
  header.app { display:flex; flex-wrap:nowrap; gap:10px; padding:8px 14px; }
  header.app .whoami { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis;
                       white-space:nowrap; font-size:12px; margin-left:0; text-align:right; }
  header.app .linkbtn { white-space:nowrap; font-size:11px; padding:2px 0; flex:none; }
  /* Same for the ticket pill. "No open tickets" is reassurance, not news, and
     it was costing a full row above the breadcrumb. */
  .ticketbar { padding:6px 14px 0; }
  .tkbtn { padding:4px 11px; min-height:30px; font-size:11px; gap:6px; }
  .tkbtn-dot { width:6px; height:6px; }
  .plantbar { padding:8px 14px; gap:8px 10px; }
  /* 65px for a single line of breadcrumbs. */
  nav.toprow { padding:6px 14px; }
  .crumb, .crumb-here { font-size:12px; }
}

/* The process schematic is 1180px wide and always will be: it is a plant, not a
   layout. Scrolling it sideways keeps every label readable, which scaling it to
   a 390px screen would not. The shadow on the right edge is the only cue people
   reliably notice that there is more to see. */
.twin-wrap { overflow-x:auto; -webkit-overflow-scrolling:touch; }
.twin-wrap .twin { min-width:900px; }
@media (max-width: 760px) {
  .twin-wrap { position:relative;
    -webkit-mask-image:linear-gradient(to right, #000 92%, rgba(0,0,0,.35) 100%);
            mask-image:linear-gradient(to right, #000 92%, rgba(0,0,0,.35) 100%); }
}


/* Second-level view picker. Replaces the sub-tab strip: at four or five
   entries a strip either scrolled (hiding the last entry, which is how
   Diagnostics went missing) or wrapped onto a second row above every screen. */
.subbar { display:flex; align-items:center; padding:10px 0 4px; }
.subbar select { font:var(--t-body); }


/* --------------------------------------------------------- hidden, actually
   The hidden ATTRIBUTE only sets display:none through a UA stylesheet rule of
   the lowest possible specificity, so any rule of ours that sets a display
   silently defeats it. That cost two visible bugs at once: an empty "View"
   picker on tabs with no sub-views (.subbar is display:flex) and a PDF viewer
   whose Close button did nothing (.pdfview is display:flex). Both elements
   were correctly marked hidden and both stayed on screen.
   One rule, applied once, rather than remembering it per component. */
[hidden] { display: none !important; }

/* The picker sat flush against the screen edge while every other bar had a
   14px gutter, so "View" was clipped. */
.subbar { padding-left:14px; padding-right:14px; }

/* KPI tiles wrap onto the next line rather than running off the right edge.
   Scrolling them sideways meant the fourth reading did not exist as far as
   anyone could tell, and swiping it bounced the whole page. */
@media (max-width: 760px) {
  .kpi-row { display:grid; grid-template-columns:repeat(2, minmax(0,1fr));
             gap:8px; overflow:visible; }
  .kpi-row .card.kpi { min-width:0; }
}


.dl-row { display:flex; flex-wrap:wrap; gap:8px; margin-top:10px; }

/* ------------------------------------------------------------------- print
   Save as PDF is window.print(), so this stylesheet IS the PDF layout.
   Everything that is navigation or control disappears; what is left is the
   plant, the date and the readings. */
@media print {
  /* The base stylesheet carries `body > *:not(#memo-print-root){display:none}`
     from a different project, which blanks this entire application on print.
     Same selector, later file, so this wins the cascade and gives the page
     back before anything below decides what to keep. */
  /* `:not([hidden])` is load bearing, and its absence is what produced blank
     pages and a PDF viewer bar printed over the charts.
     `display:revert !important` on `body > *:not(#id)` out-specifies the
     global `[hidden]{display:none!important}` — an id inside :not() counts —
     so the reset was REVIVING every hidden element: the PDF overlay, and every
     panel for a view that was not open. The empty ones printed as blank pages.
     Excluding hidden elements from the reset gives the page back without
     un-hiding what the application deliberately hid. */
  body > *:not(#memo-print-root):not([hidden]) { display: revert !important; }
  body > #gate, body > #memo-print-root, #pdfview,
  body > *[hidden], .panel[hidden] { display: none !important; }

  /* These need `body >` too: the reset above is `body > *:not(#id)`, which
     out-specifies a bare class selector, so `.tabs{display:none}` lost and the
     navigation printed into the PDF. */
  /* Controls are not content. Nothing that can be clicked belongs in a file
     somebody prints and hands to a client. */
  body > nav.tabs, body > .subbar, body > .pdfview, body > .ticketbar,
  header.app .seg-control, .theme-btn, #g-out, .dl-row, .no-print,
  .range-pick, .seg-control, #panel-menu, .toprow .crumb { display:none !important; }
  body { background:#fff; color:#000; }
  header.app { padding:0 0 8pt; border-bottom:1pt solid #999; }
  .plantbar { padding:6pt 0; border:0; }
  main { padding:0; }
  .card { border:1pt solid #ccc; box-shadow:none; break-inside:avoid;
          page-break-inside:avoid; background:#fff; }
  /* A chart split across a page break is unreadable, and a table header that
     does not repeat makes page four meaningless on its own. */
  /* Two charts per page rather than one: nine charts down a single column is
     nine pages of mostly margin. */
  .grid2 { display:grid; grid-template-columns:1fr 1fr; gap:8pt; }
  .grid2 > .card { margin-bottom:0; break-inside:avoid; page-break-inside:avoid; }
  .card { margin-bottom:8pt; }
  .chart, .spark { break-inside:avoid; }
  table { break-inside:auto; }
  thead { display:table-header-group; }
  tr { break-inside:avoid; }
  .table-scroll { overflow:visible !important; }
  a[href]::after { content:''; }   /* no printed URLs after every link */
  @page { margin:14mm 12mm; }
}

/* The day list reuses the report-row layout, but its rows are buttons rather
   than links, so they need the button chrome removed. */
.rep-list button.rep-link { background:none; border:0; font:inherit; cursor:pointer;
                            text-align:left; padding:0; color:inherit; }

/* ------------------------------------------------------- invoice drill-down
   Three columns on the row, the rest on tap. Seven columns never fitted a
   phone and four of them are only wanted once a line has been picked out. */
.inv-table .inv-row { cursor:pointer; }
.inv-table .inv-row:hover td { background:var(--surface-2); }
.inv-table .inv-row.open td { background:var(--surface-2); font-weight:600; }
/* The chevron used to float:right inside the description cell, so on a wide
   screen it drifted to the far edge of a very wide column, miles from both the
   text and the money, and at one glyph it was barely a target. Its own narrow
   column at the end of the row puts it where the eye already finishes. */
.inv-caret-cell { width:40px; text-align:center; vertical-align:middle; }
.inv-caret { display:inline-block; width:22px; height:22px; border-radius:50%;
             border:1px solid var(--border); color:var(--muted);
             transition:transform .12s, border-color .12s; position:relative; }
.inv-caret::before { content:''; position:absolute; inset:0; margin:auto;
             width:6px; height:6px; border-right:1.6px solid currentColor;
             border-bottom:1.6px solid currentColor; transform:rotate(-45deg) translate(-1px,-1px); }
.inv-row:hover .inv-caret { border-color:var(--accent); color:var(--accent); }
.inv-row.open .inv-caret { transform:rotate(90deg); border-color:var(--accent);
             color:var(--accent); }

/* PFD popup — floats over the drawing on equipment/tag click */
.pfd-popup { position:absolute; z-index:900; background:var(--surface-1);
             border:1px solid var(--border); border-radius:8px;
             padding:10px 14px; font:var(--t-body); color:var(--ink);
             box-shadow:0 4px 16px rgba(0,0,0,.15); max-width:240px;
             line-height:1.5; pointer-events:none; }
.pfd-synth { font-size:10px; font-weight:600; color:var(--accent);
             background:color-mix(in srgb,var(--accent) 12%,transparent);
             border-radius:3px; padding:1px 4px; margin-left:4px; }
.pfd-unverified { position:absolute; top:8px; right:12px; z-index:10;
                  background:#fef3c7; color:#92400e; border:1px solid #fcd34d;
                  border-radius:4px; font-size:10px; font-weight:600;
                  letter-spacing:.04em; padding:2px 7px; text-transform:uppercase;
                  pointer-events:none; }

/* The description column stretched to whatever the window was, which put a
   canyon between a two word description and its amount. Cap the table instead
   of the column, so the row still reads as one line on a laptop. */
.inv-table { max-width:960px; }
.inv-party { font-size:12px; color:var(--muted); max-width:180px; overflow:hidden;
             text-overflow:ellipsis; white-space:nowrap; }
.inv-table th:nth-child(3), .inv-table td:nth-child(3) { width:150px; }
.inv-table th:nth-child(4), .inv-table td:nth-child(4) { width:72px; }
.sr-only { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0);
           white-space:nowrap; }
.inv-table td.bad { color:var(--critical-text); font-weight:600; }
.inv-detail td { background:var(--wash); }
/* One site, more than one customer (25 Sep 2026): a heading row and a subtotal
   per Tally party, so a builder's closed contract and the society billed now
   read as two lists to chase. */
.inv-table .inv-group td { background:var(--surface-2); padding-top:12px; }
.inv-table .inv-subtotal td { border-top:1px solid var(--border); font-size:13px; }
.inv-split-note { margin-top:6px; }
.rc-split { margin-top:14px; }
.rc-split-site { margin-top:8px; }
.rc-split-list { margin:4px 0 0; padding-left:18px; }
.rc-split-list li { margin:2px 0; }
.rc-split-list .num { margin-left:6px; }
table.es.stack .rc-meta.rc-cust, .rc-meta.rc-cust { display:block; font-size:11px; color:var(--muted); margin:2px 0 0; }
.inv-dl { display:grid; grid-template-columns:auto 1fr; gap:4px 16px; margin:4px 0;
          font:var(--t-body); }
.inv-dl dt { color:var(--muted); }
.inv-dl dd { margin:0; color:var(--ink); }


.eq-card .eq-count:empty { display:none; }
.sub-back, .grp-back { display:inline-block; margin-bottom:10px; }

/* ------------------------------------------------------------------ tickets */
.rep-cls.warn { background:var(--warning); color:#fff; }
.rep-cls.ok   { background:var(--verifiable); color:#fff; }
.tk-detail { padding:10px 14px 14px; background:var(--wash); border-radius:8px; margin:2px 0 8px; }
.tk-detail p { margin:0 0 8px; font:var(--t-body); color:var(--ink); white-space:pre-wrap; }

/* ------------------------------------------------------------------- tiles
   The home grid. Each tile is a section, an icon, a one-line description and,
   where there is one, its own number. Deliberately roomy: this is the screen
   somebody lands on, and a tap target that is comfortable on a phone is the
   whole point of replacing the tab strip. */
.tile-grid { display:grid; gap:10px; margin:12px 0 22px;
             grid-template-columns:repeat(auto-fill, minmax(200px, 1fr)); }
.tile-grid-top { grid-template-columns:1fr; margin-bottom:8px; }
.home-sec { margin:6px 0 0; }

.tile { position:relative; display:grid; gap:3px; text-align:left; cursor:pointer;
        user-select:none; -webkit-user-select:none;
        padding:16px 16px 14px; border-radius:12px; border:1px solid var(--border);
        background:var(--surface-2); color:inherit; min-height:104px;
        transition:transform .09s ease, box-shadow .12s ease, border-color .12s; }
.tile:hover { border-color:var(--accent); transform:translateY(-1px);
              box-shadow:0 3px 12px rgba(0,0,0,.09); }
.tile:active { transform:none; }
.tile-ico { width:22px; height:22px; fill:none; stroke:var(--accent);
            stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round;
            margin-bottom:6px; }
.tile-name { font:var(--t-body); font-weight:600; color:var(--ink); }
.tile-note { font:var(--t-meta); color:var(--muted); line-height:1.35; }
.tile-badge { position:absolute; top:-9px; right:-9px; min-width:20px; padding:1px 6px;
              border-radius:999px; text-align:center; font:600 11px/16px var(--sans);
              background:var(--track); color:var(--ink); font-variant-numeric:tabular-nums;
              box-shadow:0 0 0 2px var(--surface-2); }
.tile-badge.on { background:var(--critical); color:#fff; }
.tile-badge.warn { background:var(--warning); color:#fff; }
.tile-badge.clear { background:var(--ok,#16a34a); color:#fff; }
.tile-badge.lavender { background:#f59e0b; color:#451a03; }
.tile-alert { border-left:4px solid var(--critical); }
.tile-wide { min-height:88px; }

/* ecotalk tile: WhatsApp-green background, white icon and label. */
[data-go="ecotalk"] { background:#128C7E; border-color:#0e7268; }
[data-go="ecotalk"]:hover { border-color:#0a5a52; }
[data-go="ecotalk"] .tile-ico { stroke:#fff; }
[data-go="ecotalk"] .tile-name { color:#fff; }
[data-go="ecotalk"] .tile-note { color:rgba(255,255,255,.75); }

/* Breadcrumb row, shown only inside a section. */
.toprow { display:flex; align-items:center; gap:12px; padding:10px 14px;
          border-bottom:1px solid var(--border); }
.crumb { background:none; border:0; cursor:pointer; font:var(--t-body);
         color:var(--accent); padding:6px 0; min-height:44px; }
.crumb-here { font:var(--t-body); font-weight:600; color:var(--ink); }

/* -------------------------------------------------- cross-site ticket button
   Above the plant picker, because tickets are not about a plant. Small by
   design: it is a standing indicator, not a headline. */
.ticketbar { display:flex; padding:10px 14px 0; }
.tkbtn { display:inline-flex; align-items:center; gap:8px; cursor:pointer;
         padding:7px 13px; min-height:38px; border-radius:999px;
         border:1px solid var(--border); background:var(--surface-2);
         font:var(--t-meta); color:var(--ink); }
.tkbtn:hover { border-color:var(--accent); }
.tkbtn-dot { width:8px; height:8px; border-radius:50%; background:var(--track); flex:none; }
.tkbtn.on { border-color:var(--critical); background:var(--critical); color:#fff; font-weight:600; }
.tkbtn.on .tkbtn-dot { background:#fff; }

/* Who is on site, and since when. Replaces three KPI cards. */
.onsite { font:var(--t-meta); color:var(--ink); margin:6px 0 0; }
.onsite strong { font-weight:600; }

/* -------------------------------------------------------- who is on site
   A name a client can tap to get a face and a number. The face matters: it is
   how somebody finds the right person in a plant room. */
/* button.who ONLY. The escalation card renders a plain <div class="who"> for
   a name that does nothing, and a bare `.who` rule gave it the accent colour,
   an underline and a pointer cursor: it looked tappable, and nothing happened.
   Anything that looks like a control has to be one. */
button.who { background:none; border:0; padding:0; cursor:pointer; font:var(--t-meta);
       font-weight:600; color:var(--accent); text-decoration:underline; }
button.who.open { text-decoration:none; }
.who-since { font:var(--t-meta); color:var(--muted); }
/* One card per person on site, all of them visible. auto-fit rather than a
   breakpoint, so it reflows as the browser is resized rather than only at
   whatever widths somebody thought of: one column on a phone, two or three
   across a desktop, without a media query deciding for it. */
/* auto-FILL, not auto-fit. auto-fit collapses the empty tracks and lets two
   cards stretch across a 1400px browser, which made a card holding a 92px
   photo and a phone number 570px wide. auto-fill keeps the tracks, so cards
   stay a sensible size and simply leave space to their right. */
.whogrid { display:grid; gap:10px; margin-top:10px;
           grid-template-columns:repeat(auto-fill, minmax(260px, 1fr));
           max-width:920px; }
.whocard { display:flex; gap:14px; align-items:flex-start; margin-top:0;
           padding:12px; border-radius:10px; background:var(--wash);
           border:1px solid var(--border); }
/* The single card the attendance table opens still needs its own spacing. */
#who-card .whocard { margin-top:10px; }
.whocard img { width:92px; height:92px; object-fit:cover; border-radius:8px; flex:none;
               background:var(--track); }
.whocard-noimg { width:92px; height:92px; border-radius:8px; flex:none;
                 background:var(--track); display:grid; place-items:center;
                 font:var(--t-meta); color:var(--muted); }
.whocard strong { font:var(--t-body); font-weight:600; color:var(--ink); }
.whocard .btn { margin-top:8px; display:inline-block; }

/* Invoice rows lead with the work, then the number. The number identifies it
   in Tally; the work is what somebody recognises. */
.inv-work { display:block; font:var(--t-body); color:var(--ink); }
.inv-no { display:block; font-size:11px; color:var(--muted); margin-top:2px; }

/* ------------------------------------------------------------- shift log rows
   Time, text, thumbnail on one line; everything else on tap. A shift files
   about five entries, so a day of large photo cards was several screens of
   scrolling to read what amounts to a few sentences. */
.lg-list { list-style:none; margin:0; padding:0; }
.lg-row { border-bottom:1px solid var(--border); }
.lg-open { display:flex; align-items:flex-start; gap:12px; width:100%;
           background:none; border:0; cursor:pointer; text-align:left;
           padding:11px 4px; color:inherit; }
.lg-open:hover { background:var(--surface-2); }
.lg-time { flex:none; width:72px; font-family:var(--mono); font-size:11px;
           color:var(--muted); padding-top:2px; }
.lg-body { flex:1; min-width:0; display:grid; gap:2px; }
.lg-kind { font:var(--t-meta); color:var(--muted); }

/* Who wrote it, what they are, and where they were standing.
   The app has always recorded at_site and never said it, so a manager's remark
   typed at a desk in Bangalore was drawn identically to an operator's
   observation made next to the blower. Tokens used here are all defined above:
   --muted, --ink-2, --warning-text, --verifiable-text. Do not reach for one
   that is not; invented variables fail silently and the text just goes black. */
.lg-who   { font:var(--t-meta); color:var(--muted); }
.lg-desig { color:var(--ink-2); font-weight:600; }
.lg-at    { color:var(--verifiable-text); }
.lg-off   { color:var(--warning-text); }

/* Section headings inside a day. Operator, mechanic and head office notes are
   different kinds of claim and are not merged into one list. */
.lg-group { display:flex; align-items:center; gap:8px; margin:18px 0 2px;
            font:var(--t-meta); text-transform:uppercase; letter-spacing:.06em;
            color:var(--muted); font-weight:600; }
.lg-group:first-of-type { margin-top:10px; }
.lg-count { font-family:var(--mono); font-size:11px; color:var(--muted);
            background:var(--surface-2); border-radius:9px; padding:1px 7px; }
/* Two lines, then an ellipsis. The rest is one tap away. */
.lg-text { font:var(--t-body); color:var(--ink); display:-webkit-box;
           -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.lg-thumb, .lg-nothumb { flex:none; width:44px; height:44px; border-radius:6px;
           object-fit:cover; background:var(--track); }
.lg-row.open .lg-text { -webkit-line-clamp:unset; }
.lg-detail { list-style:none; }
.lg-full { padding:4px 4px 16px; }
/* Shift-log day view, one section per person per shift (26 Sep 2026). */
.lg-shift { margin-top:14px; }
.eqnow-list { list-style:none; margin:6px 0 0; padding:0; display:grid; gap:6px; }
.eqnow-list li { display:flex; flex-wrap:wrap; align-items:flex-start; gap:4px 8px; }
.lg-span { font:var(--t-body); color:var(--ink-2); display:flex; flex-wrap:wrap; align-items:flex-start; gap:6px 8px; }
.lg-arrow { color:var(--muted); }
.lg-what { font:var(--t-body); color:var(--ink); overflow-wrap:anywhere; }
.lg-time .tday { font-family:var(--mono); font-size:10px; }
.lg-full img { width:100%; max-width:520px; border-radius:8px; display:block;
               margin-bottom:10px; background:var(--track); }


/* --------------------------------------------------------- contract cover
   AMC PO and consent to operate, side by side at the top of Overview. The
   EXPIRY is the headline because it is the part that needs someone to act. */
.cover-card { display:grid; gap:18px; margin-bottom:14px;
              grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); }
.cover-label { font:var(--t-meta); color:var(--muted); text-transform:uppercase;
               letter-spacing:.04em; }
.cover-value { font:var(--t-title); color:var(--ink); margin-top:4px; }
.cover-value.bad  { color:var(--critical-text); }
.cover-value.warn { color:var(--warning-text); }
.cover-value.ok   { color:var(--ink); }
.cover-value.unset { color:var(--muted); font-style:italic; font-size:1rem; }
/* "Open the signed PO". A link, not a button, because it opens a document in a
   new tab rather than changing anything. Sized above 16px equivalent so Safari
   on iOS does not zoom, and given a real hit area for a thumb. Every token here
   is defined in base-openrouter.css; inventing one has silently rendered a
   control invisible before now. */
.po-open {
  display:inline-block; margin-top:8px; padding:7px 12px;
  font:var(--t-meta); font-weight:600; line-height:1.2;
  color:var(--accent-fg); background:var(--accent);
  border:1px solid var(--border); border-radius:var(--radius);
  text-decoration:none; min-height:32px;
}
.po-open:hover  { filter:brightness(1.06); }
.po-open:focus-visible { outline:2px solid var(--focus-border); outline-offset:2px; }

.cover-note { font:var(--t-meta); color:var(--muted); margin-top:3px; }
.cover-note.bad  { color:var(--critical-text); font-weight:600; }
.cover-note.warn { color:var(--warning-text); font-weight:600; }

/* ------------------------------------------------------------ exec summary */
.hero { display:flex; flex-wrap:wrap; gap:16px; align-items:flex-start;
        justify-content:space-between; margin-bottom:16px; }
.hero-name { font:var(--t-display); color:var(--ink); margin:0; }
.hero-sub { font:var(--t-body); color:var(--muted); margin:4px 0 0; }

/* People as circles rather than another rectangle: a face reads as a person
   before any number on this page reads as anything. */
.people { display:flex; gap:14px; flex-wrap:wrap; }
.person { display:grid; justify-items:center; gap:4px; background:none; border:0;
          cursor:pointer; padding:0; color:inherit; }
.person img, .person-blank { width:54px; height:54px; border-radius:50%; object-fit:cover;
          background:var(--track); border:2px solid var(--verifiable); display:block; }
.person-name { font:var(--t-meta); color:var(--ink); }
.person-call { font:var(--t-meta); color:var(--accent); text-decoration:none; }

/* The three numbers that decide whether today is a good day. */
.big-row { display:grid; gap:10px; margin-bottom:14px;
           grid-template-columns:repeat(auto-fit, minmax(180px, 1fr)); }
.big-stat { display:grid; gap:2px; text-align:left; cursor:pointer; padding:16px;
            border-radius:12px; border:1px solid var(--border);
            background:var(--surface-2); color:inherit; }
.big-stat:hover { border-color:var(--accent); }
.big-label { font:var(--t-meta); color:var(--muted); text-transform:uppercase;
             letter-spacing:.04em; }
.big-value { font:var(--t-display); color:var(--ink); line-height:1.05;
             font-variant-numeric:tabular-nums; }
.big-sub { font:var(--t-meta); color:var(--muted); }
.big-stat.bad .big-value { color:var(--critical-text); }

/* Contract term as a bar: "568 days left" means nothing until you can see it
   is most of the way through a three-year contract, or barely into it. */
.po-bar { height:6px; border-radius:999px; background:var(--track); margin:8px 0 6px;
          overflow:hidden; max-width:280px; }
.po-bar-fill { display:block; height:100%; background:var(--verifiable); }
.po-bar-fill.warn { background:var(--warning); }
.po-bar-fill.bad  { background:var(--critical); }

.stat-row { display:grid; gap:14px; margin-top:12px;
            grid-template-columns:repeat(auto-fit, minmax(140px, 1fr)); }
.stat-label { font:var(--t-meta); color:var(--muted); }
.stat-value { font:var(--t-title); color:var(--ink); font-variant-numeric:tabular-nums; }
.stat-unit { font:var(--t-meta); color:var(--muted); margin-left:4px; }

.maint-list { list-style:none; margin:10px 0 0; padding:0; display:grid; gap:6px; }
.maint-list li { display:flex; justify-content:space-between; gap:12px;
                 padding:7px 0; border-bottom:1px solid var(--border); }
.maint-name { font:var(--t-body); color:var(--ink); }
.maint-days { font:var(--t-meta); font-weight:600; white-space:nowrap; }
.maint-days.bad  { color:var(--critical-text); }
.maint-days.warn { color:var(--warning-text); }

/* ------------------------------------------------------ real time readings
   A dense grid, because the question is "what is everything doing" and that is
   answered by seeing it at once rather than by scrolling a table. A channel
   that has stopped talking is dimmed and labelled, never shown as a number. */
.rt-head { display:flex; flex-wrap:wrap; align-items:center; gap:10px 14px; margin-bottom:12px; }
.rt-asof { font:var(--t-meta); color:var(--muted); }
.rt-grid { display:grid; gap:10px; margin-top:10px;
           grid-template-columns:repeat(auto-fill, minmax(150px, 1fr)); }
.rt-cell { padding:10px 12px; border:1px solid var(--border); border-radius:10px;
           background:var(--surface-2); }
.rt-cell.stale { opacity:.55; }
.rt-label { font:var(--t-meta); color:var(--muted); line-height:1.25; }
.rt-value { font:var(--t-title); color:var(--ink); margin-top:3px;
            font-variant-numeric:tabular-nums; }
.rt-value.bad { color:var(--critical-text); }
.rt-unit { font:var(--t-meta); color:var(--muted); }
.rt-note { font:var(--t-meta); color:var(--warning-text); margin-top:2px; }
.rt-more { margin-top:10px; }
.rt-more > summary { font:var(--t-meta); color:var(--muted); cursor:pointer;
                     padding:6px 0; list-style:none; }
.rt-more > summary::-webkit-details-marker { display:none; }
.rt-more > summary::before { content:'▸ '; }
.rt-more[open] > summary::before { content:'▾ '; }
.rt-more > summary:hover { color:var(--ink); }
/* Value and unit are one reading, not two; let the parameter name wrap instead. */
table.chan-table td.num { white-space:nowrap; }
.people-block { display:grid; gap:6px; justify-items:start; }
.people-title { font:var(--t-meta); color:var(--muted); text-transform:uppercase;
                letter-spacing:.04em; }

/* An <a class="btn"> inherits display:inline, so its vertical padding spills
   out of the line box and paints over whatever sits above it. The Download PO
   link was covering the PO start date. Anchors styled as buttons are blocks. */
a.btn { display:inline-block; text-decoration:none; }
.cover-item .btn, .maint .btn { margin-top:10px; }

/* ------------------------------------------------------------------- map */
.map-frame { position:relative; width:100%; aspect-ratio:2/1; overflow:hidden;
             border-radius:10px; border:1px solid var(--border);
             background:var(--track); margin-top:10px; }
.map-tile { position:absolute; display:block; border:0; }
.map-pin { position:absolute; transform:translate(-50%, -100%); display:grid;
           justify-items:center; text-decoration:none; z-index:2; }
.map-dot { width:14px; height:14px; border-radius:50% 50% 50% 0; rotate:-45deg;
           background:var(--accent); border:2px solid #fff;
           box-shadow:0 1px 3px rgba(0,0,0,.4); }
.map-pin.office .map-dot { background:var(--critical); }
.map-tag { font:var(--t-meta); font-weight:600; color:var(--ink);
           background:var(--surface-1); border:1px solid var(--border);
           border-radius:6px; padding:1px 6px; margin-top:3px; white-space:nowrap; }
.map-credit { position:absolute; right:4px; bottom:3px; z-index:2;
              font:var(--t-meta); color:var(--ink); background:var(--surface-1);
              opacity:.8; padding:0 4px; border-radius:4px; }
.chart .spread { fill: var(--accent); opacity: .16; stroke: none; }
.range-pick { margin-top: 10px; }

.billing-name { margin-top:8px; }
.late-report { border-left:4px solid var(--critical); margin-bottom:14px; }
.late-line { font:var(--t-title); color:var(--critical-text); margin:8px 0 0; }
.placeholder-note { border-left:4px solid var(--warning); margin-bottom:14px; }
.placeholder-note p { font:var(--t-body); color:var(--ink-2); margin:8px 0 0; }
/* The band needs explaining where the band is, not only in a header the
   reader has already scrolled past. */
.band-key { display:flex; align-items:center; gap:7px; margin:8px 0 0;
            font:var(--t-meta); color:var(--muted); }
.band-swatch { width:22px; height:11px; border-radius:3px; flex:none;
               background:var(--accent); opacity:.16;
               box-shadow:inset 0 5px 0 -4.5px var(--accent); }
/* A push tile with nothing in it is a good outcome, so it stays quiet; one
   with items earns the accent. */
.push-kpi.has .value { color:var(--critical-text); }

/* ------------------------------------------------------------- cc picker */
.cc-grid { display:grid; gap:8px; margin-top:12px;
           grid-template-columns:repeat(auto-fill, minmax(230px, 1fr)); }
.cc-item { display:flex; align-items:flex-start; gap:9px; padding:9px 11px;
           border:1px solid var(--border); border-radius:10px;
           background:var(--surface-2); cursor:pointer; }
.cc-item.fixed { opacity:.75; cursor:default; }
.cc-item input { margin-top:2px; flex:none; }
.cc-item span { display:grid; gap:1px; font:var(--t-body); color:var(--ink); }
.cc-item em { font:var(--t-meta); font-style:normal; color:var(--muted); }
.cc-item .pill { justify-self:start; margin-top:3px; }

/* Save sits beside the row rather than only inside the viewer, so a report can
   be taken without rendering it first. */
.rep-save { font:var(--t-meta); color:var(--accent); text-decoration:none;
            border:1px solid var(--border); border-radius:6px; padding:2px 8px;
            margin-left:10px; white-space:nowrap; }
.rep-save:hover { border-color:var(--accent); }

/* ------------------------------------------------------------- form field
   `.fl` was used by the ticket form and the push draft and was never defined
   in any stylesheet, so the label and its control laid out inline: "Subject
   [tiny box] Body [tiny box]" on one line. A stacked block, full width, with
   a control big enough to actually type in.

   16px on the control is not a taste decision. Safari on iOS zooms the whole
   page when a focused input is below 16px, and the zoom does not come back. */
.fl { display: block; margin-top: 12px; }
.fl > span { display: block; font: var(--t-meta); color: var(--muted); margin-bottom: 5px; }
.fl input, .fl textarea, .fl select {
  display: block; width: 100%; box-sizing: border-box;
  font: 400 16px/1.55 var(--sans); color: var(--ink);
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 10px 12px;
}
.fl textarea { resize: vertical; min-height: 220px; font-family: var(--sans); }
.fl input:focus, .fl textarea:focus, .fl select:focus {
  outline: none; border-color: var(--focus-border); box-shadow: var(--focus-shadow);
}

/* ------------------------------------------------------- engineer push
   One card instead of four. The screen is four numbers and two buttons; it
   used to run past a screen and a half on a laptop, for a page where three of
   the four numbers are normally zero.

   The number sits BESIDE its label rather than above it, which is what takes
   the height out: a stacked KPI tile is three lines tall before any padding. */
.push-card .push-head { margin-bottom: 14px; }
.push-stats { display: grid; gap: 1px; background: var(--grid);
              border: 1px solid var(--border); border-radius: var(--radius);
              grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
              overflow: hidden; margin-bottom: 14px; }
.pstat { display: flex; align-items: baseline; gap: 10px;
         background: var(--surface-1); padding: 11px 14px; }
.pstat b { font: var(--t-title); font-variant-numeric: tabular-nums;
           color: var(--critical-text); line-height: 1; }
.pstat span { display: grid; gap: 1px; font: var(--t-body); color: var(--ink); }
.pstat em { font: var(--t-meta); font-style: normal; color: var(--muted); }
/* A zero is not news. Quiet it so the eye lands on the one that is not. */
.pstat.zero b { color: var(--muted); font-weight: 500; }
.pstat.zero span { color: var(--muted); }
.push-card .dl-row { align-items: center; flex-wrap: wrap; gap: 10px; }
.push-card .dl-row .note-inline { flex: 1 1 220px; }

/* ------------------------------------------------- month to date figures
   The markup for this card existed and its stylesheet never did, so it
   rendered as a flat run of text: label, number, note, label, number, note,
   with nothing separating them. Size and weight carry the hierarchy, so the
   number reads first and its qualifier last. */
.mu-row { display:grid; gap:1px; background:var(--grid); margin-top:14px;
          border:1px solid var(--border); border-radius:var(--radius);
          overflow:hidden;
          grid-template-columns:repeat(auto-fit, minmax(160px, 1fr)); }
.mu-cell { background:var(--surface-1); padding:16px 18px; display:grid;
           align-content:start; gap:4px; }
.mu-label { font:var(--t-meta); color:var(--muted); text-transform:uppercase;
            letter-spacing:.05em; }
.mu-value { font:var(--t-display); color:var(--ink); line-height:1.05;
            font-variant-numeric:tabular-nums; }
.mu-unit { font:var(--t-meta); color:var(--muted); margin-left:5px;
           letter-spacing:0; text-transform:none; }
.mu-sub { font:var(--t-meta); color:var(--ink-2); line-height:1.45; }
.mu-value .unset { font:var(--t-title); color:var(--muted); font-style:italic; }

/* The one figure on the card that is a fraction of another, drawn as one. */
.mu-dial { justify-items:start; }
.dial-wrap { position:relative; width:84px; height:84px; margin:2px 0; }
.dial { width:84px; height:84px; transform:rotate(-90deg); }
.dial-track { fill:none; stroke:var(--track); stroke-width:8; }
.dial-arc { fill:none; stroke:var(--verifiable); stroke-width:8;
            stroke-linecap:round; transition:stroke-dasharray .5s ease; }
.dial-num { position:absolute; inset:0; display:grid; place-content:center;
            font:var(--t-title); color:var(--ink); font-variant-numeric:tabular-nums; }
.dial-num em { font:var(--t-meta); font-style:normal; color:var(--muted); }

/* Home is a destination, the group is a step back, and the current view is
   neither. Only the first two are controls and only they look like it. */
.crumb-home { font-weight:600; }
.toprow .crumb + .crumb::before,
.toprow .crumb + .crumb-here::before { content:'›'; color:var(--muted);
  margin:0 8px 0 4px; font-weight:400; }
.mu-cur { font:var(--t-title); color:var(--muted); margin-right:2px; }

/* The Google image sits on top; the OSM mosaic underneath is only revealed if
   the image fails, which is what happens when no key is configured. */
.map-img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
           display:block; z-index:1; }
.map-osm { position:absolute; inset:0; }
.map-frame:not(.map-fallback) .map-osm,
.map-frame:not(.map-fallback) .map-credit { display:none; }
.map-key { display:flex; align-items:center; gap:7px; flex-wrap:wrap;
           font:var(--t-meta); color:var(--muted); margin-top:10px; }
.mk { width:10px; height:10px; border-radius:50%; flex:none; }
.mk-site { background:var(--brand-teal, #2E7D77); }
.mk-office { background:#D64550; }
.mk + .mk { margin-left:12px; }
/* The wordmark is a control now, so it has to behave like one and look like
   nothing has changed. */
.brand-home { background:none; border:0; padding:0; margin:0; cursor:pointer;
              display:inline-flex; align-items:center; }
.brand-home:focus-visible { outline:2px solid var(--focus-border); outline-offset:3px;
                            border-radius:4px; }


/* ==================================================== real time, redesigned
   Three tiers by how often the question is asked. Green carries compliance;
   lavender carries throughput, so the eye separates "is it legal" from "is it
   performing" without reading a word. */
.rt-verdict { display:flex; align-items:center; gap:8px; margin:0 0 16px;
              font:500 13px/1.35 var(--sans); color:var(--verifiable-text); }
.rt-verdict i { width:8px; height:8px; border-radius:50%; flex:none;
                background:currentColor; }
.rt-verdict.warn { color:var(--warning-text); }
.rt-verdict.bad  { color:var(--critical-text); }
.rt-verdict.na   { color:var(--muted); }

.rt-card { padding:18px 16px; margin-bottom:12px; }
.rt-lab { font:500 11px/1 var(--sans); color:var(--muted); letter-spacing:.09em;
          text-transform:uppercase; margin:0 0 16px; display:flex;
          align-items:center; gap:8px; }
.rt-lab .n { margin-left:auto; letter-spacing:0; text-transform:none; font-weight:450; }

/* Gauges. Value and unit stacked dead centre inside the arc, never beside it. */
.g-row { display:grid; gap:2px; grid-template-columns:repeat(4, 1fr); }
.gg { margin:0; position:relative; display:grid; justify-items:center; text-align:center; }
.gg svg { width:84px; height:84px; display:block; }
.gtr { fill:none; stroke:var(--track); stroke-width:6; stroke-linecap:round; }
.ga  { fill:none; stroke:var(--verifiable); stroke-width:6; stroke-linecap:round; }
.ga-faint { opacity:.16; }
.gg.warn .ga { stroke:var(--warning); }
.gg.bad  .ga { stroke:var(--critical); }
.gg.na   .ga { stroke:var(--muted); }
.gx { fill:var(--verifiable-text); stroke:var(--surface-1); stroke-width:2.5; }
.gg.warn .gx { fill:var(--warning-text); }
.gg.bad  .gx { fill:var(--critical-text); }
/* Throughput reads lavender, compliance reads green. */
.gg.lav .ga { stroke:#a78bfa; }
.gg.lav.warn .ga { stroke:var(--warning); }
.gg.lav.bad  .ga { stroke:var(--critical); }
.gg-v { position:absolute; top:27px; left:0; right:0; font:700 19px/1 var(--sans);
        font-variant-numeric:tabular-nums; letter-spacing:-.02em; color:var(--ink); }
.gg-u { position:absolute; top:48px; left:0; right:0; font:450 9px/1 var(--sans);
        color:var(--muted); }
.gg figcaption { font:600 12px/1.25 var(--sans); margin-top:-4px; display:grid; gap:1px;
                 color:var(--ink); }
.gg figcaption small { font:450 10px/1 var(--sans); color:var(--muted); }
.gg.sm svg { width:70px; height:70px; }
.gg.sm .gg-v { top:22px; font-size:16px; }
.gg.sm .gg-u { top:39px; }

/* The three numbers that say how the plant is performing right now. */
.flow-strip { display:flex; margin-bottom:18px; border-radius:12px; overflow:hidden;
              background:var(--surface-2); border:1px solid var(--border); }
.flow-strip div { flex:1; padding:12px 14px; display:grid; gap:3px; min-width:0; }
.flow-strip div + div { border-left:1px solid var(--border); }
.flow-strip span { font:450 10px/1 var(--sans); color:var(--muted);
                   letter-spacing:.06em; text-transform:uppercase; }
.flow-strip b { font:700 22px/1 var(--sans); font-variant-numeric:tabular-nums;
                color:#a78bfa; }
.flow-strip b em { font:450 10px/1 var(--sans); color:var(--muted); font-style:normal;
                   margin-left:3px; }

.run-h { display:flex; align-items:baseline; gap:8px; margin-bottom:12px; }
.run-h b { font:700 20px/1 var(--sans); color:var(--ink); }
.run-h span { font:450 12px/1 var(--sans); color:var(--muted); }
.chips { display:flex; flex-wrap:wrap; gap:5px; }
.chip { display:inline-flex; align-items:center; gap:6px; font:450 11px/1 var(--sans);
        background:var(--surface-2); border:1px solid var(--border);
        border-radius:99px; padding:6px 10px; color:var(--ink-2); }
.chip i { width:6px; height:6px; border-radius:50%; background:var(--muted); flex:none; }
.chip.run i { background:var(--verifiable-text); }
.chip.trip { color:var(--critical-text); border-color:var(--critical); }
.chip.trip i { background:var(--critical); }

/* Everything a person asks for rarely, one tap away with its count on the row.
   Collapsed, not hidden. */
.rt-fold { background:var(--surface-1); border:1px solid var(--border);
           border-radius:14px; margin-bottom:10px; overflow:hidden; }
.rt-fold > summary { list-style:none; cursor:pointer; padding:15px 16px;
        font:500 13px/1 var(--sans); display:flex; align-items:center; gap:9px;
        color:var(--ink-2); }
.rt-fold > summary::-webkit-details-marker { display:none; }
.rt-fold > summary::before { content:''; }
.rt-fold > summary::after { content:''; margin-left:auto; width:7px; height:7px;
        border-right:1.5px solid var(--muted); border-bottom:1.5px solid var(--muted);
        transform:rotate(-45deg); transition:transform .15s; }
.rt-fold[open] > summary::after { transform:rotate(45deg); }
.rt-fold > summary .n { color:var(--muted); font-weight:450; margin-left:0; }
.rt-fold .dbody { padding:0 16px 14px; }
.rt-fold .rw { display:flex; align-items:baseline; gap:10px; padding:8px 0;
               border-bottom:1px solid var(--grid); }
.rt-fold .rw:last-child { border-bottom:0; }
.rt-fold .rw span { font:450 12px/1.3 var(--sans); color:var(--muted); }
.rt-fold .rw b { margin-left:auto; font:600 14px/1 var(--sans);
                 font-variant-numeric:tabular-nums; color:var(--ink); }
.rt-fold .rw b em { font:450 10px/1 var(--sans); color:var(--muted);
                    font-style:normal; margin-left:3px; }
.rt-fold .rw.na b { color:var(--muted); font-weight:450; }

/* Desktop has the room for more across, so it uses it rather than stretching
   four gauges to the width of a laptop. */
@media (min-width: 780px) {
  /* auto-fit, not a fixed six: with four gauges a six-column grid left two
     empty tracks and the row hugged the left edge with dead space beside it.
     Empty tracks collapse, so however many gauges a site has, they spread. */
  .g-row { grid-template-columns:repeat(auto-fit, minmax(96px, 1fr)); }
  .rt-fold .dbody { columns:2; column-gap:28px; }
  .rt-fold .rw { break-inside:avoid; }
}

/* Hold to reveal the password on the head office sign in. */
.pwwrap { position: relative; display: block; }
.pwwrap input { width: 100%; padding-right: 46px; }
.peek { position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
        width: 38px; height: 38px; display: flex; align-items: center;
        justify-content: center; background: none; border: 0; cursor: pointer;
        color: var(--muted); border-radius: 8px; }
.peek.on { color: var(--accent); background: var(--accent-subtle); }

/* A destructive button. Signing an account out is not the same kind of act as
   clearing a phone, and the two sit next to each other in the same row, so it
   has to look different before it is pressed rather than explain itself
   afterwards in a dialog. */
.btn.danger { border-color: var(--critical); color: var(--critical-text); }
.btn.danger:hover:not(:disabled) { background: var(--critical-wash, #fdecea); border-color: var(--critical-text); }

/* The second home heading. Administration is not a plant, so it needs air
   above it rather than reading as one more row of the grid before it. */
.home-sec-2 { margin-top: 34px; }
.home-subnote { margin: -6px 0 14px; font: var(--t-meta); color: var(--muted); }
/* On a phone the two grids stack, so the only thing separating them is the
   heading. Give it a rule as well as space, or Administration reads as one
   more row of the plant's own tiles. */
@media (max-width: 760px) {
  .home-sec-2 { margin-top: 26px; padding-top: 20px; border-top: 1px solid var(--border); }
  .home-subnote { margin: -2px 0 12px; }
}

/* Sortable column headings. The arrow says which column and which way; the
   cursor says the heading is a control before anybody clicks it. */
th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
th.sortable:hover { color: var(--ink); text-decoration: underline; }


/* The plant box is an input now, not a select, so it needs the select's look.
   77 plants in a dropdown was a scroll; this keeps the browser's own
   type-ahead and stays one tap for somebody who only holds two or three. */
.plantbar input#plant-pick { font: inherit; color: inherit; background: var(--surface-1);
  border: 1px solid var(--border); border-radius: 8px; padding: 6px 10px;
  min-width: 220px; }
.plantbar input#plant-pick:focus { outline: 2px solid var(--focus-border); outline-offset: 1px; }

/* The plant search: an empty box that drops its results underneath.
   Clicking it with nothing typed lists every plant, which is the dropdown;
   typing narrows it, matching aliases as well as names.

   NOTE the token names. This first shipped using var(--surface-1), which does
   not exist in this stylesheet: the real ones are --surface-1 and --surface-2.
   An undefined custom property resolves to nothing, so the list rendered with
   NO background at all and the tiles showed straight through it. Check a token
   exists before using it; this is the third time that has bitten here. */
.plantbox { position: relative; }
.plantbar input#plant-pick { font: inherit; color: var(--ink); background: var(--surface-1);
  border: 1px solid var(--border); border-radius: 8px; padding: 6px 30px 6px 10px;
  min-width: 240px; cursor: pointer;
  /* The chevron is what makes this read as a dropdown rather than a text box.
     It is the state-selector pattern: looks like a select, opens its list on
     click, and accepts typing to narrow it. Without the caret nobody knows
     there is a list behind it. */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23888' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat; background-position: right 10px center; }
.plantbar input#plant-pick::-webkit-search-cancel-button { display: none; }
.plantbar input#plant-pick:focus { outline: 2px solid var(--focus-border); outline-offset: 1px; }
.plant-hits { position: absolute; z-index: 200; top: calc(100% + 4px); left: 0;
  min-width: 300px; max-height: 360px; overflow-y: auto; margin: 0; padding: 4px;
  list-style: none;
  /* Opaque, explicitly. A dropdown you can read the page through is unusable. */
  background: var(--surface-1); color: var(--ink);
  border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, .22); }
.plant-hits li { padding: 9px 11px; border-radius: 7px; cursor: pointer; font-size: 14px;
  background: transparent; }
.plant-hits li:hover { background: var(--wash); }
/* Why a site matched, when it matched on something other than its name. */
.plant-hits li em { display: block; font-style: normal; font-size: 12px; color: var(--muted); }
.plant-hits li.none { color: var(--muted); cursor: default; }
.plant-hits li.none:hover { background: transparent; }

/* A PO matched from the folder but not yet confirmed by an engineer. Shown to
   head office, because they are the ones who confirm it; withheld from the
   client by po.js, which is where that rule belongs. */
.cover-note.unconfirmed { color: var(--warning-text); margin-top: 6px; }


/* ===================== iOS focus zoom, the last word =====================
 * Safari on iOS zooms the page whenever a focused text control computes below
 * 16px, and it never zooms back out: the reader has to pinch out by hand after
 * every use. Janardhan hit this on the site picker on 13 Sep 2026.
 *
 * WHY THIS BLOCK IS AT THE BOTTOM. The rule already existed higher up as
 * `@media (max-width:640px) { input, select, textarea, button { font-size:16px } }`
 * and had been silently losing. A MEDIA QUERY ADDS NO SPECIFICITY, so
 * `.plantbar input#plant-pick { font: inherit }` (id + class + element) beat the
 * bare `input` selector, and even after the selector was matched exactly it
 * still lost, because the other rule sits later in the file and equal
 * specificity is decided by source order. Being last is the whole mechanism.
 *
 * Note `font: inherit` is the trap: the shorthand sets font-size too, so it
 * silently reintroduces the inherited 14px of --t-body.
 *
 * Buttons are left alone deliberately: iOS zooms on FOCUS of a text control,
 * and a button never takes that kind of focus, so shrinking one is safe.
 *
 * `maximum-scale=1` in the viewport would also stop it, and the operator app
 * uses that. It is not used here because it disables pinch-zoom for everyone,
 * including anyone who needs it to read the screen. Fixing the font size keeps
 * the page still AND leaves zoom available. */
@media (max-width: 640px) {
  .plantbar input#plant-pick,
  .subbar select,
  .gate-card input,
  input[type="search"], input[type="text"], input[type="password"],
  input[type="number"], input[type="tel"], input[type="email"],
  select, textarea { font-size: 16px; }
}

/* ---- Actions: what is waiting on me to approve ---------------------------
   One row per record. The row carries who filed it and when, because an
   approver's first question is whose work this is, and the second is how long
   it has been sitting there. */
.apv-list { display: grid; gap: 10px; margin-top: 14px; }
.apv {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 12px 14px; border: 1px solid var(--rule); border-radius: 10px;
  background: var(--surface-1);
}
.apv-main { flex: 1 1 260px; display: grid; gap: 2px; min-width: 0; }
.apv-site { font-size: 13px; color: var(--muted); }
.apv-by   { font-size: 12px; color: var(--muted); }
.apv-no   { font-size: 12px; color: var(--muted); font-style: italic; }
.apv-err  { flex-basis: 100%; font-size: 12px; color: var(--critical-text); }
/* Approved rows stay on screen rather than vanishing. Something disappearing
   the instant you press a button reads as "did that work?", and the whole
   point of this panel is to answer that question. */
.apv.done { background: var(--verifiable-wash); border-color: transparent; }

/* Filter bar on a management queue: who filed it, which plant, what order. */
.tasks-filter {
  display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-end;
  margin: 16px 0 4px; padding-bottom: 14px;
  border-bottom: 1px solid var(--rule);
}
.tasks-filter label {
  display: grid; gap: 4px; font-size: 12px; color: var(--muted);
}
.tasks-filter select { min-width: 150px; }
.tasks-count { margin-left: auto; font-size: 13px; color: var(--muted); }

/* ---- One filed visit, read as a form ------------------------------------ */
.vv-state { padding: 10px 12px; border-radius: 8px; font-size: 13px; margin: 14px 0; }
.vv-state.ok   { background: var(--verifiable-wash); color: var(--verifiable-text); }
.vv-state.warn { background: color-mix(in srgb, var(--asserted) 14%, var(--surface-1)); color: var(--warning-text); }
.vv-sum { display: flex; flex-wrap: wrap; gap: 18px; margin: 14px 0 4px;
          font-size: 13px; color: var(--muted); }
.vv-sum .bad { color: var(--critical-text); }
.vv-note p { margin: 4px 0 14px; font-size: 14px; }
.vv-unit { margin-top: 22px; }
.vv-items { list-style: none; margin: 8px 0 0; padding: 0; }
.vv-items li {
  display: grid; grid-template-columns: 1fr auto; gap: 4px 14px;
  padding: 8px 0; border-bottom: 1px solid var(--rule); font-size: 14px;
}
.vv-ans { font-size: 12px; font-weight: 600; text-align: right; white-space: nowrap; }
.vv-ans.ok  { color: var(--verifiable-text); }
.vv-ans.bad { color: var(--critical-text); }
.vv-inote { grid-column: 1 / -1; font-size: 12px; color: var(--muted); }
.vv-photo { grid-column: 1 / -1; max-width: 260px; border-radius: 8px; margin-top: 6px; }
.vv-photos { display: flex; gap: 8px; flex-wrap: wrap; padding: 10px 0 4px; }
.vv-thumb { width: 72px; height: 72px; object-fit: cover; border-radius: 6px;
            border: 1px solid #ddd; cursor: zoom-in; }
.vv-lightbox { position: fixed; inset: 0; background: rgba(0,0,0,.88);
               display: flex; align-items: center; justify-content: center;
               z-index: 9999; cursor: zoom-out; }
.vv-lightbox[hidden] { display: none; }
.vv-lightbox img { max-width: 92vw; max-height: 88vh; border-radius: 6px; display: block; }
.vv-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 14px;
          margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--rule); }
.rep-by   { font-size: 12px; color: var(--muted); margin-left: 10px; }
.rep-pend { font-size: 11px; font-weight: 600; color: var(--warning-text);
            background: color-mix(in srgb, var(--asserted) 14%, var(--surface-1)); padding: 2px 8px; border-radius: 99px; margin-left: 8px; }
.rep-appr { font-size: 11px; font-weight: 600; color: var(--verifiable-text);
            background: var(--verifiable-wash); padding: 2px 8px;
            border-radius: 99px; margin-left: 8px; }
.vv-trail { margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--rule); }
.vv-trail ul { list-style: none; margin: 8px 0; padding: 0; }
.vv-trail li { font-size: 13px; padding: 5px 0; color: var(--muted); }
.vv-trail li b { color: var(--ink); }

/* ---- The estate: every plant, and where authorisation has run out -------- */
.es-wrap { overflow-x: auto; margin-top: 8px; }
table.es { width: 100%; border-collapse: collapse; font-size: 13px; }
table.es th {
  text-align: left; font-weight: 600; font-size: 11px; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted);
  padding: 8px 12px 8px 0; border-bottom: 1px solid var(--rule); white-space: nowrap;
}
table.es td { padding: 9px 12px 9px 0; border-bottom: 1px solid var(--rule); vertical-align: top; }
table.es td.bad  { color: var(--critical-text); font-weight: 600; }
table.es td.warn { color: var(--warning-text); }
.es-code { display: inline-block; font-size: 11px; color: var(--muted); margin-left: 6px; }
.es-none { color: var(--muted); }

.po-kpis { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0 2px; }
/* A STRIP TO GLANCE AT, not four cards to scroll past. These started as big
   KPI cards and pushed the table, which is the actual screen, below the fold on
   a laptop and two screens down on a phone. The number is the point; it does
   not need forty points of leading around it. */
.po-kpi {
  flex: 0 1 auto; min-width: 108px; padding: 7px 12px; border-radius: 8px;
  background: var(--surface-2);
  display: flex; align-items: baseline; gap: 7px;
  border: 1px solid transparent; text-align: left; font: inherit; color: inherit;
}
.po-kpi b { font-size: 17px; line-height: 1.2; }
.po-kpi span { font-size: 11px; color: var(--muted); }
/* Clickable ones (the PO counters) say so on hover; the rest are just readouts. */
button.po-kpi { cursor: pointer; }
button.po-kpi:hover { border-color: var(--rule); }
.po-kpi.bad  { background: color-mix(in srgb, var(--critical) 12%, var(--surface-1)); } .po-kpi.bad b  { color: var(--critical-text); }
.po-kpi.warn { background: color-mix(in srgb, var(--asserted) 14%, var(--surface-1)); } .po-kpi.warn b { color: var(--warning-text); }
.po-list { list-style: none; margin: 6px 0 18px; padding: 0; }
.po-row {
  display: grid;
  grid-template-columns: minmax(0,2fr) minmax(0,1.3fr) minmax(0,1.3fr) auto;
  gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--rule); font-size: 13px;
  align-items: baseline;
}
.po-do { display: flex; gap: 14px; justify-content: flex-end; white-space: nowrap; }
@media (max-width: 720px) {
  .po-row { grid-template-columns: 1fr; gap: 3px; padding: 12px 0; }
  .po-do { justify-content: flex-start; margin-top: 4px; }
}
.po-row.bad  .po-when { color: var(--critical-text); font-weight: 600; }
.po-row.warn .po-when { color: var(--warning-text); }
.po-who { color: var(--muted); }
table.es th.num, table.es td.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
table.es tfoot th { padding-top: 12px; border-top: 2px solid var(--rule); font-size: 13px; text-transform: none; letter-spacing: 0; color: var(--ink); }

/* ---- One person's month: a day per row, same on phone and desktop -------- */
.pm-days { list-style: none; margin: 14px 0 0; padding: 0; }
.pm-day {
  display: grid; grid-template-columns: 110px minmax(0,1fr) minmax(0,1fr);
  gap: 8px 20px; padding: 12px 0;
  border-bottom: 1px solid var(--rule); font-size: 13px;
}
.pm-date { font-weight: 600; white-space: nowrap; }
.pm-cell { white-space: pre-line; }          /* the entries carry real newlines */
.pm-lbl {
  display: none; font-size: 10px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted);
}
.pm-day.gap .pm-date { color: var(--warning-text); }
.pm-miss { color: var(--warning-text); font-weight: 600; }
.pm-day.future { opacity: .55; }
@media (max-width: 720px) {
  /* Stacked, with the labels turned on: without them "Planned" and "Actual"
     become two anonymous paragraphs under a date. */
  .pm-day { grid-template-columns: 1fr; gap: 6px; }
  .pm-lbl { display: block; margin-bottom: 2px; }
}

/* ---- Go-to-site combobox on the estate screens -------------------------- */
.es-find { position: relative; }
.es-combo { position: relative; display: block; }
.es-combo input {
  width: 100%; min-width: 190px; padding: 7px 30px 7px 11px;
  border: 1px solid var(--rule); border-radius: 8px;
  background: var(--surface-1); color: var(--ink); font: inherit;
  /* 16px, or iOS Safari zooms into the field on focus and never zooms back.
     The same rule the field app needed; a media query adds no specificity, so
     this has to be right here rather than fixed later in the file. */
  font-size: 16px;
}
.es-combo input::-webkit-search-cancel-button { display: none; }
.es-combo input:focus { outline: 2px solid var(--focus-border); outline-offset: 1px; }
.es-caret {
  position: absolute; right: 9px; top: 50%; transform: translateY(-50%);
  color: var(--muted); pointer-events: none;
}
/* The dropdown reuses .plant-hits so the estate list and the plant picker look
   like one control in two places, rather than two controls. */
.es-combo .plant-hits { min-width: 100%; }
/* A subset chip, set apart so it does not read as a fourth state that adds up
   with the others. */
.po-kpi.subset { background: transparent; border-color: var(--rule); }
.po-kpi.ok b { color: var(--verifiable-text); }

/* ---- Staff scorecard ---------------------------------------------------- */
/* Colour is never the only signal: every cell carries its number, and the
   header explains the bands. The tint is the glance, the number is the fact. */
table.es td.b-green { color: var(--verifiable-text); }
table.es td.b-amber { color: var(--warning-text); font-weight: 600; }
table.es td.b-red   { color: var(--critical-text); font-weight: 700; }
.dot-score {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 30px; padding: 2px 7px; border-radius: 99px;
  font-weight: 700; font-variant-numeric: tabular-nums;
}
.dot-score.b-green { background: var(--verifiable-wash); color: var(--verifiable-text); }
.dot-score.b-amber { background: color-mix(in srgb, var(--asserted) 14%, var(--surface-1));
                     color: var(--warning-text); }
.dot-score.b-red   { background: color-mix(in srgb, var(--critical) 12%, var(--surface-1));
                     color: var(--critical-text); }
/* FIVE COLUMNS THAT FIT A PHONE. Measured at 369px against a 300px content
   box before this, which is exactly the sideways scroll the redesign was meant
   to remove. The name wraps, the four numbers do not. */
table.es.at { table-layout: fixed; width: 100%; }
table.es.at th, table.es.at td { padding-right: 4px; }
table.es.at th:first-child, table.es.at td:first-child { width: auto; }
table.es.at th.num, table.es.at td.num { width: 44px; }
table.es.at td:first-child { white-space: normal; word-break: break-word; }
@media (max-width: 460px) {
  table.es.at { font-size: 12px; }
  table.es.at th { font-size: 9px; letter-spacing: 0; }
  table.es.at th.num, table.es.at td.num { width: 38px; }
  table.es.at .es-code { display: block; margin: 0; font-size: 10px; }
  .dot-score { min-width: 24px; padding: 1px 5px; }
}

/* ---- One person's month, as a calendar ---------------------------------- */
.cal-head, .cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.cal-head { margin-top: 16px; }
.cal-head span { font-size: 10px; text-transform: uppercase; letter-spacing: .05em;
                 color: var(--muted); text-align: center; padding-bottom: 4px; }
.cal { margin-bottom: 12px; }
.cal-pad { aspect-ratio: 1; }
.cal-d {
  aspect-ratio: 1; border: 1px solid transparent; border-radius: 7px;
  font: inherit; font-size: 12px; cursor: pointer; color: var(--ink);
  display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start;
  padding: 5px 6px; gap: 3px;
  background: var(--surface-2); overflow: hidden;
}
.cal-num { font-size: 12px; font-weight: 600; line-height: 1; flex-shrink: 0; }
.cal-snip { font-size: 9px; line-height: 1.3; color: inherit; opacity: .7;
            overflow: hidden; display: none; -webkit-box-orient: vertical; text-align: left; }
@media (min-width: 721px) {
  .cal-snip { display: -webkit-box; -webkit-line-clamp: 3; }
}
/* On wide screens cap all cells (including the pad spacer) to a fixed height so
   every grid row is uniform. The pad's aspect-ratio:1 was making the first row
   ~114px tall on a wide screen; explicit height beats aspect-ratio in grid. */
@media (min-width: 721px) {
  .cal-d, .cal-pad { aspect-ratio: auto; height: 88px; }
}
.cal-d.done      { background: var(--verifiable-wash); color: var(--verifiable-text); font-weight: 600; }
.cal-d.miss      { background: color-mix(in srgb, var(--critical) 16%, var(--surface-1));
                   color: var(--critical-text); font-weight: 700; }
.cal-d.plan      { background: color-mix(in srgb, var(--info) 12%, var(--surface-1)); }
.cal-d.unplanned { background: transparent; border-color: var(--rule); color: var(--muted); }
.cal-d.today     { outline: 2px solid var(--accent); outline-offset: 1px; }
.cal-d.on        { border-color: var(--ink); }
.cal-key { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 11px; color: var(--muted); }
.cal-key span { display: inline-flex; align-items: center; gap: 5px; }
.cal-key i.k { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }
.cal-key i.k.done      { background: var(--verifiable-wash); }
.cal-key i.k.miss      { background: color-mix(in srgb, var(--critical) 16%, var(--surface-1)); }
.cal-key i.k.plan      { background: color-mix(in srgb, var(--info) 12%, var(--surface-1)); }
.cal-key i.k.unplanned { border: 1px solid var(--rule); }
.cal-day { margin-top: 18px; }
.cal-day .pm-day { grid-template-columns: minmax(0,1fr) minmax(0,1fr); border: 0; }
@media (max-width: 720px) { .cal-day .pm-day { grid-template-columns: 1fr; } }

/* ---- My diary, in the field app ---------------------------------------- */
.fd-field { padding: 12px 0; border-bottom: 1px solid var(--rule); }
.fd-text { margin: 4px 0 8px; white-space: pre-line; font-size: 14px; }
.fd-in {
  width: 100%; padding: 10px; border: 1px solid var(--rule); border-radius: 8px;
  background: var(--surface-1); color: var(--ink); font: inherit;
  /* 16px or iOS zooms the page on focus and never zooms back. */
  font-size: 16px; margin: 4px 0 8px;
}
.fd-in:focus { outline: 2px solid var(--focus-border); outline-offset: 1px; }
.fd-note { font-size: 12px; color: var(--critical-text); }
.fd-clock { padding: 12px 0; }
.fd-punch {
  display: inline-block; margin: 4px 6px 0 0; padding: 3px 9px; border-radius: 99px;
  background: var(--verifiable-wash); color: var(--verifiable-text); font-size: 12px;
}
#fdiary .cal-day, #fdiary .fd-field:first-of-type { margin-top: 4px; }
.subsel { margin: 8px 0; padding: 7px 10px; border-radius: 8px;
          border: 1px solid var(--rule); background: var(--surface-1); color: var(--ink);
          font: inherit; font-size: 16px; }

/* ---- Site ownership and membership ------------------------------------- */
.sa-row {
  padding: 14px 0; border-bottom: 1px solid var(--rule);
  display: grid; gap: 8px;
  /* A grid item defaults to min-width:auto and will not shrink below its
     content, which is how one long option name took the whole page to 500px on
     a 320px phone. */
  min-width: 0;
}
.sa-head b { font-size: 15px; }
.sa-own, .sa-mem { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-width: 0; }
.sa-own select, .sa-add {
  padding: 6px 10px; border-radius: 8px; border: 1px solid var(--rule);
  background: var(--surface-1); color: var(--ink); font: inherit; font-size: 16px;
  /* A <select> sizes itself to its WIDEST OPTION, and these carry a full name
     and a designation. Capped, or the longest person in the company sets the
     width of the page. */
  flex: 1 1 180px; min-width: 0; max-width: 100%; width: auto;
}
.sa-chip {
  display: inline-flex; align-items: center; gap: 5px; font-size: 12px;
  background: var(--surface-2); border-radius: 99px; padding: 3px 6px 3px 10px;
}
.sa-x { border: 0; background: transparent; color: var(--muted); cursor: pointer;
        font-size: 15px; line-height: 1; padding: 0 4px; }
.sa-x:hover { color: var(--critical-text); }
.sa-note { font-size: 12px; color: var(--muted); }

/* Nothing on the dashboard scrolls sideways either. Same reasoning as the
   field app: a page that moves left to right has lost the reader's place. */
.panel, .card { min-width: 0; max-width: 100%; }

/* ---- Team performance --------------------------------------------------- */
.tm-tabs { display: flex; flex-wrap: wrap; gap: 6px; margin: 16px 0 6px; }
.tm-tab {
  font: inherit; font-size: 13px; padding: 7px 12px; border-radius: 8px;
  border: 1px solid var(--rule); background: var(--surface-1); color: var(--muted);
  cursor: pointer; display: inline-flex; align-items: center; gap: 7px;
}
.tm-tab.on { background: var(--surface-2); color: var(--ink); font-weight: 600; }
.tm-tab span { font-size: 11px; color: var(--muted); }
.tm-row { padding: 14px 0; border-bottom: 1px solid var(--rule); display: grid; gap: 8px; min-width: 0; }
.tm-who b { font-size: 15px; }
.tm-kpis { display: flex; flex-wrap: wrap; gap: 8px; }
.tm-kpis > span {
  display: grid; gap: 1px; min-width: 92px; padding: 7px 10px;
  background: var(--surface-2); border-radius: 8px; font-size: 14px;
  font-variant-numeric: tabular-nums;
}
.tm-kpis i { font-style: normal; font-size: 10px; text-transform: uppercase;
             letter-spacing: .04em; color: var(--muted); }
.tm-kpis small { font-size: 10px; font-weight: 400; }
.tm-kpis > span.b-green { color: var(--verifiable-text); }
.tm-kpis > span.b-amber { color: var(--warning-text); }
.tm-kpis > span.b-red   { color: var(--critical-text); font-weight: 600; }
.tm-audit { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.tm-audit i { font-style: normal; font-size: 10px; text-transform: uppercase;
              letter-spacing: .04em; color: var(--muted); margin-right: 4px; }
.tm-tag { font-size: 11px; padding: 2px 8px; border-radius: 99px; background: var(--surface-2); }
.tm-tag.b-green { background: var(--verifiable-wash); color: var(--verifiable-text); }
.tm-tag.b-amber { background: color-mix(in srgb, var(--asserted) 14%, var(--surface-1)); color: var(--warning-text); }
.tm-tag.b-red   { background: color-mix(in srgb, var(--critical) 12%, var(--surface-1)); color: var(--critical-text); }

/* ================= COMPACT ESTATE SCREENS (14 Sep 2026) ==================
 * Janardhan, on a phone, looking at Receivables: the header furniture had to be
 * scrolled past twice before the table started, and the table itself then
 * scrolled left to right.
 *
 * Three separate faults, fixed separately:
 *   1. The KPI strip was a RAGGED WRAP. Four flex items sized to their own
 *      content, so "₹1.56 Cr outstanding" and "₹24.1 L 30 to 90 days" were
 *      different widths and each took its own line. A grid makes them one
 *      shape, which is both smaller and quieter.
 *   2. The filter row was five 150px selects, each on its own line on a phone
 *      because a <select> cannot shrink below its widest OPTION unless its
 *      track can. minmax(0,1fr) is what lets it.
 *   3. The table scrolled sideways, which is the one thing this app does not
 *      do. Fixed by collapsing the low-value columns into a second line inside
 *      the row rather than by shrinking type until it cannot be read.
 */
.po-kpis.grid4 {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px; margin: 10px 0 2px;
}
.po-kpis.grid4 .po-kpi {
  min-width: 0; padding: 7px 9px; display: block; text-align: left;
}
.po-kpis.grid4 .po-kpi b    { display: block; font-size: 15px; }
.po-kpis.grid4 .po-kpi span { display: block; font-size: 10px; line-height: 1.25; }

/* The filter bar, as a grid so every control is the same width and a select
   can actually shrink. `min-width: 0` on the select AND minmax(0,1fr) on the
   track: either one alone leaves it sized to its longest option. */
.tasks-filter.compact {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px 10px; align-items: end; margin: 12px 0 2px; padding-bottom: 10px;
}
.tasks-filter.compact label  { font-size: 11px; gap: 2px; min-width: 0; }
/* SELECT ONLY. A <select> opens a picker and does not zoom, so it can be 13px.
   A TEXT INPUT under 16px makes iOS Safari zoom in on focus and never zoom back,
   and this rule is more specific than the `.es-combo input` 16px guard, so
   including `input` here silently re-broke the thing that guard exists to
   prevent. Janardhan hit it on the "Go to site" box within the hour. */
.tasks-filter.compact select { min-width: 0; width: 100%; font-size: 13px; padding: 6px 7px; height: 34px; box-sizing: border-box; }
.tasks-filter.compact input  { min-width: 0; width: 100%; font-size: 16px; padding: 6px 7px; height: 34px; box-sizing: border-box; }
/* The es-combo wrapper input overrides padding for the caret; keep height consistent. */
.tasks-filter.compact .es-combo input { padding: 6px 28px 6px 7px; }
.tasks-filter.compact .tasks-count {
  grid-column: 1 / -1; margin-left: 0; text-align: right; font-size: 12px;
}
.tasks-filter.compact .po-checks {
  grid-column: 1 / -1; display: flex; gap: 18px; align-items: center;
  flex-wrap: wrap; padding: 2px 0;
}
.tasks-filter.compact .cb-label {
  display: flex; align-items: center; gap: 5px;
  font-size: 13px; cursor: pointer; white-space: nowrap; font-weight: normal;
}
.tasks-filter.compact .cb-label input[type="checkbox"] {
  /* Override the full-width input rule above — checkboxes must be compact */
  width: auto; min-width: 0; height: auto; font-size: inherit; padding: 0;
}
.tasks-filter.compact .cb-label small { font-size: 11px; color: var(--ink-3); }

/* ---- A table that does not scroll sideways ------------------------------
 * The columns worth losing first are the ones whose value is a short token the
 * reader can carry on a second line: the invoice number, the type, the age
 * band and the engineer. The site and the money stay, because those are the two
 * things somebody came to the screen for. `.rc-meta` carries the rest and is
 * rendered always, so nothing is missing on a phone, only arranged differently.
 */
.rc-meta { display: none; }
table.es.stack { table-layout: fixed; }
table.es.stack td, table.es.stack th { overflow-wrap: anywhere; }
/* The invoice number is the longest token on the row and the least read. */
table.es.stack .inv { font-size: 11px; margin-left: 0; }

@media (max-width: 700px) {
  .po-kpis.grid4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tasks-filter.compact { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  table.es.stack .hide-sm { display: none; }
  table.es.stack .rc-meta {
    display: block; font-size: 11px; color: var(--muted); margin: 3px 0 0;
    line-height: 1.45;
  }
  table.es.stack .rc-meta .bad  { color: var(--critical-text); font-weight: 600; }
  table.es.stack .rc-meta .warn { color: var(--warning-text); font-weight: 600; }
  table.es.stack { font-size: 13px; }
  table.es.stack td, table.es.stack th { padding-right: 8px; }
  .es-wrap { overflow-x: visible; }
}

/* ================== MONTHLY REPORTS (14 Sep 2026) ========================
 * Seven months against up to nineteen plants. As a table that is a sideways
 * scroll on any phone, so a plant is a LINE and its months are a strip of seven
 * squares under it. Two lines per plant, nothing off-screen at 320px, and a
 * skipped month reads as a red square in a run of green without anybody having
 * to parse a number.
 *
 * The strip is a seven-column grid rather than flex, so every row's squares
 * line up with the month header above them. minmax(0,1fr) again: without it the
 * cells size to their "Aug" label and the row overflows on a narrow screen.
 */
.mr-strip {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 3px; margin: 4px 0 0;
}
.mr-strip.head { margin: 0; }
.mrc {
  display: flex; align-items: center; justify-content: center;
  height: 26px; border-radius: 5px; font-size: 10px; font-weight: 600;
  letter-spacing: .01em; text-decoration: none; overflow: hidden;
  border: 1px solid transparent; color: var(--muted);
  background: var(--surface-2);
}
a.mrc:hover { border-color: var(--ink); }
.mrc.ok      { background: var(--verifiable-wash); color: var(--verifiable-text); }
.mrc.late    { background: color-mix(in srgb, var(--critical) 16%, var(--surface-1));
               color: var(--critical-text); }
/* Filed, but after the deadline. NOT called `.late`, and that is the whole
   point: `.mrc.late` is the MISSING state, it carries the same specificity, and
   it is defined after `.mrc.ok`, so a cell classed `ok late` lost the cascade
   and painted red. Every report filed after the 5th was being shown as a report
   never filed. Caught 14 Sep 2026 by rendering the engineer's own screen and
   looking at it: Varun's thirteen plants were a wall of red against a stated
   52% filed, and the two could not both be true.

   Reported, never scored: the ranking is percentage complete, and folding
   punctuality into it would make one number mean two things. */
.mrc.ok.slow { box-shadow: inset 0 -3px 0 var(--asserted); }
/* Not due yet. Deliberately not amber: nobody is late, and an amber square
   between the 1st and the 5th of every month would train people to ignore it. */
.mrc.due     { background: transparent; border-style: dashed; border-color: var(--rule); }
.mrc.unknown { background: repeating-linear-gradient(45deg,
                 var(--surface-2), var(--surface-2) 4px,
                 var(--surface-1) 4px, var(--surface-1) 8px); }
.mrc.hd      { background: transparent; color: var(--muted); font-weight: 600;
               text-transform: uppercase; height: 18px; font-size: 9px; }

.mr-key { margin: 10px 0 2px; }
.mr-key-l { display: block; font-size: 11px; color: var(--muted); margin-bottom: 2px; }

.mr-legend { display: flex; flex-wrap: wrap; gap: 12px; margin: 10px 0 2px;
             font-size: 11px; color: var(--muted); }
.mr-legend span { display: flex; align-items: center; gap: 5px; }
.mr-legend i.mrc { width: 16px; height: 14px; flex: 0 0 16px; }

.mr-site { padding: 8px 0; border-bottom: 1px solid var(--rule); }
.mr-site-h { display: flex; align-items: baseline; justify-content: space-between;
             gap: 10px; font-size: 13px; }
.mr-site-h a { font-weight: 600; }
.mr-miss { font-size: 11px; font-weight: 600; color: var(--critical-text); white-space: nowrap; }
.mr-all  { font-size: 11px; color: var(--verifiable-text); white-space: nowrap; }
.mr-unk  { font-size: 11px; color: var(--muted); white-space: nowrap; }
.mr-sites.own { margin-top: 2px; }

/* One engineer on the board. A button when it opens its plants, a plain div on
   the engineer's own screen where the other names are standings and not
   drill-downs: their site detail is not his to read. */
.mr-eng { border-bottom: 1px solid var(--rule); }
.mr-eng-h {
  display: grid; grid-template-columns: 26px minmax(0, 1fr) 96px auto;
  align-items: center; gap: 10px; width: 100%; padding: 11px 0;
  background: none; border: 0; font: inherit; color: inherit; text-align: left;
}
button.mr-eng-h { cursor: pointer; }
.mr-rank { font-size: 13px; font-weight: 700; color: var(--muted); text-align: center; }
.mr-who { min-width: 0; }
.mr-who b { display: block; font-size: 14px; }
.mr-who .es-code { display: block; margin: 1px 0 0; font-size: 11px; line-height: 1.4; }
.mr-pct { position: relative; display: block; height: 20px; border-radius: 4px;
          background: var(--surface-2); overflow: hidden; }
.mr-pct i { position: absolute; inset: 0 auto 0 0; display: block; opacity: .35; }
.mr-pct b { position: relative; display: block; line-height: 20px; text-align: center;
            font-size: 11px; font-weight: 700; }
.mr-pct.b-green i { background: var(--verifiable); } .mr-pct.b-green b { color: var(--verifiable-text); }
.mr-pct.b-amber i { background: var(--asserted); }   .mr-pct.b-amber b { color: var(--warning-text); }
.mr-pct.b-red   i { background: var(--critical); }   .mr-pct.b-red   b { color: var(--critical-text); }
.mr-pct.none { height: auto; background: none; font-size: 11px; color: var(--muted); }
.mr-caret { width: 8px; height: 8px; border-right: 2px solid var(--muted);
            border-bottom: 2px solid var(--muted); transform: rotate(45deg);
            margin-right: 3px; }
.mr-eng.open .mr-caret { transform: rotate(-135deg); }
.mr-sites { padding: 2px 0 12px; }
.mr-h3 { font-size: 15px; margin: 26px 0 2px; }
.mr-sep { margin-top: 18px; font-weight: 600; color: var(--ink); }

@media (max-width: 560px) {
  .mr-eng-h { grid-template-columns: 20px minmax(0, 1fr) 62px auto; gap: 7px; }
  .mrc { height: 24px; font-size: 9px; }
  .mr-legend { gap: 9px; }
}

/* The money column needs room for "Outstanding" and for a seven-figure rupee
   value; everything else can take what is left. Without this the fixed layout
   splits the width evenly and the header wraps mid-word. */
@media (max-width: 700px) {
  table.es.stack th.num, table.es.stack td.num { width: 96px; }
  /* The two actions stack rather than sitting side by side: "View PO" and
     "Go to site" on one line is 126px of nowrap text, which no 84px column can
     hold, and widening the column is what pushed the table off the screen in
     the first place. */
  table.es.stack td.po-do { width: 74px; }
  table.es.stack .po-do { flex-direction: column; align-items: flex-end;
                          gap: 6px; white-space: normal; }
}

/* ---- Who covers a plant: the role, and the accountable name ------------
 * Janardhan, 14 Sep 2026: "Highlight engineer (owner) in different color, and
 * indicate FO and mechanic." The line read "Jayraj K · Dasharath R ·
 * Mahalingaiah C · 4 ops", which is three names and no way to tell which is
 * which, on a screen whose whole point is who answers for the plant.
 *
 * The OWNER is coloured, not merely the first engineer in the list. They are
 * different things now that membership exists: an engineer can hold a site
 * without being accountable for it.
 */
.who-r {
  font-size: 9px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); margin-right: 3px;
}
.who-own { color: var(--accent); font-weight: 600; }
.who-own .who-r { color: var(--accent); opacity: .75; }
table.es .who-own { color: var(--accent); font-weight: 600; }

/* ---- Site ownership: read first, change on purpose ---------------------
 * Janardhan, 14 Sep 2026. Every one of 78 rows used to carry a live <select>
 * that POSTed on change, so a mis-tap while scrolling reassigned a plant and
 * its access with no confirmation and no undo. A row is now a summary with a
 * Change button; the controls exist only inside a row somebody opened.
 */
.sa-sum { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.sa-sum-l { min-width: 0; }
.sa-row.read { padding: 11px 0; border-bottom: 1px solid var(--rule); }
.sa-row.editing {
  padding: 12px; margin: 10px 0; border: 1px solid var(--accent);
  border-radius: 10px; background: var(--surface-2);
}
.sa-line { display: block; font-size: 12px; color: var(--muted); margin-top: 3px; }
.bad-inline { color: var(--critical-text); font-weight: 600; }
.sa-split { font-size: 11px; color: var(--muted); margin-top: -4px; }
.sa-open, .sa-close { flex: 0 0 auto; white-space: nowrap; }

/* Smaller again (Janardhan, 14 Sep 2026: "Make everything a little smaller").
   The tiles are a glance, not a headline: the number carries it, the label just
   says which number it is. `tiny` also lets a name sit in the value slot, which
   is what the worst-performer tile needs. */
.po-kpis.grid4.tiny { gap: 5px; margin: 8px 0 2px; }
.po-kpis.grid4.tiny .po-kpi { padding: 5px 8px; }
.po-kpis.grid4.tiny .po-kpi b    { font-size: 13px; line-height: 1.25;
                                   overflow-wrap: anywhere; }
.po-kpis.grid4.tiny .po-kpi span { font-size: 9px; line-height: 1.3; }

/* The board rows, a notch down across the board. */
.mr-eng-h { padding: 9px 0; }
.mr-who b { font-size: 13px; }
.mr-who .es-code { font-size: 10px; }
.mr-rank { font-size: 12px; }
.mr-pct { height: 18px; }
.mr-pct b { line-height: 18px; font-size: 10px; }
.mr-site { padding: 7px 0; }
.mr-site-h { font-size: 12px; }
.mr-miss, .mr-all, .mr-unk { font-size: 10px; }
.mrc { height: 22px; font-size: 9px; }
.mr-legend { font-size: 10px; gap: 10px; margin: 8px 0 2px; }
.mr-h3 { font-size: 14px; margin: 20px 0 2px; }

/* ---- The home grid on a phone: two up, and shorter -----------------------
 * Janardhan, 14 Sep 2026: "these tiles themselves could be smaller to allow
 * more to fit without scrolling."
 *
 * The grid asked for a 200px minimum track, so at 390px exactly one fitted and
 * every tile was a full-width 104px block: six tiles meant scrolling past three
 * screens of menu to reach the seventh. Two fixed columns instead of an
 * auto-fill minimum, and the icon moves onto the title's line rather than
 * sitting on its own above it, which is most of the height.
 *
 * The note is clamped to two lines rather than shortened at the source: it is
 * the only thing telling somebody what a tile does the first time they see it,
 * and it reads fine in full on a laptop.
 */
@media (max-width: 760px) {
  .tile-grid, .tile-grid-top {
    grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin: 8px 0 16px;
  }
  .tile, .tile-wide {
    min-height: 0; padding: 10px 11px 11px; gap: 0;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas: "ico name" "note note";
    align-items: center;
  }
  .tile-ico  { grid-area: ico; width: 15px; height: 15px; margin: 0 6px 0 0; }
  .tile-name { grid-area: name; font-size: 13px; line-height: 1.2;
               padding-right: 20px; overflow-wrap: anywhere; }
  .tile-note { grid-area: note; font-size: 10px; line-height: 1.3; margin-top: 3px;
               display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
               overflow: hidden; }
  .tile-badge { top: -7px; right: -7px; min-width: 20px; padding: 0 6px; font-size: 10px; }
  .home-sec { font-size: 15px; }
  .home-subnote { font-size: 11px; }
}

/* ---- Shift operations log, read on a laptop -----------------------------
 * The ONE screen in this app allowed to scroll sideways, and it says so on the
 * page. Seventeen columns of a log sheet cannot be folded into a phone row
 * without destroying what makes it readable: a column is one instrument across
 * a whole day, and reading down it is how you spot a blower that stopped. It is
 * wrapped in its own scroller so the PAGE never moves, which is the actual rule.
 */
.sl-wrap { overflow-x: auto; margin-top: 8px; border: 1px solid var(--rule);
           border-radius: 10px; }
table.es.sl { font-size: 12px; min-width: 900px; }
table.es.sl th, table.es.sl td { padding: 6px 10px; white-space: nowrap; }
table.es.sl thead th { position: sticky; top: 0; background: var(--surface-1); z-index: 1; }
table.es.sl th i { font-style: normal; font-weight: 400; color: var(--muted); }
.sl-hr { position: sticky; left: 0; background: var(--surface-1);
         font-variant-numeric: tabular-nums; text-align: left; }
.sl-by { color: var(--muted); font-size: 11px; }
/* An hour nobody wrote. Shaded rather than marked, because a gap is a fact
   about the sheet and not an error to shout about. */
tr.sl-gap td, tr.sl-gap th { background: color-mix(in srgb, var(--critical) 5%, transparent); }
.sl-shifts { display: flex; flex-wrap: wrap; gap: 14px; margin: 10px 0 2px; font-size: 12px; }
.sl-shift b { margin-right: 5px; }
.sl-shift i { color: var(--muted); font-style: normal; }

/* Shift log Gantt header — date chip + PDF button */
.sl-head { display: flex; align-items: center; justify-content: space-between;
           flex-wrap: wrap; gap: 10px; margin-bottom: 10px; }
.sl-head h2 { margin: 0; }
.sl-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Left-aligned date chip with visible border — high contrast vs plain text box */
/* flex-direction and the input overrides are for the field app, whose stylesheet
   makes every <label> a column and every input a full-width box: the chip
   rendered 160x76 with the icon stacked over the date. */
.sl-datechip {
  display: inline-flex; flex-direction: row; align-items: center; gap: 6px; cursor: pointer;
  width: auto; align-self: flex-start;
  padding: 5px 10px; border: 1.5px solid var(--border); border-radius: 8px;
  background: var(--surface-2); color: var(--ink-2); font-size: 13px;
}
.sl-datechip:hover { border-color: var(--accent); }
/* 16px on the input itself: iOS zooms any focused control under 16px and never
   zooms back, and this chip is used on the operator's phone as well. */
.sl-datechip input[type="date"] {
  border: none; background: transparent; color: var(--ink);
  font-family: inherit; font-size: 16px; padding: 0; cursor: pointer; outline: none;
  width: auto; min-width: 0; height: auto; min-height: 0; margin: 0;
  border-radius: 0; box-shadow: none; line-height: 1.2;
}
.sl-gantt-wrap { margin-top: 4px; }
/* `display:inline-flex` on .icon-btn beats the browser's own [hidden] rule, so a
   hidden Password or Sign out icon showed on the sign-in screen. */
.icon-btn[hidden] { display: none; }

/* ---- ecotalk header nav icon + unread badge ------------------------------ */
.hdr-msg-btn { position: relative; flex: none; }
/* Home icon beside it, drawn like the field app's (teal line icon). */
.hdr-home-btn { flex: none; color: var(--accent); }
.hdr-msg-badge {
  position: absolute; top: -4px; right: -4px;
  min-width: 18px; height: 18px; padding: 0 4px;
  border-radius: 999px;
  background: var(--critical); color: #fff;
  font: 700 10px/18px var(--sans); text-align: center;
  pointer-events: none;
  box-shadow: 0 0 0 2px var(--surface-1);
  font-variant-numeric: tabular-nums;
}
/* Same rule as icon-btn[hidden]: flex beats [hidden] natively. */
.hdr-msg-badge[hidden] { display: none; }

/* ---- Hamburger dropdown (Janardhan, 27 Sep 2026 iPhone screenshot: every
   row must share ONE pattern -- [icon 20px][label], left-aligned, same
   padding/height -- Theme included, not a left-label-then-far-right-toggle
   outlier; and the panel sizes to its content instead of stretching wide).
   Selectors are scoped under `#hdr-dropdown` so they win over `.icon-btn`'s
   centering regardless of stylesheet order -- both apps share this block.
   Tightened again same day: "some are left aligned some are right? make
   everything left aligned and reduce the pixels in width" -- icons stay on
   every row (Training/Password/Sign out), nothing removed. Every row was
   ALREADY `justify-content:flex-start`+`text-align:left` (no per-row
   override found anywhere in either stylesheet), so the width/padding/gap
   below were cut to read tighter and more obviously left-packed; flag this
   for a fresh screenshot after deploy in case something device-specific
   (not reproduced in static code) is still pulling a row rightward. */
.hdr-menu-wrap { position: relative; flex: none; }
#hdr-dropdown {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 200;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,.15);
  padding: 4px; display: flex; flex-direction: column; gap: 2px;
  width: max-content; min-width: 168px; max-width: min(212px, 70vw);
}
#hdr-dropdown .hdr-dd-btn {
  width: 100%; box-sizing: border-box; display: flex; flex-direction: row;
  align-items: center; justify-content: flex-start; gap: 8px;
  padding: 9px 10px; min-height: 44px; border-radius: 6px;
  text-align: left; font: var(--t-body); color: var(--ink);
}
.dd-label { flex: 1; font: var(--t-body); color: var(--ink); text-align: left; }
.dd-sep { height: 1px; background: var(--border); margin: 4px 0; }
/* The icon column every row shares, Theme's sun/moon included (assets/theme.js
   paints its SVG into this slot so the row's label sibling survives). */
#hdr-dropdown .hdr-dd-ico,
#hdr-dropdown .hdr-dd-btn svg { flex: none; width: 20px; height: 20px; color: var(--ink-2); }
/* A small state/notification dot at a row's right edge (training discoverability,
   27 Sep 2026) -- pushed there by `.dd-label`'s `flex: 1`, not by margin tricks. */
.dd-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--critical); flex: none; }
.dd-dot[hidden] { display: none; }

/* ---- ecotalk tile is no longer in the grid (header icon replaces it) ----- */
[data-go="ecotalk"] { display: none; }

/* ===================== SHIFT LOG GANTT (both apps) ==========================
 * Moved here from operator/field.css so the dashboard's Shift operations log
 * renders styled; field.css loads after this sheet and no longer carries it. */
.gantt-top-bar { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.gantt-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; padding-bottom: 6px; }
.gantt { min-width: 600px; position: relative; }
.gantt-axis { position: relative; height: 20px; border-bottom: 1px solid var(--rule);
  margin-left: 100px; margin-bottom: 4px; }
.gantt-axis-label { position: absolute; transform: translateX(-50%);
  font-size: 10px; color: var(--muted); top: 4px; }
.gantt-section-head { margin: 10px 0 4px; padding-left: 100px;
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted); }
.gantt-row { display: flex; align-items: center; min-height: 32px; }
.gantt-label { width: 140px; min-width: 140px; font-size: 11px; color: var(--muted);
  white-space: normal; word-break: break-word; overflow: visible;
  padding-right: 8px; line-height: 1.2; }
.gantt-track { flex: 1; position: relative; height: 28px;
  border-bottom: 1px solid var(--rule); }
.gantt-band { position: absolute; top: 0; bottom: 0; pointer-events: none; }
.gantt-shift-1st { background: rgba(76,175,80,.06);  }
.gantt-shift-2nd { background: rgba(33,150,243,.06); }
.gantt-shift-3rd { background: rgba(255,152,0,.06);  }
.gantt-shift-gen { background: rgba(255,193,7,.05);  }
.gantt-bar { position: absolute; top: 4px; height: 20px; border-radius: 3px;
  cursor: pointer; min-width: 4px; overflow: hidden; }
.gantt-bar.att        { background: var(--accent-subtle); border: 1px solid var(--accent); }
.gantt-bar.att.away   { border: 2px solid var(--critical-text); }
.gantt-bar.eq-on      { background: var(--good);    border: 1px solid var(--good-text); }
.gantt-bar.activity   { background: var(--info);    border: 1px solid var(--info-text); }
.gantt-bar-label { font-size: 9px; color: var(--accent); padding: 2px 4px;
  white-space: nowrap; overflow: hidden; line-height: 16px; }
.gantt-dot { position: absolute; top: 50%; transform: translate(-50%,-50%);
  width: 14px; height: 14px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; border: 2px solid; }
.gantt-dot.reading { background: var(--verifiable-wash); border-color: var(--verifiable-text); }
.gantt-dot.note    { background: var(--info-text); border-color: transparent; }
.gantt-dot-label   { font-size: 8px; font-weight: 700; color: inherit; }
.gantt-drill { margin: 6px 0 0 100px; padding: 10px 12px;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--radius-md); font-size: 13px; line-height: 1.5; }
.gantt-drill img { max-width: 100%; border-radius: 6px; margin-top: 8px; display: block; }

/* ===================== ECOTALK (both apps) =================================
 * 1-on-1 messages. assets/ecotalk.js. Every text control is 16px: iOS zooms a
 * focused field under 16px and never zooms back. */
.ch { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.ch-bar { display: flex; align-items: center; gap: 10px; min-width: 0; }
.ch-title { margin: 0; flex: 1; font-size: 18px; }
.ch-search {
  width: 100%; box-sizing: border-box; font-family: inherit; font-size: 16px;
  padding: 8px 12px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface-1); color: var(--ink);
}
.ch-note { font-size: 13px; color: var(--muted); margin: 8px 2px; }
.ch-group { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin: 14px 2px 2px; }
.ch-list { list-style: none; margin: 0; padding: 0; }
.ch-row {
  display: flex; align-items: center; gap: 10px; width: 100%; box-sizing: border-box;
  padding: 10px 6px; background: none; border: 0; border-bottom: 1px solid var(--rule);
  color: var(--ink); font: inherit; text-align: left; cursor: pointer; min-width: 0;
}
.ch-row:hover { background: var(--surface-2); }
.ch-row:focus-visible { outline: 2px solid var(--focus-border); outline-offset: -2px; }
.ch-av {
  flex: none; width: 38px; height: 38px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-subtle); color: var(--ink-2); font-weight: 600; font-size: 13px;
}
.ch-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.ch-name { font-weight: 600; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ch-sub { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ch-unread .ch-sub { color: var(--ink-2); font-weight: 500; }
.ch-meta { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.ch-time { font-size: 11px; color: var(--muted); white-space: nowrap; }
.ch-badge {
  min-width: 18px; padding: 1px 6px; border-radius: 999px; box-sizing: border-box;
  background: #25D366; color: #fff; font-size: 11px; font-weight: 700; text-align: center;
}
.ch-thread { height: min(72vh, calc(100dvh - 200px)); min-height: 360px; }
.ch-msgs {
  flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch;
  display: flex; flex-direction: column; gap: 6px; padding: 10px 6px;
  background: var(--plane); border: 1px solid var(--rule); border-radius: 12px;
}
.ch-day { align-self: center; margin: 6px 0 2px; padding: 2px 10px; border-radius: 999px;
  font-size: 11px; color: var(--muted); background: var(--surface-2); }
.ch-b {
  align-self: flex-start; max-width: min(80%, 520px); box-sizing: border-box;
  padding: 7px 10px 4px; border-radius: 12px 12px 12px 3px;
  background: var(--surface-1); border: 1px solid var(--border);
}
.ch-b.ch-mine { align-self: flex-end; border-radius: 12px 12px 3px 12px;
  background: var(--accent-subtle); border-color: transparent; }
.ch-text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 15px; line-height: 1.35; color: var(--ink); }
.ch-stamp { display: block; text-align: right; font-size: 10px; color: var(--muted); margin-top: 2px; white-space: nowrap; }
.ch-tick.read { color: #1d9bf0; font-weight: 700; }
.ch-pending { opacity: .7; }
.ch-failed { border: 1px solid var(--critical); cursor: pointer; }
.ch-failed .ch-stamp { color: var(--critical-text); white-space: normal; }
.ch-compose { display: flex; align-items: flex-end; gap: 8px; }
.ch-compose[hidden] { display: none; }
.ch-compose textarea {
  flex: 1; min-width: 0; resize: none; max-height: 120px; box-sizing: border-box;
  font-family: inherit; font-size: 16px; line-height: 1.3; padding: 9px 14px;
  border: 1px solid var(--border); border-radius: 20px; background: var(--surface-1); color: var(--ink);
}
.ch-compose textarea:focus { outline: none; border-color: var(--focus-border); }
.ch-send {
  flex: 0 0 42px; width: 42px; height: 42px; border-radius: 50%; border: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  background: #25D366; color: #fff; -webkit-tap-highlight-color: transparent;
}
.ch-send:focus-visible { outline: 2px solid var(--focus-border); outline-offset: 2px; }
.ch-card { padding: 14px; }
@media (max-width: 460px) { .ch-card { padding: 10px; } .ch-b { max-width: 86%; } }

/* ---- Clock, on the dashboard -------------------------------------------
 * The same punch the field app takes, reachable without leaving for it. No
 * selfie here: an operator's photo is the proof behind their wages, an engineer
 * at his own desk in head office does not need to photograph himself, and
 * asking would make the tile something people route around.
 */
.ck-open { padding: 12px 14px; border-radius: 10px; margin: 6px 0 14px;
           background: var(--verifiable-wash); }
.ck-open b { display: block; font-size: 15px; color: var(--verifiable-text); }
.ck-open span { font-size: 12px; color: var(--muted); }
/* The soft-geofence distance line (27 Sep 2026): red when outside the fence,
   the normal muted tone when inside it. Management clock screen only. */
.ck-open .ck-away { display: block; font-size: 12px; font-weight: 600; color: var(--critical-text); }
.ck-open .ck-at   { display: block; font-size: 12px; color: var(--muted); }
.ck-note { display: inline-block; margin-left: 10px; font-size: 12px; color: var(--muted); }
.ck-rule { margin-top: 12px; }
#ck-ntext { width: 100%; box-sizing: border-box; font: inherit; font-size: 16px;
            padding: 9px 10px; border-radius: 10px; border: 1px solid var(--rule);
            background: var(--surface-1); color: var(--ink); margin: 6px 0 10px; }

/* ---- The open-punch bar, on every screen -------------------------------
 * Janardhan, 14 Sep 2026: "clocked in at <name> since <time/date> shown
 * prominently across all screens for engineers." Outside every panel, above the
 * breadcrumb. Green rather than red: being clocked in is the correct state
 * while you are at work, and a red bar all day would train people to ignore it.
 */
.punchbar {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 14px; font-size: 13px;
  background: var(--verifiable-wash); color: var(--verifiable-text);
  border-bottom: 1px solid color-mix(in srgb, var(--verifiable) 30%, transparent);
}
.punchbar b { font-weight: 700; }
.punchbar-dot { width: 8px; height: 8px; border-radius: 50%; flex: none;
                background: var(--verifiable-text); }
.punchbar-go {
  margin-left: auto; flex: none; cursor: pointer; font: inherit; font-size: 12px;
  font-weight: 600; padding: 4px 11px; border-radius: 999px;
  border: 1px solid currentColor; background: none; color: inherit;
}
.punchbar-go:hover { background: color-mix(in srgb, var(--verifiable) 18%, transparent); }
@media (max-width: 700px) {
  .punchbar { font-size: 12px; padding: 7px 14px; gap: 8px; }
  .punchbar-go { padding: 3px 9px; font-size: 11px; }
}

/* ---- Regent Prime as its own shortcut ----------------------------------
 * "Regent Prime should likely be its own shortcut, not drop down as those are
 * sites." It is not a plant and does not belong in a list of plants: an
 * engineer's day starts there, so it is one tap rather than a scroll through
 * seventy-eight names.
 */
.ck-shortcuts { display: grid; gap: 8px; margin: 10px 0 14px; }
.ck-office-row { display: flex; align-items: center; gap: 10px; margin: 0 0 8px; }
.ck-office-label { flex: 1; min-width: 0; }
.ck-office-label b { display: block; font-size: 15px; }
.ck-office-label i { display: block; font-style: normal; font-size: 11px;
                     color: var(--muted); margin-top: 2px; }

/* =====================================================================
 * COMPACT, EVERYWHERE (Janardhan, 14 Sep 2026, from his phone)
 *
 * "Reduce tile size", "esp in the summaries". Written at the END of the file on
 * purpose: several of the rules below replace earlier ones of the same
 * specificity, and a media query adds none, so source order is what decides.
 * One block, so every summary strip and every tile shrinks together and a new
 * screen that reuses the classes gets the same size for free.
 * ===================================================================== */

/* The theme toggle is now a `.hdr-dd-btn` row like its dropdown siblings
   (27 Sep 2026) -- see the "Hamburger dropdown" block above. `#theme-btn`'s
   old standalone circle-button styling is gone with it; line ~531's
   `.theme-btn` print-hide entry is now inert (nothing carries that class)
   and harmless. */

/* The field app's header, one notch tighter to match. The ID keeps these ahead
   of operator/field.css, which loads after this sheet. */
#app header.fhead { padding: 8px 14px; gap: 8px; }
#app header.fhead .who { font-size: 15px; }
@media (max-width: 460px) { #app header.fhead { row-gap: 4px; } }

/* ---- Home tiles: smaller, and more of them per screen ------------------- */
.tile-grid { gap: 8px; margin: 10px 0 18px;
             grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); }
.tile, .tile-wide {
  min-height: 0; padding: 9px 11px 10px; gap: 0; border-radius: 10px;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: "ico name" "note note"; align-items: center;
}
.tile-ico  { grid-area: ico; width: 16px; height: 16px; margin: 0 7px 0 0; }
.tile-name { grid-area: name; font-size: 13px; line-height: 1.25; padding-right: 20px;
             overflow-wrap: anywhere; }
.tile-note { grid-area: note; font-size: 11px; line-height: 1.3; margin-top: 3px;
             display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
             overflow: hidden; }
.tile-badge { top: -7px; right: -7px; min-width: 20px; padding: 0 6px; font-size: 10px; }
@media (max-width: 760px) {
  /* iOS app-icon style: 4-column square grid, icon centred, short label below, no note.
     overflow:visible so the badge can peek out of the corner without being clipped. */
  .tile-grid, .tile-grid-top { grid-template-columns: repeat(4, minmax(0, 1fr));
                               gap: 5px; margin: 6px 0 14px; }
  .tile, .tile-wide {
    aspect-ratio: 1; padding: 8px 4px 6px; border-radius: 12px;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 4px; overflow: visible;
  }
  .tile-ico  { width: 18px; height: 18px; margin: 0; }
  .tile-name { font-size: 9px; line-height: 1.25; padding-right: 0; text-align: center;
               overflow-wrap: break-word; }
  .tile-note { display: none; }
  .tile-badge { top: -6px; right: -6px; min-width: 14px; padding: 0 3px; font-size: 8px; }
  .home-sec { font-size: 14px; }
}
@media (max-width: 340px) {
  .tile-grid, .tile-grid-top { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---- ONE summary style: every KPI strip at the top of a screen ----------
 * Small label, one tight number, several per row at 400px. Applies to the
 * estate strips (.po-kpis), the per-plant cards (.field-kpis .card.kpi and
 * .kpi-row), the Exec summary money cards (.big-stat) and the Engineer push
 * counts (.pstat), so they shrink together rather than one screen at a time. */
.po-kpis { gap: 5px; margin: 8px 0 2px; }
.po-kpi { min-width: 0; padding: 5px 8px; gap: 5px; border-radius: 7px; }
.po-kpi b { font-size: 14px; line-height: 1.2; }
.po-kpi span { font-size: 10px; line-height: 1.25; }
.po-kpis.grid4 { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 5px; margin: 8px 0 2px; }
.po-kpis.grid4 .po-kpi { padding: 5px 7px; }
.po-kpis.grid4 .po-kpi b { font-size: 14px; line-height: 1.2; overflow-wrap: anywhere; }
.po-kpis.grid4 .po-kpi span { font-size: 10px; line-height: 1.25; }

.panel .field-kpis, .panel .kpi-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 5px; margin: 0 0 10px;
}
.panel .field-kpis .card.kpi, .panel .kpi-row .card.kpi {
  min-width: 0; padding: 6px 8px; margin: 0; border-radius: 7px;
}
.panel .card.kpi .label { font-size: 10px; line-height: 1.25; }
.panel .card.kpi .value { font-size: 15px; line-height: 1.2; margin: 1px 0 0;
                          font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.panel .card.kpi .unit  { font-size: 10px; margin-left: 2px; }
.panel .card.kpi .note-inline { font-size: 10px; margin: 1px 0 0; line-height: 1.25; }

.big-row { gap: 6px; }
.big-stat { padding: 7px 9px; border-radius: 8px; gap: 1px; }
.big-label { font-size: 10px; letter-spacing: .02em; }
.big-value { font-size: 17px; line-height: 1.15; }
.big-sub   { font-size: 10px; line-height: 1.25; }

.push-stats { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); margin-bottom: 10px; }
.pstat { padding: 6px 9px; gap: 6px; }
.pstat b { font-size: 16px; }
.pstat span { font-size: 12px; }
.pstat em { font-size: 10px; }

@media (max-width: 700px) {
  /* FOUR ACROSS on a phone, not two: a summary is glanced at, and two rows of
     two pushed the table below the fold. */
  .po-kpis.grid4 { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px; }
  .po-kpis.grid4 .po-kpi { padding: 4px 5px; }
  .po-kpis.grid4 .po-kpi b { font-size: 12.5px; }
  .po-kpis.grid4 .po-kpi span { font-size: 9px; }
  .panel .field-kpis, .panel .kpi-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .panel .card.kpi .value { font-size: 13.5px; }
  .big-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .big-value { font-size: 14px; }
  .push-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---- Clock, compact ----------------------------------------------------- */
.ck-card { padding: 12px 14px; }
.ck-h { font-size: 15px; margin: 0 0 8px; display: flex; align-items: baseline; gap: 8px; }
.ck-h span { font-size: 11px; font-weight: 400; color: var(--muted); }
.ck-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: center; }
.ck-row select { min-width: 0; width: 100%; font-size: 16px; padding: 7px 8px; }
.ck-row .es-combo { flex: 1; min-width: 0; }
.ck-row .btn { white-space: nowrap; }
.ck-card > select { width: 100%; font-size: 16px; padding: 7px 8px; margin: 0 0 6px; }
.ck-open { padding: 8px 10px; margin: 0 0 6px; border-radius: 9px; }
.ck-open b { font-size: 14px; }
.ck-open span { font-size: 11px; }
.ck-office-row { margin: 0 0 6px; }
.ck-office-label b { font-size: 14px; }
.ck-office-label i { font-size: 11px; margin: 0; }
.ck-fine { display: block; font-size: 11px; color: var(--muted); margin: 6px 0 0; line-height: 1.3; }
.ck-row .ck-fine { margin: 0; }
#ck-ntext { margin: 0 0 6px; padding: 7px 9px; }
.ck-note:empty { display: none; }
.ck-note { margin: 4px 0 0; }
/* Inline confirm/cancel links inside .ck-note */
.btn-link { background: none; border: none; padding: 0; font: inherit; font-weight: 600;
            color: var(--accent); cursor: pointer; text-decoration: underline; }
.ck-note.ck-warn { color: var(--critical-text); }
.ck-note.ck-ok   { color: var(--verifiable-text); font-weight: 500; }
.ck-note.ck-err  { color: var(--critical-text); font-weight: 600; font-size: 13px; }
/* "Clock in anyway (location unconfirmed)", offered under a vague-fix refusal
   at a plant (assets/geofence.js fallbackAllowed). */
.ck-fallback { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin: 8px 0 0; }
.ck-fallback span { font-size: 11px; color: var(--muted); line-height: 1.3; flex: 1 1 180px; }

/* ---- Monthly reports: one figure first, then smaller rows --------------- */
.mr-head { display: flex; align-items: baseline; gap: 10px; margin: 4px 0 0; }
.mr-head b { font-size: 20px; line-height: 1.2; letter-spacing: -.01em; }
.mr-head-pct { font-size: 12px; font-weight: 700; padding: 1px 7px; border-radius: 999px;
               background: var(--surface-2); }
.mr-head-pct.b-green { color: var(--verifiable-text); background: var(--verifiable-wash); }
.mr-head-pct.b-amber { color: var(--warning-text);
                       background: color-mix(in srgb, var(--asserted) 14%, var(--surface-1)); }
.mr-head-pct.b-red   { color: var(--critical-text);
                       background: color-mix(in srgb, var(--critical) 12%, var(--surface-1)); }
.mr-sub { font-size: 11px; color: var(--muted); margin: 2px 0 0; line-height: 1.35; }
.mr-rule { font-size: 11px; margin: 2px 0 4px; }
.mr-legend { flex-wrap: nowrap; gap: 8px; font-size: 10px; margin: 6px 0 2px; overflow: hidden; }
.mr-legend span { gap: 3px; white-space: nowrap; }
.mr-legend i.mrc { width: 12px; height: 10px; flex: 0 0 12px; }
.mr-eng-h { padding: 5px 0; gap: 8px; }
.mr-who b { font-size: 12.5px; line-height: 1.2; }
.mr-who .es-code { font-size: 10px; line-height: 1.3; margin: 0; }
.mr-rank { font-size: 11px; }
.mr-pct { height: 16px; }
.mr-pct b { line-height: 16px; font-size: 10px; }
.mr-site { padding: 4px 0; }
.mr-site-h { font-size: 11.5px; }
.mr-site-h a { font-weight: 600; }
.mr-miss, .mr-all, .mr-unk { font-size: 10px; }
.mr-strip { gap: 2px; margin: 2px 0 0; }
.mrc { height: 17px; font-size: 8.5px; border-radius: 4px; }
.mrc.hd { height: 14px; font-size: 8px; }
.mr-h3 { font-size: 13.5px; margin: 16px 0 0; }
.mr-sep { margin-top: 12px; }

/* ---- Finance tables ----------------------------------------------------- */
.fin-work { display: block; font-size: 11.5px; color: var(--muted); line-height: 1.3; margin-top: 1px; }
.fin-po { font-weight: 600; font-variant-numeric: tabular-nums; }
.fin-asat { font-size: 11px; margin: 2px 0 0; }
.fin-asat.warn { color: var(--warning-text); }
.fin-foot { font-size: 11px; margin-top: 10px; line-height: 1.4; }
.fin-actions { margin: 8px 0 0; align-items: center; }
.fin-actions .note-inline { font-size: 11px; }
.fin-details { font-size: 11px; margin-top: 4px; color: var(--muted); }
.fin-details summary { cursor: pointer; }
.tasks-filter.compact .fin-find { grid-column: span 2; }
table.es.fin th:nth-child(2) { width: 30%; }
#wc-out { margin: 6px 0 4px; }
#wc-out .fl textarea { min-height: 150px; }
@media (max-width: 700px) {
  .tasks-filter.compact .fin-find { grid-column: 1 / -1; }
  table.es.fin th:nth-child(2) { width: auto; }
}

/* A tile name breaks between words, never inside one. `anywhere` let
   "Receivables" wrap as "Receivable / s" three across on a phone, found on the
   finance home screen, 14 Sep 2026. The space held for a badge is released when
   the tile has none, which is most of them. */
.tile-name { overflow-wrap: break-word; word-break: normal; hyphens: auto; }
@media (max-width: 760px) {
  .tile-name { font-size: 11.5px; }
  .tile:not(:has(.tile-badge)) .tile-name { padding-right: 0; }
}
/* Cards on a phone: the desktop padding and a 22px heading were a quarter of
   the first screen before a single row. Element plus class so it beats `.card`
   from the base sheet whatever order the two load in. */
@media (max-width: 700px) {
  section.panel > .card { padding: 14px; }
  section.panel > .card > h2 { font-size: 18px; margin: 0 0 4px; }
}

/* ---- Clock-ins: team summary and one person's days (14 Sep 2026) -------- */
.tasks-filter.compact .pc-custom { grid-column: 1 / -1; display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 10px; }
.tasks-filter.compact .pc-custom[hidden] { display: none; }
table.es.pc th:first-child { width: 34%; }
.es-code.ok { color: var(--verifiable-text); font-weight: 600; }
#panel-attendance > div + div { margin-top: 10px; }
.pp-day { border-top: 1px solid var(--rule); padding: 8px 0 6px; }
.pp-dh { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; font-size: 12px; }
.pp-dh b { font-size: 13px; }
.pp-dh .ok { color: var(--verifiable-text); }
.pp-dh .warn { color: var(--warning-text); font-weight: 600; }
.pp-hrs { margin-left: auto; color: var(--muted); }
.pp-chain { list-style: none; margin: 4px 0 0; padding: 0 0 0 10px; border-left: 2px solid var(--rule); }
.pp-v { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 8px; align-items: center;
        padding: 4px 0 4px 8px; font-size: 12px; position: relative; }
.pp-v::before { content: ''; position: absolute; left: -15px; top: 10px; width: 8px; height: 8px;
                border-radius: 50%; background: var(--accent); }
.pp-v.plant::before { background: var(--verifiable-text); }
.pp-v.flag::before { background: var(--critical); }
/* "Still in" is not a problem the way the other flags are (no clock-out,
   over 16 h, auto-closed) -- it just means the visit has not ended yet.
   Janardhan, 28 Sep 2026: "'still in' should not show in red... maybe
   amber... or actually green for in... once clock out it reverts to
   black or ecotech teal". Green while open; once out_at is set this class
   no longer applies and it falls through to the plain default (--accent,
   the Ecotech teal, already the row's base colour). */
.pp-v.still-in::before { background: var(--good); }
.pp-v.still-in .pp-t b { color: var(--good-text); font-weight: 600; }
.pp-place { font-weight: 600; min-width: 0; overflow-wrap: anywhere; }
.pp-t { grid-column: 1; color: var(--muted); }
.pp-v.flag .pp-t b { color: var(--critical-text); font-weight: 600; }
.punch-away { color: var(--critical-text); font-weight: 600; font-size: 11px; margin-left: 4px; }
.pp-phs { grid-column: 2; grid-row: 1 / span 2; display: flex; gap: 4px; }
.pp-ph { width: 40px; height: 40px; object-fit: cover; border-radius: 6px; background: var(--surface-2); }
@media (max-width: 700px) {
  table.es.pc th:first-child { width: auto; }
  .pp-hrs { margin-left: 0; }
}

/* ---- Works in my plants (mechanic / FO dashboard) ----------------------- */
.mw-filter { width: 100%; max-width: 360px; margin: 10px 0 0; padding: 7px 11px;
             border: 1px solid var(--border); border-radius: 8px; font: var(--t-body);
             background: var(--surface-2); color: var(--ink); outline: none; box-sizing: border-box; }
.mw-filter:focus { border-color: var(--accent); box-shadow: 0 0 0 2px var(--focus-ring, color-mix(in srgb, var(--accent) 20%, transparent)); }
.mw-tabbar { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0 16px; }
.mw-tab { display: inline-flex; align-items: center; gap: 6px; }
.mw-tab-on,
.mw-tab-on:hover,
.mw-tab-on:active { background: var(--accent) !important; color: var(--accent-fg) !important; border-color: transparent; }
.mw-tab { -webkit-tap-highlight-color: transparent; }
.mw-count { display: inline-block; min-width: 18px; padding: 1px 5px;
            background: rgba(0,0,0,.12); border-radius: 99px; font-size: 11px;
            font-variant-numeric: tabular-nums; line-height: 16px; text-align: center; }
.mw-tab-on .mw-count { background: rgba(255,255,255,.25); }
.mw-table { width: 100%; }
.mw-desc { max-width: 280px; }
.mw-mono { font-family: var(--mono); font-size: 12px; }
.mw-stage { display: inline-block; font: 11px/1 var(--sans); border-radius: 4px;
            padding: 3px 7px; white-space: nowrap; }
.mw-stage-warn  { background: var(--warning-wash, #fff8e1);  color: var(--warning-text, #7c5c00); }
.mw-stage-ok    { background: var(--verifiable-wash, #e8f5e9); color: var(--verifiable-text); }
.mw-stage-mid   { background: var(--accent-subtle); color: var(--accent); }
.mw-stage-clear { background: var(--surface-2); color: var(--ink-2); }
.muted { color: var(--muted); }

/* ---- Perform PM (dashboard wizard) -------------------------------------- */
.pm-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 0 0 6px; }
.pm-head span { font: var(--t-meta, 12px/1 var(--font)); color: var(--muted); }
.pm-bar { height: 4px; border-radius: 3px; background: var(--border); overflow: hidden; margin-bottom: 14px; }
.pm-bar > div { height: 100%; background: var(--accent); transition: width .25s ease; }
.pm-items { margin-bottom: 12px; }
.pm-group { font-size: 12px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); margin: 16px 0 6px; }
.pm-item { padding: 10px 0; border-bottom: 1px solid var(--border); }
.pm-q { font-size: 15px; line-height: 1.4; margin-bottom: 8px; color: var(--ink); }
.pm-a { display: flex; gap: 7px; flex-wrap: wrap; }
.pm-opt { flex: 1; min-height: 44px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--surface); font-size: 14px; cursor: pointer; }
.pm-opt.on { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
.pm-val { flex: 1; min-height: 44px; font-size: 16px; padding: 0 12px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--surface); }
.pm-note-lbl { display: block; font-size: 13px; color: var(--muted); margin-bottom: 6px; }
.pm-note-ta { display: block; width: 100%; box-sizing: border-box; border: 1px solid var(--border);
  border-radius: 8px; padding: 8px 10px; font: inherit; font-size: 15px;
  background: var(--surface); resize: vertical; margin-top: 4px; }
.pm-photo-row { margin: 10px 0; }
.pm-nav { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.pm-draft-saved { font-size: 12px; color: var(--verifiable-text); margin-left: 8px; }
.pm-foot { border-top: 1px solid var(--border); margin-top: 14px; padding-top: 14px; }
.pm-sum { font-size: 15px; color: var(--ink); margin: 0 0 12px; }
.pm-bad { background: #fff6e5; border-left: 3px solid var(--warning, #e6a817);
  color: var(--warning-text, #7c5c00); padding: 10px 12px; border-radius: 6px; margin: 10px 0; }
.pm-bad b { display: block; margin-bottom: 4px; }
.pm-skip { background: var(--verifiable-wash, #e8f5e9); color: var(--verifiable-text);
  padding: 10px 12px; border-radius: 6px; margin: 10px 0; font-size: 14px; }

/* ---- Shared PM / Service location UI ------------------------------------ */
/* Amber away-from-site warning — non-blocking, stays visible throughout form */
.pm-away-warn {
  background: color-mix(in srgb, var(--warn, #f59e0b) 15%, var(--surface-1));
  border: 1px solid color-mix(in srgb, var(--warn, #f59e0b) 50%, transparent);
  color: var(--ink);
  border-radius: 8px;
  padding: 10px 12px;
  font-size: 13px;
  line-height: 1.4;
}
/* Persistent site name chip once a site is selected */
.pm-site-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--surface-2, var(--surface-1));
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 4px 12px;
  font-size: 13px;
  font-weight: 600;
  color: var(--accent);
  margin-bottom: 4px;
}
.pm-site-chip::before { content: '📍'; font-size: 12px; }

/* ---- Perform Service ---------------------------------------------------- */
.psvc-card { display: flex; flex-direction: column; gap: 14px; }
.psvc-lbl { display: block; font-size: 13px; color: var(--muted); }
.psvc-ta { display: block; width: 100%; box-sizing: border-box; border: 1px solid var(--border);
  border-radius: 8px; padding: 10px 12px; font: inherit; font-size: 15px;
  background: var(--surface); resize: vertical; margin-top: 4px; }
.psvc-photos { display: flex; flex-wrap: wrap; gap: 8px; }
.psvc-thumb { position: relative; width: 80px; height: 80px; }
.psvc-thumb img { width: 100%; height: 100%; object-fit: cover; border-radius: 8px; border: 1px solid var(--border); }
.psvc-rm { position: absolute; top: -6px; right: -6px; width: 22px; height: 22px;
  border-radius: 50%; background: var(--critical-text, #c0392b); color: #fff;
  border: none; font-size: 14px; line-height: 1; cursor: pointer; padding: 0; }
.psvc-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.psvc-msg { color: var(--critical-text, #c0392b); }

@media (max-width: 520px) {
  .mw-tabbar { gap: 4px; }
  .mw-tab { font-size: 12px; }
  .mw-desc { max-width: 160px; overflow-wrap: anywhere; }
}

/* ---- psvc PDF attachment row -------------------------------------------- */
.psvc-pdf-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.psvc-pdf-name { font-size: 13px; color: var(--ink); }
.psvc-pdf-rm { background: none; border: none; color: var(--muted); cursor: pointer;
  font-size: 13px; padding: 0; }

/* ---- visit-view: rejected state + reject button -------------------------- */
.vv-state.bad { background: color-mix(in srgb, var(--critical) 14%, var(--surface-1));
  color: var(--critical-text); }
.vv-reject-btn { background: none; border: 1px solid var(--critical-text);
  color: var(--critical-text); }
.vv-reject-row { margin-top: 10px; display: flex; flex-wrap: wrap; align-items: flex-start;
  gap: 8px; }
.vv-reject-ta { flex: 1 1 220px; border: 1px solid var(--border); border-radius: 8px;
  padding: 8px 10px; font: inherit; font-size: 14px; background: var(--surface);
  resize: vertical; }

/* ---- my-reports: mechanic submission history ----------------------------- */
.mr-list { display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }
.mr-card { display: grid; grid-template-columns: auto 1fr; gap: 2px 10px;
  border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px;
  cursor: pointer; background: var(--surface-1); }
.mr-card:active { background: var(--surface-2); }
.vv-badge { grid-row: 1 / 3; align-self: center; display: inline-block;
  padding: 2px 10px; border-radius: 999px; font-size: 11px; font-weight: 700;
  white-space: nowrap; }
.vv-badge.ok   { background: var(--verifiable-wash); color: var(--verifiable-text); }
.vv-badge.warn { background: color-mix(in srgb, var(--asserted) 14%, var(--surface-1));
  color: var(--warning-text); }
.vv-badge.bad  { background: color-mix(in srgb, var(--critical) 14%, var(--surface-1));
  color: var(--critical-text); }
.mr-kind  { font-size: 14px; font-weight: 600; }
.mr-when  { font-size: 12px; color: var(--muted); }
.mr-items { font-size: 12px; color: var(--muted); }

/* My Reports — mechanic submission table */
.mr-table { width:100%; border-collapse:collapse; font-size:13px; }
.mr-table th { text-align:left; padding:6px 8px; border-bottom:2px solid var(--line); color:var(--muted); font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:.04em; }
.mr-table td { padding:8px; border-bottom:1px solid var(--line); vertical-align:top; }
.mr-badge { display:inline-block; padding:2px 7px; border-radius:10px; font-size:11px; font-weight:600; }
.mr-badge.mr-ok   { background:#d4edda; color:#155724; }
.mr-badge.mr-bad  { background:#f8d7da; color:#721c24; }
.mr-badge.mr-pend { background:#fff3cd; color:#856404; }
.mr-badge.mr-ret  { background:#ffe5b4; color:#7a4800; }
.mr-loc { font-size:11px; }
.mr-loc.ok  { color:var(--ok-text, #198754); }
.mr-loc.bad { color:var(--critical-text, #dc3545); }
.mr-reason { font-size:11px; color:var(--critical-text, #dc3545); margin-top:3px; font-style:italic; }
.mr-by { color:var(--muted); font-size:12px; }
.mr-actions { white-space:nowrap; }
.mr-del { background:none; border:none; font-size:16px; cursor:pointer; padding:2px 6px; color:var(--critical-text, #dc3545); }

/* ---------------------------------------------------------------- Control Panel
   The plant's equipment as indicator lamps (26 Sep 2026). Green = running,
   red = off, grey = never reported, amber hatched = last known only (older
   than 24 h, or this screen lost its connection). Read-only. Tokens used are
   all defined: --good, --critical, --warning, --track, --border, --muted. */
/* The lamp colours, as ONE pair: green = ON, red = OFF (Janardhan's ask,
   26 Sep 2026). Indian MCC panels often use the opposite convention (red =
   running); switching is these two lines. */
:root { --cp-on: var(--good); --cp-off: var(--critical); }
.cp-head h2 { margin:0; }
.cp-sum { font:var(--t-body); color:var(--ink-2); margin:6px 0 0; }
.cp-n-on  { color:var(--good-text); }
.cp-n-off { color:var(--critical-text); }
.cp-offline { color:var(--warning-text); }
.cp-group { margin-top:16px; }
.cp-group-h { font:var(--t-meta); color:var(--muted); text-transform:uppercase; letter-spacing:.08em; margin:0 0 8px; }
.cp-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(132px, 1fr)); gap:10px; }
.cp-lamp-wrap { display:flex; flex-direction:column; align-items:center; text-align:center; gap:3px;
  padding:12px 8px 10px; border:1px solid var(--border); border-radius:var(--radius-md);
  background:var(--surface-1); color:inherit; cursor:pointer; min-width:0; font:inherit; }
.cp-lamp-wrap.open { border-color:var(--accent); }
.cp-lamp { width:30px; height:30px; border-radius:50%; margin-bottom:4px; border:2px solid var(--border);
  background:var(--track); box-shadow:inset 0 -3px 6px rgba(0,0,0,.18); }
.cp-lamp.cp-on  { background:var(--cp-on);    box-shadow:0 0 10px 1px var(--cp-on), inset 0 -3px 6px rgba(0,0,0,.15); }
.cp-lamp.cp-off { background:var(--cp-off); }
.cp-lamp.cp-stale { background:repeating-linear-gradient(135deg, var(--warning) 0 4px, var(--track) 4px 8px); }
.cp-name  { font:var(--t-body); font-weight:600; color:var(--ink); overflow-wrap:anywhere; line-height:1.25; }
.cp-state { font:var(--t-meta); font-weight:700; letter-spacing:.06em; color:var(--ink-2); }
.cp-by    { font:var(--t-meta); color:var(--muted); overflow-wrap:anywhere; }
.cp-when .tcell { font:var(--t-meta); color:var(--ink-2); }
.cp-hist { margin-top:16px; border-top:1px solid var(--border); padding-top:10px; }
.cp-hist h3 { font:var(--t-body); font-weight:600; margin:0 0 6px; }
.cp-hist ul { list-style:none; margin:0; padding:0; display:grid; gap:6px; }
.cp-hist li { display:flex; flex-wrap:wrap; align-items:flex-start; gap:4px 8px; font:var(--t-body); }
.cp-foot { margin-top:14px; }
@media (max-width: 420px) { .cp-grid { grid-template-columns:repeat(2, minmax(0, 1fr)); } }
/* Operator app only (renderControlPanel with opts): the same lamp, with the
   ON/OFF pair under it when he is on shift at this site (27 Sep 2026). The
   dashboard never renders these classes. Explicit width/min-height/direction
   because field.css makes every control a 46px full-width box. */
.cp-cell { display:flex; flex-direction:column; gap:6px; min-width:0; }
.cp-cell > .cp-lamp-wrap { width:100%; }
.cp-ctl { display:flex; flex-direction:row; border:1px solid var(--border); border-radius:var(--radius-sm); overflow:hidden; }
.cp-ctl .cp-sw { flex:1 1 0; width:auto; min-width:0; min-height:44px; padding:6px 4px; margin:0; border:none; border-radius:0;
  background:var(--surface-2); color:var(--ink-2); font:inherit; font-size:15px; font-weight:700; letter-spacing:.04em; cursor:pointer; }
.cp-ctl .cp-sw + .cp-sw { border-left:1px solid var(--border); }
.cp-ctl .cp-sw-on.is-active  { background:var(--cp-on);  color:var(--surface-1); }
.cp-ctl .cp-sw-off.is-active { background:var(--cp-off); color:var(--surface-1); }
.cp-ctl .cp-sw:disabled { opacity:.55; cursor:wait; }
.cp-msg { display:block; font:var(--t-meta); color:var(--ink-2); line-height:1.35; min-height:0; overflow-wrap:anywhere; }
.cp-msg:empty { display:none; }
.cp-msg-pending { color:var(--warning-text); }
.cp-access { font:var(--t-body); margin:8px 0 0; padding:8px 10px; border-radius:var(--radius-sm); border:1px solid var(--border); }
.cp-access.cp-rw { border-color:var(--cp-on); }
.cp-access.cp-ro { color:var(--ink-2); background:var(--surface-2); }
.cp-lamp-wrap.cp-flash { border-color:var(--accent); box-shadow:0 0 0 2px var(--accent); }
/* Duty/standby pairs and flags (27 Sep 2026), both apps. */
.cp-pair { grid-column:1 / -1; border:1px solid var(--border); border-radius:var(--radius-md); padding:8px; display:flex; flex-direction:column; gap:8px; min-width:0; }
.cp-pair-h { display:flex; flex-wrap:wrap; align-items:center; gap:6px 10px; }
.cp-pair-name { font:var(--t-body); font-weight:650; color:var(--ink); }
.cp-pair-tag { font:var(--t-meta); color:var(--muted); text-transform:uppercase; letter-spacing:.06em; }
.cp-pair-grid { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:8px; }
.cp-swap { margin-left:auto; width:auto; min-height:36px; padding:4px 14px; border:1px solid var(--accent); border-radius:var(--radius-sm);
  background:var(--surface-1); color:var(--accent); font:inherit; font-weight:700; cursor:pointer; }
.cp-swap:disabled { opacity:.55; cursor:wait; }
.cp-flags { list-style:none; margin:8px 0 0; padding:0; display:grid; gap:6px; }
.cp-flags-pair { margin:0; }
.cp-flag { display:flex; align-items:flex-start; gap:8px; font:var(--t-body); padding:6px 8px; border-radius:var(--radius-sm); border:1px solid var(--border); }
.cp-flag-dot { width:10px; height:10px; border-radius:50%; flex:none; margin-top:6px; background:var(--muted); }
.cp-flag-red { border-color:var(--critical); color:var(--critical-text); }
.cp-flag-red .cp-flag-dot { background:var(--critical); }
.cp-flag-amber { border-color:var(--warning); color:var(--warning-text); }
.cp-flag-amber .cp-flag-dot { background:var(--warning); }
.cp-flag-soft { color:var(--ink-2); }
.cp-prompt { border:1px solid var(--warning); border-radius:var(--radius-sm); padding:8px; display:flex; flex-direction:column; gap:8px; }
.cp-prompt p { margin:0; font:var(--t-body); }
.cp-prompt .cp-reason, .cp-prompt .cp-reason-cancel { width:auto; min-height:40px; padding:6px 12px; border-radius:var(--radius-sm);
  border:1px solid var(--border); background:var(--surface-2); color:var(--ink); font:inherit; font-weight:650; cursor:pointer; text-align:left; }
.cp-prompt .cp-reason[data-reason="eqt_high"] { border-color:var(--warning); }
.cp-reason-other { display:flex; flex-direction:row; gap:6px; align-items:stretch; }
.cp-reason-other .cp-reason-text { flex:1 1 auto; min-width:0; width:auto; min-height:40px; font-size:16px; padding:6px 8px;
  border:1px solid var(--border); border-radius:var(--radius-sm); background:var(--surface-1); color:var(--ink); }
.cp-info { font:var(--t-body); color:var(--ink-2); margin:8px 0 0; padding:6px 8px; border-left:3px solid var(--border); }
.cp-hist-tag { font:var(--t-meta); color:var(--ink-2); border:1px solid var(--border); border-radius:10px; padding:0 6px; }

/* Gantt: equipment left ON from before midnight, and a lane with no change
   today saying what state the asset was left in (26 Sep 2026). */
.gantt-bar.eq-on.carried { border-left-style: dashed; }
.gantt-eq-note { position: absolute; left: 6px; top: 50%; transform: translateY(-50%);
  font: var(--t-meta); color: var(--muted); white-space: nowrap; pointer-events: none; }

/* ------------------------------------------------------- coverage timeline
   Continuous, wrapped time (26 Sep 2026): one row per 24 h (12 h on a phone),
   shifts as bars across row breaks, gaps as hatched red bands. Tokens used are
   all defined: --accent, --accent-subtle, --critical, --critical-text, --border,
   --muted, --ink, --ink-2, --surface-1, --track, --warning. */
.cov-controls { display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin-top:10px; }
.cov-seg { display:inline-flex; border:1px solid var(--border); border-radius:var(--radius-sm); overflow:hidden; }
.cov-seg button { font:var(--t-meta); padding:6px 12px; border:0; background:var(--surface-1); color:var(--ink-2); cursor:pointer; }
.cov-seg button + button { border-left:1px solid var(--border); }
.cov-seg button.is-active { background:var(--accent); color:#fff; }
.cov-sum { font:var(--t-body); color:var(--ink-2); margin:10px 0 0; }
.cov { margin-top:10px; display:grid; gap:14px; }
.cov-row-h { font:var(--t-meta); color:var(--muted); margin-bottom:3px; }
.cov-track { position:relative; background:var(--surface-1); border:1px solid var(--border); border-radius:6px; overflow:hidden; }
.cov-axis { position:relative; height:16px; }
.cov-tick { position:absolute; top:2px; transform:translateX(-50%); font:500 10px/1 var(--mono); color:var(--muted); white-space:nowrap; }
.cov-tick.first { transform:none; } .cov-tick.last { transform:translateX(-100%); }
.cov-bar { position:absolute; height:24px; display:flex; align-items:center; gap:2px; padding:0 4px; min-width:4px;
  background:var(--accent-subtle); border:1px solid var(--accent); border-radius:4px; color:var(--ink);
  font:600 11px/1 var(--sans, inherit); overflow:hidden; white-space:nowrap; cursor:pointer; box-sizing:border-box; }
.cov-bar.open { border-style:solid; border-right:3px solid var(--accent); }
.cov-bar.auto { border-style:dashed; background:repeating-linear-gradient(135deg, var(--accent-subtle) 0 4px, var(--surface-1) 4px 8px); }
.cov-bar.sel { outline:2px solid var(--ink); outline-offset:1px; }
.cov-bar-t { overflow:hidden; text-overflow:ellipsis; }
.cov-cont { color:var(--muted); font-size:10px; }
.cov-gap { position:absolute; top:0; bottom:0; background:repeating-linear-gradient(135deg,
  color-mix(in srgb, var(--critical) 28%, transparent) 0 5px, transparent 5px 10px); }
.cov-gap.open { box-shadow:inset -2px 0 0 var(--critical); }
.cov-day { position:absolute; top:0; bottom:0; border-left:1px dashed var(--muted); }
.cov-day span { position:absolute; top:1px; left:3px; font:500 10px/1 var(--mono); color:var(--muted); white-space:nowrap; background:var(--surface-1); }
.cov-now { position:absolute; top:0; bottom:0; border-left:2px solid var(--critical); }
.cov-now span { position:absolute; bottom:1px; left:3px; font:600 10px/1 var(--mono); color:var(--critical-text); background:var(--surface-1); }
.cov-lists { margin-top:14px; }
.cov-ul { list-style:none; margin:6px 0 0; padding:0; display:grid; gap:6px; }
.cov-li { display:flex; flex-wrap:wrap; align-items:flex-start; gap:4px 10px; width:100%; text-align:left; background:none;
  border:0; border-bottom:1px solid var(--border); padding:6px 2px; color:inherit; font:var(--t-body); cursor:pointer; }
.cov-li-t { display:inline-flex; flex-wrap:wrap; align-items:flex-start; gap:4px 6px; }
.cov-gap-li { display:flex; flex-wrap:wrap; align-items:flex-start; gap:4px 8px; font:var(--t-body); }
.cov-detail { margin-top:12px; }
/* Exec summary: one line, the last 24 h. */
.cov-strip { margin-top:8px; width:100%; max-width:420px; }
.cov-s-track { position:relative; height:14px; background:var(--surface-1); border:1px solid var(--border); border-radius:4px; overflow:hidden; }
.cov-s-bar { position:absolute; top:2px; bottom:2px; background:var(--accent); border-radius:2px; opacity:.85; }
.cov-s-bar.auto { opacity:.35; }
.cov-s-gap { position:absolute; top:0; bottom:0; background:color-mix(in srgb, var(--critical) 55%, transparent); }
.cov-s-now { position:absolute; top:0; bottom:0; border-left:2px solid var(--critical); }
.cov-s-axis { position:relative; height:13px; }
.cov-s-tick { position:absolute; top:1px; transform:translateX(-50%); font:500 9px/1 var(--mono); color:var(--muted); white-space:nowrap; }
.cov-strip-note { font:var(--t-meta); color:var(--ink-2); margin:2px 0 0; max-width:420px; }
.cov-unv { position:absolute; top:0; bottom:0; background:repeating-linear-gradient(135deg,
  color-mix(in srgb, var(--warning) 35%, transparent) 0 5px, transparent 5px 10px); }
.cov-s-unv { position:absolute; top:0; bottom:0; background:color-mix(in srgb, var(--warning) 45%, transparent); }

/* Trends > Equipment runtime: PLC (solid) and Operator (outlined) lanes. */
.rt-legend { font:var(--t-meta); color:var(--ink-2); display:flex; flex-wrap:wrap; align-items:center; gap:4px 10px; margin:6px 0 0; }
.rt-key { display:inline-block; width:18px; height:10px; border-radius:2px; vertical-align:middle; margin-right:3px; }
.rt-key.plc  { background:var(--good); }
.rt-key.op   { border:2px solid var(--accent); background:transparent; }
.rt-key.mm   { background:repeating-linear-gradient(135deg, var(--warning) 0 3px, transparent 3px 6px); border:1px solid var(--warning); }
.rt-key.trip { width:3px; background:var(--critical); }
.rt-axis-wrap { margin:10px 0 0 76px; }
.rt-axis { position:relative; height:14px; }
.rt-tick { position:absolute; transform:translateX(-50%); font:500 10px/1 var(--mono); color:var(--muted); white-space:nowrap; }
.rt-asset { margin-top:10px; }
.rt-name { display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 10px; font:var(--t-body); }
.rt-lane { display:flex; align-items:center; gap:6px; margin-top:3px; }
.rt-lane-l { flex:none; width:70px; font:var(--t-meta); color:var(--muted); }
.rt-track { position:relative; flex:1; min-width:0; height:18px; background:var(--surface-1); border:1px solid var(--border); border-radius:4px; overflow:hidden; }
.rt-band { position:absolute; top:2px; bottom:2px; border-radius:2px; min-width:2px; }
.rt-band.plc { background:var(--good); }
.rt-band.op  { border:2px solid var(--accent); background:var(--accent-subtle); box-sizing:border-box; }
.rt-mm   { position:absolute; top:0; bottom:0; background:repeating-linear-gradient(135deg, color-mix(in srgb, var(--warning) 60%, transparent) 0 3px, transparent 3px 6px); }
.rt-trip { position:absolute; top:0; bottom:0; width:2px; background:var(--critical); }
.rt-chg  { position:absolute; top:1px; transform:translateX(2px); font:600 9px/14px var(--mono); color:var(--ink-2); white-space:nowrap; pointer-events:auto; }
@media (max-width: 640px) { .rt-axis-wrap { margin-left:0; } .rt-lane-l { width:54px; } }
.cp-note { font:var(--t-meta); color:var(--warning-text); overflow-wrap:anywhere; }
/* In-app selfie (assets/selfie.js), both apps: front camera, mirrored, ✕ always. */
.selfie-sheet { position:fixed; inset:0; z-index:1000; background:rgba(0,0,0,.72); display:flex; align-items:center; justify-content:center; padding:16px; }
.selfie-card { position:relative; width:100%; max-width:420px; background:var(--surface-1); color:var(--ink); border-radius:14px; padding:14px; display:flex; flex-direction:column; gap:10px; }
.selfie-x { position:absolute; top:6px; right:6px; width:44px; height:44px; min-height:0; padding:0; border:0; border-radius:50%;
  background:var(--surface-2); color:var(--ink); font-size:22px; line-height:44px; cursor:pointer; z-index:2; }
.selfie-h { margin:4px 48px 0 2px; font-weight:650; }
.selfie-video, .selfie-still { width:100%; max-height:60vh; object-fit:cover; border-radius:10px; background:#000; }
.selfie-video { transform:scaleX(-1); }
.selfie-btns { display:flex; flex-direction:column; gap:8px; }
.selfie-btns .btn { width:100%; }
.selfie-sheet.selfie-closing { background:transparent; }
.tasks-filter.month-nav { align-items: center; }

/* Sign-in cards: turn on notifications / precise location (28 Sep 2026). */
.en-card { border: 1px solid var(--border); border-left: 4px solid var(--accent); border-radius: var(--radius-md);
  background: var(--surface-1); padding: 14px 16px; margin: 0 0 14px; }
.en-card h3 { font: var(--t-heading); margin: 0 0 6px; color: var(--ink); }
.en-card p { font: var(--t-body); color: var(--ink-2); margin: 0 0 8px; }
.en-card .en-why, .en-card .en-steps { font: var(--t-body); color: var(--ink-2); margin: 0 0 10px; padding-left: 20px; }
.en-card .en-why li, .en-card .en-steps li { margin: 0 0 4px; }
.en-card .en-warn { color: var(--warning-text); font-weight: 600; }
.en-card .en-msg:empty { display: none; }
.en-card .en-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }

/* HR Sites (28 Sep 2026): attendance Gantt and contact cards. */
.hrs-filter { display: grid; gap: 4px; font-size: 12px; color: var(--muted); margin: 10px 0; max-width: 320px; }
.hrs-gantt { margin: 12px 0 16px; }
.hrs-axis { position: relative; height: 16px; margin-left: 120px; font: var(--t-meta); color: var(--muted); }
.hrs-axis span { position: absolute; transform: translateX(-50%); white-space: nowrap; }
.hrs-lane { display: flex; align-items: center; gap: 8px; margin: 4px 0; }
.hrs-who { flex: 0 0 112px; font: var(--t-meta); color: var(--ink); overflow-wrap: anywhere; }
.hrs-track { position: relative; flex: 1; height: 14px; background: var(--surface-2); border-radius: var(--radius-sm); }
.hrs-bar { position: absolute; top: 0; bottom: 0; background: var(--accent); border-radius: var(--radius-sm); }
.hrs-bar.open { background: var(--good); }
.hrs-bar.auto { background: var(--warning); }
.hrs-contacts { display: grid; gap: 8px; margin: 6px 0 12px; }
.hrs-contact { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  border: 1px solid var(--border); border-radius: var(--radius-md); padding: 8px 12px; }

/* Training (dashboard) and Team Training (28 Sep 2026). */
.tr-dash-list { display: grid; gap: 10px; margin: 12px 0; }
.tr-dash-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.tr-dash-lang { display: flex; align-items: center; gap: 6px; font: var(--t-meta); color: var(--muted); }
.tr-dash-video { width: 100%; max-width: 820px; border-radius: var(--radius-md); background: #000; }
.tt-resp { margin: 6px 0 12px; padding-left: 20px; font: var(--t-body); color: var(--ink-2); }
