:root {
  --navy: #1B2A4A; --navy-dk: #141F38; --copper: #B87333; --copper-lt: #E8D6C2;
  --paper: #F5F2EC; --ink: #232A3B; --warm: #D8D0C5; --good: #2E7D4F;
  --amber: #C77D1E; --red: #B3452E; --gray: #8B8578;
  /* The weather-day fill. Warm enough to sit in this palette, yellow enough to
     be unmistakable next to the weekend cream (#FBF8F2) it sits beside. */
  --wx-lt: #FCF1D2;
  /* The schedule bars (RJ, 24 Sep 2026): blue = to do, bright yellow = waiting
     on Watay or sent back, green = report in. Their own tokens rather than
     --navy / --amber / --good, so re-colouring the bars never re-colours the
     rest of the app. */
  --bar-todo: #2F6FD6; --bar-wait: #FFD21F; --bar-done: #22A04B;
  --mono: ui-monospace, Menlo, Consolas, monospace;
}
* { box-sizing: border-box; margin: 0; }
html, body { height: 100%; }
body {
  font-family: Arial, Helvetica, sans-serif; background: #E9E4DA; color: var(--ink);
  -webkit-font-smoothing: antialiased; overscroll-behavior-y: none;
}

/* The site photo --------------------------------------------------------------
   One image, used in three places: behind the sign-in hero, behind the hub
   header, and around the shell on a wide screen. It is in the service worker's
   SHELL list, so it is there on a phone with no signal - which is also why it
   is 218 KB and not the 7 MB original. Every use stacks a veil over it. Half
   this photograph is bright sky, and white text on bright sky is unreadable;
   the veil is what makes the type legible rather than a matter of luck.

   Deliberately NOT applied to the daily record, stations or docs. Those are
   working screens, often read one-handed in full sun, and a photograph behind
   a number is a legibility problem, not a decoration. */
/* The sign-in screen: photo behind the whole page ----------------------------
   A fixed element rather than `background-attachment: fixed` on the shell. The
   crew list is sixteen names and scrolls on a phone, so the photo has to stay
   put while the content moves - and iOS Safari has long-standing bugs pairing
   fixed attachment with `cover`, which is not something to discover on a
   satellite link in the bush. A plain fixed div behaves everywhere. */
.bgphoto {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background-color: var(--navy-dk);
  background-image:
    /* Kept deliberately light. The first attempt ran .86/.70/.90 and the page
       read as flat navy - sixteen crew rows already cover most of a phone
       screen, so the veil has to do the least work that still keeps the type
       readable, not the most. */
    linear-gradient(180deg, rgba(17, 26, 46, .74) 0%, rgba(17, 26, 46, .40) 42%,
                    rgba(17, 26, 46, .68) 100%),
    url("/static/img/site-bg.jpg");
  background-size: cover, cover;
  background-position: center 58%, center 58%;
  background-repeat: no-repeat, no-repeat;
}
.shell.signin { background: transparent; box-shadow: none; }
.shell.signin > *:not(.bgphoto) { position: relative; z-index: 1; }
/* The hero no longer carries its own photo - the whole page is the photo now,
   and stacking one crop on another just draws a visible seam. */
.shell.signin .hero { background: none; padding: 44px 22px 26px; }
.shell.signin .lbl { color: var(--copper-lt); }
.shell.signin .muted, .shell.signin .small { color: #C9D2E2; }
.shell.signin .tit { color: #fff; }

/* Names and keypad become dark glass. White cards on a photograph is the
   obvious move and the wrong one: it turns the list into a wall of bright
   rectangles and loses the picture entirely. */
.shell.signin .rowbtn, .shell.signin .pad3 button {
  background: rgba(14, 22, 40, .50);
  border-color: rgba(255, 255, 255, .22);
  color: #fff;
  /* Light blur on purpose. At blur(9px) each row became an opaque smear and
     the photograph only survived in the gaps between them - on a phone, where
     sixteen rows ARE the screen, that is the whole picture gone. 3px still
     settles the busy gravel behind the names without erasing what is there. */
  -webkit-backdrop-filter: blur(3px) saturate(118%);
  backdrop-filter: blur(3px) saturate(118%);
}
.shell.signin .rowbtn .tit, .shell.signin .rowbtn .muted { color: #fff; }
.shell.signin input {
  background: rgba(14, 22, 40, .55); border-color: rgba(255, 255, 255, .28);
  color: #fff; font-size: 16px; padding: 12px;
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}
/* 16px on the input above is not a style choice: iOS Safari zooms the whole
   page in on focus for anything smaller, and a crew member who taps the email
   field should not end up scrolled sideways on a sign-in screen. */
.shell.signin input::placeholder { color: rgba(255, 255, 255, .45); }
.shell.signin .pad3 button { color: #fff; }
.shell.signin .pad3 button:active { background: rgba(184, 115, 51, .78); }
.shell.signin .pins i { background: rgba(255, 255, 255, .28); }
.shell.signin .pins i.f { background: var(--copper); }
.shell.signin .note {
  background: rgba(16, 25, 44, .68); border-color: rgba(255, 255, 255, .18);
  color: #E8EDF5;
}

.hero, .hdr.on-photo {
  background-color: var(--navy);
  background-image:
    /* Heaviest where the type sits and clearing downward. The obvious way round
       is wrong: the title is at the top, and so is the bright sky. A veil that
       fades upward puts white text on the brightest part of the photograph. */
    linear-gradient(180deg, rgba(20, 31, 56, .90) 0%, rgba(20, 31, 56, .62) 100%),
    url("/static/img/site-bg.jpg");
  background-size: cover, cover;
  /* 72%, not centre. These are short wide bands, so `cover` shows a narrow
     horizontal slice - and centred on a 4:3 photo whose top half is sky, that
     slice is sky. 72% lands on the tower line, the transformer and the yard,
     which is the half worth showing. */
  background-position: center 72%, center 72%;
  background-repeat: no-repeat, no-repeat;
}
/* Taller than the old flat navy block: this is the front door, and the photo
   needs vertical room to read as a substation rather than a dark smudge. */
.hero { padding: 52px 22px 38px; }
.hero .ttl { color: #fff; font-size: 30px; font-weight: 700; line-height: 1.1;
  text-shadow: 0 1px 12px rgba(10, 16, 30, .55); }
.hero .sub { color: var(--copper-lt); font-size: 13px; margin-top: 8px;
  text-shadow: 0 1px 10px rgba(10, 16, 30, .5); }

/* On a wide screen the 480px shell leaves margins. Fill them with the same
   photo rather than flat putty. Phone-width screens are covered by the shell
   entirely, so this is desktop-only - and `fixed` attachment is skipped there
   too, because it forces a repaint on every scroll on mobile for no gain. */
@media (min-width: 700px) {
  body {
    background-color: #0F1319;
    background-image:
      linear-gradient(rgba(15, 19, 25, .74), rgba(15, 19, 25, .74)),
      url("/static/img/site-bg.jpg");
    background-size: cover, cover;
    background-position: center, center;
    background-repeat: no-repeat, no-repeat;
    background-attachment: fixed, fixed;
  }
}
#root { min-height: 100%; }
.shell { max-width: 480px; margin: 0 auto; min-height: 100vh; min-height: 100dvh;
  background: var(--paper); display: flex; flex-direction: column;
  box-shadow: 0 0 24px rgba(20,31,56,.18); position: relative; }
.content { flex: 1; overflow-y: auto; padding-bottom: 76px; }

/* connectivity strip */
.strip { position: sticky; top: 0; z-index: 40; width: 100%; border: 0;
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
  padding: 6px 14px; color: #fff; font: 700 11px/1.2 var(--mono);
  letter-spacing: .05em; cursor: pointer; }
.strip.online { background: var(--good); }
.strip.offline { background: var(--amber); }
.strip.syncing { background: var(--amber); }
.strip .dot { width: 8px; height: 8px; border-radius: 50%; background: #fff;
  display: inline-block; margin-right: 8px; }
.strip.offline .dot { animation: pulse 1.2s infinite; }
.strip.syncing .dot { animation: pulse .5s infinite; }
@keyframes pulse { 50% { opacity: .25; } }
.strip .hint { font-weight: 400; opacity: .85; text-transform: none; }

/* header */
.hdr { background: var(--navy); padding: 14px 16px 12px; display: flex;
  flex-wrap: wrap; justify-content: space-between; align-items: flex-start; }
.hdr h1 { color: #fff; font-size: 19px; line-height: 1.15; }
.hdr .sub { color: var(--copper-lt); font-size: 11px; margin-top: 2px; }
.hdr .who { text-align: right; }
.hdr .who .nm { color: #fff; font-weight: 700; font-size: 14px; }
.hdr .who .links { margin-top: 3px; display: flex; gap: 10px; justify-content: flex-end;
  align-items: center; }
.hdr .who button { background: none; border: 0; color: var(--copper-lt);
  font-size: 11px; text-decoration: underline; cursor: pointer; padding: 0; }

/* Log out. RJ, 12 Sep: "red in color with a faint neon red color. i want it to
   look the same on everypage your on".

   The palette's --red (#B3452E) is a muted brick meant for red text on paper -
   on navy it goes flat and slightly brown, which is the opposite of neon. This
   is its bright cousin, and it is used nowhere else in the app, so red in the
   header means exactly one thing.

   Neon is the glow, not the hue: a halo outside the pill, a matching one inside
   it, and the text lit the same way. Kept low-alpha because RJ asked for faint
   and because a log out button that shouts gets pressed by accident.

   The tinted background is what makes it identical on every page. Behind the
   hub header is a photograph under a dark veil, and a border-and-glow alone
   picks up whatever is behind it; a solid-ish ground of its own does not. */
.hdr .who button.out {
  color: #FF6B6B; text-decoration: none; font-weight: 700; letter-spacing: .04em;
  padding: 3px 10px; border-radius: 99px;
  border: 1px solid rgba(255, 107, 107, .55);
  background: rgba(255, 77, 77, .12);
  text-shadow: 0 0 6px rgba(255, 77, 77, .55);
  box-shadow: 0 0 7px rgba(255, 77, 77, .30), inset 0 0 7px rgba(255, 77, 77, .12);
  transition: box-shadow .15s, background .15s, color .15s;
}
.hdr .who button.out:hover {
  color: #fff; border-color: #FF6B6B; background: rgba(255, 77, 77, .30);
  box-shadow: 0 0 13px rgba(255, 77, 77, .55), inset 0 0 9px rgba(255, 77, 77, .22);
}
.hdr .who button.out:focus-visible { outline: 2px solid #FF6B6B; outline-offset: 2px; }

/* today's date in the header --------------------------------------------------
   Phone first: the header has no spare width beside the title and the user block,
   so the date takes its own full-width row underneath, on one line. From 700px up
   there is room to centre it in the gap between them, stacked. */
.hdr .today { order: 3; flex-basis: 100%; display: flex; gap: 8px;
  align-items: baseline; margin-top: 8px; padding-top: 6px;
  border-top: 1px solid rgba(255, 255, 255, .13); }
.hdr .today b { color: #fff; font-size: 13px; font-weight: 700; }
.hdr .today span { color: var(--copper-lt); font: 12px var(--mono); letter-spacing: .02em; }
@media (min-width: 700px) {
  .hdr .today { order: 0; flex-basis: auto; display: block; text-align: center;
    margin-top: 0; padding-top: 1px; border-top: 0; }
  .hdr .today b { display: block; font-size: 14px; }
  .hdr .today span { display: block; margin-top: 2px; }
}

/* nav */
.nav { position: absolute; bottom: 0; left: 0; right: 0; display: flex;
  background: #fff; border-top: 1px solid var(--warm); z-index: 40; }
/* Icons over labels, and a filled highlight on the tab you are on (RJ, 15 Sep).
   Each tab stacks a currentColor icon over its label; inactive tabs are muted
   grey, the active one turns copper with a copper-tint fill (var(--copper-lt))
   and keeps the copper top bar - so the current tab reads at a glance on a phone
   in a switchyard, not just a thin line.

   Seven tabs for an admin still have to fit one row: the label font steps
   10px -> 9.5px on the narrowest phones, white-space:nowrap stops a too-wide
   label wrapping into a second row (it shows as overflow instead, which the test
   catches), and test_activity_tab.py holds that one-row-at-360 invariant - it
   had to prove the icons did not break it either. The icon adds height, not
   width, and the vertical padding drops from 12px to 7px to keep the bar about
   as tall as it was (.content clears it at padding-bottom:76px). */
.nav button { flex: 1; min-width: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: flex-end; gap: 3px;
  padding: 7px 0 max(7px, env(safe-area-inset-bottom));
  border: 0; background: none; font: 700 10px Arial; white-space: nowrap;
  color: var(--gray); border-top: 3px solid transparent; cursor: pointer; }
.nav button .ic { width: 22px; height: 22px; flex: 0 0 auto; }
.nav button .lb { line-height: 1; }
@media (max-width: 400px) { .nav button { font-size: 9.5px; letter-spacing: -.2px; }
  .nav button .ic { width: 20px; height: 20px; } }
/* Eight tabs from 17 Sep (the dashboard), which is 45px of label each at 360 and
   40px at 320. One more step down keeps the longest labels inside their own
   button instead of colliding with the next one - measured, not guessed. */
@media (max-width: 380px) { .nav button { font-size: 9px; letter-spacing: -.3px; } }
/* A tab whose name is too long for a narrow phone carries both spellings and CSS
   picks one: "Dashboard" where it fits, "Dash" where it does not. Better than
   clipping the word, and better than calling it "Dash" on a tablet. */
.nav button .lb .narrow { display: none; }
@media (max-width: 400px) {
  .nav button .lb .wide { display: none; }
  .nav button .lb .narrow { display: inline; } }
.nav button.on { color: var(--copper); border-top-color: var(--copper);
  background: var(--copper-lt); }

/* atoms */
.pad { padding: 16px; }
.lbl { font-size: 11px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--copper); margin: 0 0 6px; }
.card { background: #fff; border: 1px solid var(--warm); border-radius: 8px;
  padding: 12px; }
.chip { display: inline-block; padding: 2px 9px; border-radius: 99px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .02em; white-space: nowrap; }
.chip.good { background: var(--good); color: #fff; }
.chip.amber { background: var(--amber); color: #fff; }
.chip.red { background: var(--red); color: #fff; }
.chip.copper { background: var(--copper); color: #fff; }
.chip.quiet { background: #E4E9F2; color: var(--navy); }
/* The schedule bar colours as chips in the day sheet. Navy on the bright
   yellow - white on it is unreadable. */
.chip.bardone { background: var(--bar-done); color: #fff; }
.chip.barwait { background: var(--bar-wait); color: var(--navy); }
/* Same trick as the nav labels: a chip whose full wording does not fit a narrow
   phone carries both spellings and CSS picks one. "RETURNED - NEEDS ATTENTION"
   beside a kind chip is 25 characters; "RETURNED" in the same red still says
   the thing that matters. */
.chip .narrow { display: none; }
@media (max-width: 430px) {
  .chip .wide { display: none; }
  .chip .narrow { display: inline; } }
.btn { display: block; width: 100%; border: 0; border-radius: 8px;
  padding: 13px; font: 700 15px Arial; color: #fff; background: var(--copper);
  cursor: pointer; }
.btn.navy { background: var(--navy); }
.btn.line { background: none; border: 1.5px dashed var(--copper);
  color: var(--copper); padding: 9px; font-size: 13px; }
.btn:disabled { opacity: .4; cursor: default; }
.btn:focus-visible, .nav button:focus-visible, .strip:focus-visible,
.rowbtn:focus-visible { outline: 3px solid var(--copper); outline-offset: 1px; }
input, select, textarea { width: 100%; border: 1px solid var(--warm);
  border-radius: 6px; padding: 9px 10px; font: 14px Arial; color: var(--ink);
  background: #fff; }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--copper); }
.fld { display: block; margin-bottom: 12px; }
.fld > span { display: block; font-size: 12px; font-weight: 700;
  color: var(--navy); margin-bottom: 4px; }
.mono { font-family: var(--mono); }
.muted { color: var(--gray); font-size: 12px; }
.small { font-size: 11px; }

/* tiles */
.tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.tile { background: var(--navy); border-radius: 8px; padding: 10px 6px;
  text-align: center; }
.tile b { display: block; color: #fff; font: 700 20px var(--mono); }
.tile span { color: var(--copper-lt); font-size: 10.5px; }

/* hub widgets --------------------------------------------------------------
   The two group tiles on the front door. Deliberately much bigger than the
   .tile stat blocks above: these are the first thing a gloved hand has to hit
   on a phone, so each one is a full-width target rather than a grid cell. */
.widgets { display: grid; gap: 10px; }
.widget { display: flex; width: 100%; align-items: center; gap: 13px;
  background: #fff; border: 1px solid var(--warm); border-left: 5px solid var(--copper);
  border-radius: 8px; padding: 15px 14px; text-align: left; cursor: pointer;
  font: 14px Arial; color: var(--ink); }
.widget b { display: block; font-size: 17px; color: var(--navy); line-height: 1.2; }
.widget em { display: block; font-style: normal; font-size: 12px; color: var(--gray);
  margin-top: 3px; line-height: 1.4; }
.widget i { display: block; font-style: normal; font: 700 10.5px/1.2 var(--mono);
  letter-spacing: .05em; text-transform: uppercase; color: var(--copper); margin-top: 7px; }
.widget i.warn { color: var(--amber); }
.widget .go { flex: none; color: var(--warm); font-size: 22px; line-height: 1; }
.widget .ic { flex: none; display: flex; align-items: center; justify-content: center;
  width: 48px; height: 48px; border-radius: 8px; background: var(--navy); color: #fff; }
.widget .ic svg { width: 27px; height: 27px; }
.widget:focus-visible { outline: 3px solid var(--copper); outline-offset: 1px; }
/* The line between the service groups and Lines (RJ, 23 Sep). Copper fading
   out at both ends, full width, with room above and below so Lines reads as a
   separate section rather than the next tile in the list. */
/* v1.61.3 - RJ, marking up the laptop again: "can we makes the Lines tab
   lower down on the page. i really want a divided line between Tech service
   & Lines." So the line is heavier and near-solid, and the space around it
   scales with the screen height - Lines sits well down the page on a laptop
   and a little lower on a phone, instead of reading as the next tile. */
.hubdiv { grid-column: 1 / -1; border: 0; height: 3px; margin: 8vh 0 5vh;
  border-radius: 2px; opacity: .9;
  background: linear-gradient(90deg, transparent, var(--copper) 4%,
                              var(--copper) 96%, transparent); }

/* Lines has nothing behind it yet, so it is drawn as reserved rather than
   live - present and tappable, but visibly not the same kind of thing. */
.widget.soon { border-left-color: var(--warm); background: #FBF9F5; }
.widget.soon .ic { background: var(--warm); color: var(--gray); }
.widget.soon b { color: var(--gray); }
.widget.soon i { color: var(--gray); }

.ic.big { display: inline-flex; align-items: center; justify-content: center;
  width: 60px; height: 60px; border-radius: 10px; background: var(--warm);
  color: var(--gray); }
.ic.big svg { width: 34px; height: 34px; }

/* back-to-hub link, sitting above the section name in the header */
.hdr .uplink { background: none; border: 0; padding: 0 0 3px; cursor: pointer;
  color: var(--copper-lt); font: 700 11px Arial; letter-spacing: .02em; }
.hdr .uplink:focus-visible { outline: 2px solid var(--copper); outline-offset: 2px; }

/* The hub and Lines have no bottom nav, so they don't need the gutter that
   keeps the last row of the field app clear of it. */
.content.nonav { padding-bottom: 20px; }

/* list rows */
.rowbtn { display: flex; width: 100%; align-items: center; gap: 12px;
  background: #fff; border: 1px solid var(--warm); border-radius: 8px;
  padding: 10px 12px; text-align: left; cursor: pointer; font: 14px Arial; }
.rowbtn + .rowbtn, .card + .card, .rowbtn + .card, .card + .rowbtn { margin-top: 7px; }
.letter { flex: none; display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 6px; background: var(--navy);
  color: #fff; font: 700 21px var(--mono); }
.letter.done { background: var(--good); }
.grow { flex: 1; min-width: 0; }
.tit { font-weight: 700; font-size: 14px; color: var(--navy); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

/* modal + toast */
.scrim { position: fixed; inset: 0; background: rgba(20,31,56,.55); z-index: 60;
  display: flex; align-items: flex-end; justify-content: center; }
.sheet { width: 100%; max-width: 480px; max-height: 92vh; overflow-y: auto;
  background: var(--paper); border-radius: 14px 14px 0 0; padding: 16px; }
.toast { position: fixed; bottom: 86px; left: 50%; transform: translateX(-50%);
  background: var(--navy-dk); color: #fff; padding: 9px 16px; border-radius: 8px;
  font-size: 13px; z-index: 80; max-width: 90%; text-align: center;
  box-shadow: 0 4px 14px rgba(0,0,0,.3); }

/* pin pad */
.pins { display: flex; gap: 12px; justify-content: center; margin: 14px 0 18px; }
.pins i { width: 12px; height: 12px; border-radius: 50%; background: var(--warm); }
.pins i.f { background: var(--copper); }
.pad3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
  max-width: 260px; margin: 0 auto; }
.pad3 button { padding: 13px 0; border: 1px solid var(--warm); border-radius: 8px;
  background: #fff; font: 700 18px var(--mono); color: var(--navy); cursor: pointer; }
.pad3 button:disabled { opacity: 0; }

.note { background: #EFE9DE; border: 1px solid var(--warm); border-radius: 8px;
  padding: 9px 12px; font-size: 12px; color: #6B6353; }
.warnbar { background: var(--amber); color: #fff; border-radius: 8px;
  padding: 8px 12px; font-size: 12px; font-weight: 700; }

/* Found on site, still to do --------------------------------------------------
   The one block on these two screens that means somebody has to GO BACK - and
   at a station reached by helicopter that is a flight, not a drive. It shipped
   in v1.54.0 as a copper heading over ordinary white cards, which is exactly
   what every other section on the page looks like; RJ's note on 22 Sep was that
   it did not stand out. So it stops being a heading and becomes a panel: a
   solid amber bar, a two-pixel border, and a warm fill that separates it from
   the white cards of the planned work below.

   Amber and not red on purpose. Red on this palette is a failure - OUTAGE, a
   HIGH item, a returned record. Work found on site is none of those; it is a
   trip somebody has to plan, and the two should not read the same from across
   a truck cab. */
.returns { border: 2px solid var(--amber); border-radius: 10px;
  background: var(--wx-lt); overflow: hidden; margin: 14px 0 16px; }
.returns .rhd { display: flex; align-items: center; gap: 8px;
  background: var(--amber); color: #fff; padding: 9px 12px;
  font-size: 12.5px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; }
.returns .rhd .rcount { margin-left: auto; flex-shrink: 0;
  background: rgba(255, 255, 255, .24); border-radius: 99px;
  padding: 1px 10px; font-family: var(--mono); letter-spacing: 0; }
/* Bottom padding is 2px because every ActivityRow carries its own 8px inline
   margin and an inline style cannot be overridden from here. */
.returns .rbody { padding: 10px 12px 2px; }
.returns .rbody.solo { padding-bottom: 11px; }
.returns .rwhy { font-size: 12px; color: #6B5A38; margin: 0 0 9px; }
/* The cards inside pick up a warmer border so they read as part of the panel
   rather than as the same white card that is stacked below it. */
.returns .card { border-color: #E2C892; }

/* Found on site AND fixed. Green, because nobody has to do anything about it -
   it is the record of extra work, not a job. Same shape as the amber panel so
   the two read as one question answered twice: what we found and dealt with,
   what we found and have to go back for. */
.returns.fixedwork { border-color: var(--good); background: #EDF5EF; }
.returns.fixedwork .rhd { background: var(--good); }
.returns.fixedwork .rwhy { color: #4A5B4F; }
.returns.fixedwork .card { border-color: #BCD6C4; }

/* The three counts on a station are the filter, not a readout (RJ, 22 Sep).
   A button, so it is a tap target and reachable from a keyboard, with every
   default a button brings stripped back to what the card already was. */
.statbox { display: block; width: 100%; text-align: center; font: inherit;
  color: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent; }

/* Approvals pipeline folders. Each is a card whose header is the whole tap
   target: a chevron, the stage name over one line saying what it means, and a
   count badge so the stack size reads at a glance without opening it. Copper
   badge when there is work in it, grey when empty - the eye should land on the
   folders that need something. */
.folder { border: 1px solid var(--warm); border-radius: 8px; background: #fff;
  margin-bottom: 10px; overflow: hidden; }
.folderhd { display: flex; align-items: center; gap: 10px; width: 100%;
  text-align: left; padding: 12px; border: 0; background: none; cursor: pointer; }
.folderhd .fchev { flex: 0 0 auto; width: 12px; color: var(--copper);
  font-size: 12px; }
.folderhd .ftit { display: block; font-weight: 700; font-size: 14px;
  color: var(--navy); }
.folderhd .fsub { display: block; font-size: 11.5px; color: var(--gray);
  margin-top: 1px; }
.folderhd .fcount { flex: 0 0 auto; min-width: 24px; text-align: center;
  padding: 3px 9px; border-radius: 99px; background: var(--copper); color: #fff;
  font: 700 13px Arial; }
.folderhd .fcount.zero { background: #E4E9F2; color: var(--navy); }
.folderbody { padding: 0 12px 10px; }
.split { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.seg { display: flex; border: 1px solid var(--warm); border-radius: 6px;
  overflow: hidden; }
.seg button { flex: 1; border: 0; background: #fff; padding: 9px 0;
  font: 700 13px Arial; color: var(--ink); cursor: pointer; }
.seg button.em { background: var(--good); color: #fff; }
.seg button.tm { background: var(--copper); color: #fff; }
.g2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.g3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.addbox { background: #fff; border: 1.5px dashed var(--copper); border-radius: 8px;
  padding: 12px; margin-top: 7px; }

/* A day in Watay's list. A whole card that is one tap, because on a phone in a
   truck the target is the card, not a link inside it. */
.reclink { width: 100%; text-align: left; cursor: pointer; font: inherit;
  align-items: center; gap: 10px; }
.reclink:active { background: var(--paper); }

/* A day they have already signed. Green edge so the list reads at a glance as
   done / not done without having to read either chip. */
.good-card { border-left: 4px solid var(--good); }

/* The Admin button, at the bottom of Today. RJ, 14 Sep: "can we make that Admin
   Button Glow orange? so it stands out".

   It had been a dashed outline in copper, which is the app's quietest button
   style - the same one used for "Edit this record". That was fair when the
   panel behind it held PINs and backups. It now also holds the tracker import
   and account creation, and RJ went looking for it twice and could not see it.

   Same neon technique as the log out button: a halo outside, a matching one
   inside, and the text lit the same way, over a ground of its own so it does
   not borrow whatever is behind it. Orange rather than copper - copper IS the
   page, and a button the colour of everything else is what it was already
   doing. Brighter than log out on purpose: this one is meant to be found, and
   nothing behind it is destructive. */
.btn.line.glow {
  color: #E8892B; border: 1.5px solid rgba(232, 137, 43, .65);
  background: rgba(232, 137, 43, .10);
  font-weight: 700; letter-spacing: .03em;
  text-shadow: 0 0 7px rgba(232, 137, 43, .45);
  box-shadow: 0 0 10px rgba(232, 137, 43, .32),
              inset 0 0 10px rgba(232, 137, 43, .13);
  transition: box-shadow .15s, background .15s, color .15s;
}
.btn.line.glow:hover {
  color: #fff; background: rgba(232, 137, 43, .82);
  border-color: #E8892B;
  text-shadow: none;
  box-shadow: 0 0 16px rgba(232, 137, 43, .60),
              inset 0 0 12px rgba(232, 137, 43, .20);
}
.btn.line.glow:focus-visible { outline: 3px solid #E8892B; outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .btn.line.glow { transition: none; } }

/* The Activity tab's header row. RJ, 21 Sep: "can we make the Rates & Pin and
   Backups into buttons that match each other" - the two halves of the tab were
   small white pills and the Admin sheet was a full-width banner under them,
   which read as three unrelated controls stacked by accident.

   One definition, three states: plain, `on` (the half you are reading, filled
   navy) and `lit` (the Admin sheet, which keeps the orange glow from 14 Sep -
   the whole point of that glow is that it is the one thing on the page RJ goes
   looking for). Same height, same radius, same type in all three.

   It wraps rather than scrolls. The padding and the gap are set where they are
   so all three fit one row down to 360px - every phone in the crew's hands -
   and below that the Admin pill drops to its own line and is still the same
   pill, which is a better answer than a row that runs off the edge. */
.tabrow { display: flex; flex-wrap: wrap; gap: 6px; }
.tabrow button {
  border: 1px solid var(--warm); border-radius: 99px; padding: 8px 12px;
  font: 700 12.5px Arial; letter-spacing: .02em; white-space: nowrap;
  background: #fff; color: var(--navy); cursor: pointer;
  transition: box-shadow .15s, background .15s, color .15s;
}
.tabrow button.on { background: var(--navy); border-color: var(--navy); color: #fff; }
.tabrow button.lit {
  color: #E8892B; border-color: rgba(232, 137, 43, .65);
  background: rgba(232, 137, 43, .10); letter-spacing: .03em;
  text-shadow: 0 0 7px rgba(232, 137, 43, .45);
  box-shadow: 0 0 10px rgba(232, 137, 43, .32),
              inset 0 0 10px rgba(232, 137, 43, .13);
}
.tabrow button.lit:hover {
  color: #fff; background: rgba(232, 137, 43, .82); border-color: #E8892B;
  text-shadow: none;
  box-shadow: 0 0 16px rgba(232, 137, 43, .60),
              inset 0 0 12px rgba(232, 137, 43, .20);
}
.tabrow button:focus-visible { outline: 3px solid var(--copper); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .tabrow button { transition: none; } }

/* A new account's starting PIN. Big, spaced and monospaced because it gets
   read down a phone line to somebody in a truck, and 6 and 8 at 13px do not
   survive that. Selectable so it can be copied rather than transcribed. */
.pinout { font-size: 30px; font-weight: 700; letter-spacing: .18em;
  color: var(--navy); text-align: center; margin: 10px 0 6px;
  padding: 8px; background: var(--paper); border-radius: 8px;
  user-select: all; -webkit-user-select: all; }

/* Expense cost. The input takes the room and the running amount sits beside it,
   so a supervisor typing a charter rate watches the line total appear rather
   than finding out what it came to on the sheet Watay signs. */
.costrow { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.costrow input { flex: 1 1 120px; min-width: 0; }
/* The label may wrap; the amount never may, and it keeps the right-hand end.
   Without this the rate-card line ran "$365.00 per day - off the rate card
   $1,0" and clipped the total off a 414px screen - the one number on the row
   worth reading. */
.costrow > span:first-child { flex: 1 1 60%; min-width: 0; }
.costrow > b { white-space: nowrap; margin-left: auto; }
.costrow .mono, .costrow b { font: 13px var(--mono); color: var(--navy); }

/* "no cost yet" - a pass-through nobody has priced. Amber rather than red: it
   is not an error, it is an empty money column on a document somebody signs,
   and it should read as a thing still to do. Deliberately NOT $0.00, which
   would say the charter was free. */
.nocost { color: var(--amber); font-weight: 700; font-size: 11px;
  letter-spacing: .03em; text-transform: uppercase; margin-left: 8px;
  white-space: nowrap; }
hr.sep { border: 0; border-top: 1px solid var(--warm); margin: 14px 0; }
@media (prefers-reduced-motion: reduce) {
  .strip .dot { animation: none !important; }
}

/* ---------- wide screens ----------------------------------------------
   The app is phone-first and stays that way: none of this applies below
   900px. On a laptop a 480px column stranded in the middle of a 1400px
   screen wastes exactly the space where someone reviews a station, so the
   shell widens and long lists flow into columns.

   Sign-in and the first-run PIN screen deliberately stay narrow - a keypad
   stretched across a monitor reads as broken, not spacious. */
@media (min-width: 900px) {
  .shell { max-width: 1120px; }
  .shell.narrow { max-width: 480px; }

  /* The schedule calendar wants the 1120px too, and no more - seven columns
     stretched across 1800px is a lot of white space per day. */

  /* Short rows - stations, documents. Fills the width as far as it can. */
  .cols { display: grid; gap: 10px; align-items: start;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
  .cols > .rowbtn { margin: 0; }

  /* Text-heavy rows - activity write-ups. The wider minimum keeps line
     length readable instead of running descriptions across the whole screen. */
  .cols.text { grid-template-columns: repeat(auto-fill, minmax(460px, 1fr)); }
  .cols.text > .card { margin-bottom: 0 !important; }

  /* Tech Service and Inventory side by side; below the line, Lines across
     the full width (RJ, 23 Sep). */
  .widgets { grid-template-columns: repeat(3, 1fr); }
  .widget { align-items: flex-start; }
  .widget.soon { grid-column: 1 / -1; }
  .hubdiv { margin: 22vh 0 16vh; }

  .hdr { padding: 16px 22px 14px; }
  .pad { padding: 20px 22px; }
  .nav button { padding: 14px 0; }

  /* Activity trail. The roll-up of who's been in sits beside the log instead
     of pushing it a screen down. Below 900px these are plain blocks and stack
     in the order they're written, which is the phone layout. */
  .audit { display: grid; grid-template-columns: 340px minmax(0, 1fr);
    gap: 20px; align-items: start; }
  .audit .side { position: sticky; top: 12px; }
}

/* the kind of work a service record covers -----------------------------------
   Four big targets rather than a dropdown: this is picked once, at the top,
   with gloves on, and it decides what the rest of the form means. */
.kinds { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px;
  margin-bottom: 10px; }
.kind { text-align: left; padding: 10px 12px; border-radius: 8px; cursor: pointer;
  background: #fff; border: 1px solid var(--warm); }
.kind b { display: block; font-size: 15px; color: var(--navy); line-height: 1.2; }
.kind em { display: block; font-style: normal; font-size: 11px; color: var(--gray);
  margin-top: 2px; line-height: 1.25; }
.kind.on { background: var(--navy); border-color: var(--navy); }
.kind.on b { color: #fff; }
.kind.on em { color: var(--copper-lt); }
.kind:focus-visible { outline: 3px solid var(--copper); outline-offset: 1px; }

/* A card that is also a button. Resets the button chrome so the card inside
   looks identical to a non-tappable one - the only difference should be that
   it responds. */
.plaincard { display: block; width: 100%; padding: 0; border: 0; background: none;
  text-align: left; cursor: pointer; }
.plaincard + .plaincard { margin-top: 7px; }
.plaincard:focus-visible { outline: 3px solid var(--copper); outline-offset: 2px;
  border-radius: 8px; }

/* The way into the approval queue. Amber down the left edge because it is the
   same "something is waiting on you" as the AWAITING APPROVAL chip, and a
   manager should be able to spot it on Today without reading anything. */
.queuebtn { border-left: 4px solid var(--amber); margin-bottom: 14px; }

/* The head of a full-screen service record. The station is the biggest thing
   on it: approving is deciding about a place, and "B - Pickle Lake TS" is what
   the person doing it is holding in their head, not a record id. */
.rechead { margin-bottom: 12px; }
.rechead .split { margin-bottom: 8px; }
.recstation { display: block; color: var(--navy); font-size: 19px; line-height: 1.25; }
/* Approve sits below the whole record, so it cannot be tapped without the
   hours having gone past. */
.recacts { margin-top: 18px; padding-top: 14px; border-top: 2px solid var(--warm); }

/* The schedule calendar -------------------------------------------------------
   Weeks as rows, days as cells - a calendar, because that is what it is.

   The cells show what is on a day at a glance; the typing happens in the day
   sheet. A month cell is about 90px wide on a laptop and 50px on a phone,
   which is enough to READ "G - Sachigo Lake TS" and nowhere near enough to
   type it. */
.schbar { display: flex; justify-content: space-between; align-items: center;
  gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.schspan, .schpick { display: flex; gap: 6px; align-items: center; }
.schbar .chip { cursor: pointer; border: 0; font-family: inherit; font-size: 12px; }
/* The weather-day button. Every other control in this bar is quiet grey, so
   the amber makes it the only colour in the row - but amber alone was not enough
   (RJ, 12 & 15 Sep: "make it stand out so the techs can see it"), so it is now a
   full button rather than a chip: a weather icon, bigger bold text, and a slow
   amber halo that pulses so a crew that has just been grounded finds it without
   hunting through the bar. Reduced-motion turns the pulse off. Its own class,
   not .chip.amber, which is a status pill elsewhere. */
.schbar .wxbtn { display: inline-flex; align-items: center; gap: 6px;
  background: var(--amber); color: #fff; font-weight: 700; font-size: 13px;
  padding: 8px 15px; border-radius: 99px;
  box-shadow: 0 0 0 3px rgba(199, 125, 30, .22), 0 2px 5px rgba(27, 42, 74, .3);
  animation: wxhalo 2.4s ease-in-out infinite; }
.schbar .wxbtn .wxic { width: 17px; height: 17px; flex: 0 0 auto; }
.schbar .wxbtn:hover { background: #A96C15; }
@keyframes wxhalo {
  0%, 100% { box-shadow: 0 0 0 3px rgba(199, 125, 30, .22), 0 2px 5px rgba(27, 42, 74, .3); }
  50% { box-shadow: 0 0 0 7px rgba(199, 125, 30, .08), 0 2px 5px rgba(27, 42, 74, .3); }
}
@media (prefers-reduced-motion: reduce) { .schbar .wxbtn { animation: none; } }
/* Month and year sit together and read as one control. Auto width so
   "September" is not clipped and "May" does not leave a hole. */
.schpick select { width: auto; padding: 5px 8px; font-size: 13px; font-weight: 700;
  color: var(--navy); }

.cal { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 1px; background: var(--warm); border: 1px solid var(--warm);
  border-radius: 10px; overflow: hidden; }

.caldow { background: var(--navy); color: #fff; text-align: center;
  padding: 6px 2px; font-size: 11px; font-weight: 700; letter-spacing: .05em; }
.caldow.we { background: var(--navy-dk); }

.calcell { background: #fff; border: 0; text-align: left; font-family: inherit;
  padding: 4px; min-height: 78px; cursor: pointer; display: flex;
  flex-direction: column; gap: 2px; align-items: stretch; overflow: hidden; }
.calcell.we { background: #FBF8F2; }
/* A day nobody could fly, filled. After .we on purpose - a weathered-out
   Saturday is a weather day first. Hover still wins on specificity
   (.calcell:hover:not(.empty) is three classes to this rule's two), so the
   cell still lights copper under the cursor. */
.calcell.wx { background: var(--wx-lt); }
.calcell.empty { background: var(--paper); cursor: default; }
/* Today is the copper bar, not a fill - a filled cell reads as "something is
   on this day", which is a different fact. */
.calcell.now { box-shadow: inset 0 3px 0 var(--copper); }
.calcell:hover:not(.empty) { background: var(--copper-lt); }
.calcell:focus-visible { outline: 3px solid var(--copper); outline-offset: -3px; }

.calnum { display: flex; justify-content: space-between; align-items: baseline;
  font-family: var(--mono); font-size: 12px; font-weight: 700; color: var(--navy); }
.calnum em { font-style: normal; font-size: 9px; font-weight: 700; color: var(--copper); }

.calitem { display: block; border-left: 3px solid var(--bar-todo); padding: 1px 0 1px 4px;
  font-size: 11px; line-height: 1.25; color: var(--ink); overflow: hidden; }
.calitem b { color: var(--navy); }
.calitem em { display: block; font-style: normal; color: var(--gray); }
.calitem.wx { border-left-color: var(--amber); font-weight: 700; color: var(--navy); }
/* How far the line's report has got (v1.65.0). Only the bar changes colour -
   the text stays as it was, so a month of green still reads as a month. */
.calitem.ok { border-left-color: var(--bar-done); }
.calitem.wait { border-left-color: var(--bar-wait); }

/* The key under the calendar: three short bars, the same width as the real
   ones, so the legend looks like what it explains. */
.calkey { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-bottom: 6px; }
.calkey span { display: inline-flex; align-items: center; gap: 5px; }
.calkey i { display: inline-block; flex: none; width: 4px; height: 14px;
  border-radius: 1px; background: var(--bar-todo); }
.calkey i.ok { background: var(--bar-done); }
.calkey i.wait { background: var(--bar-wait); }

/* Faint until the day is hovered. A grid full of plus signs reads as a form to
   fill in rather than a month to look at. */
.caladd { align-self: flex-start; margin-top: auto; color: var(--gray);
  font-size: 12px; opacity: .3; }
.calcell:hover .caladd { opacity: 1; color: var(--copper); }

/* The line being edited in the day sheet, so it is obvious which one the form
   below is about. */
.card.on { border-color: var(--copper); box-shadow: inset 3px 0 0 var(--copper); }

@media (max-width: 560px) {
  .calcell { min-height: 62px; padding: 3px; }
  .calitem { font-size: 10px; border-left-width: 2px; padding-left: 3px; }
  /* The task line goes on a phone. The station is what you are looking for at
     this size; the rest is one tap away in the day sheet.
     Except on a line with no station, where the second line is the only thing
     telling two of them apart - 14 October is "Mobilize north" for both crews
     and without it reads as the same thing written twice. */
  .calitem:not(.noloc) em { display: none; }
}


.linkbtn { border: 0; background: none; padding: 0; margin-top: 4px; cursor: pointer;
  color: var(--copper); font-family: inherit; font-size: 11px; font-weight: 700;
  text-decoration: underline; }

/* ---------- photos of station work (v1.58.0) ---------- */
.photobtns { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin: 4px 0 0; }
.photobtns .btn { padding: 9px; font-size: 13px; }
.thumbs { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 8px; }
.thumb { position: relative; width: 72px; height: 72px; border-radius: 6px; overflow: hidden;
  border: 1px solid var(--warm); background: #EEF1F6; padding: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center; }
.thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.thumb .thumbx { position: absolute; top: 2px; right: 2px; width: 22px; height: 22px;
  border: 0; border-radius: 99px; background: rgba(20, 31, 56, .8); color: #fff;
  font-size: 12px; line-height: 22px; padding: 0; cursor: pointer; }
.camsheet { position: fixed; inset: 0; z-index: 70; background: #000;
  display: flex; flex-direction: column; }
.camsheet video { flex: 1; width: 100%; min-height: 0; object-fit: contain; background: #000; }
.camwait { position: absolute; top: 45%; left: 0; right: 0; text-align: center; color: #fff; }
.cambar { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: 12px; padding: 14px 16px calc(14px + env(safe-area-inset-bottom)); background: var(--navy-dk); }
.cambar .btn { width: auto; justify-self: start; padding: 10px 22px; }
.shutter { width: 68px; height: 68px; border-radius: 99px; border: 5px solid #fff;
  background: var(--copper); cursor: pointer; }
.shutter:disabled { opacity: .4; cursor: default; }
.lightbox { position: fixed; inset: 0; z-index: 70; background: rgba(0, 0, 0, .88);
  display: flex; align-items: center; justify-content: center; padding: 12px; cursor: zoom-out; }
.lightbox img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 4px; }
.thumb .lostph { color: var(--red); font-weight: 700; text-align: center; line-height: 1.2; }
/* Labour priced on the record (v1.59.0) */
.rowamt { font-size: 15px; font-weight: 700; color: var(--navy); margin-top: 2px; }
.labtotal { color: var(--navy); }
/* The grand labour total under the last labour row (v1.59.1) */
.labfoot { display: flex; justify-content: flex-end; align-items: baseline;
  flex-wrap: wrap; gap: 4px 14px; margin: 2px 0 0; padding: 10px 14px;
  border-radius: 8px; background: var(--navy); color: #fff; }
.labfoot-lbl { font-size: 11px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--copper-lt); margin-right: auto; }
.labfoot-hrs { font-size: 13px; color: var(--copper-lt); }
.labgrand { font-size: 19px; color: #fff; }
.labfoot-short { flex-basis: 100%; text-align: right; font-size: 11px;
  font-weight: 700; color: #F2C14E; }
/* Labour priced on the entry form (v1.59.2) */
.formamt { color: var(--navy); font-size: 13px; }
/* Expense prices and the record's grand total (v1.59.3) */
.exprow .rowamt { font-size: 14px; }
.mkup { display: inline-block; margin-left: 4px; padding: 1px 6px; border-radius: 99px;
  background: var(--copper-lt); color: #7A4A1E; font-size: 10px; font-weight: 700;
  letter-spacing: .02em; vertical-align: 1px; }
.grand { background: #fff; border: 2px solid var(--navy); border-radius: 10px; overflow: hidden; }
.grand-line { display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; padding: 8px 14px; border-bottom: 1px solid var(--warm);
  font-size: 13px; color: var(--navy); }
.grand-line .mono { font-weight: 700; white-space: nowrap; }
.grand-line.mk { color: #8A5A2B; background: #FBF5EE; }
.grand-total { display: flex; justify-content: space-between; align-items: baseline;
  padding: 12px 14px; background: var(--navy); color: #fff; }
.grand-lbl { font-size: 11px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--copper-lt); }
.grand-amt { font-size: 22px; color: #fff; }
.grand-short { padding: 7px 14px; background: #FFF6E0; color: #8A5A00;
  font-size: 12px; font-weight: 700; text-align: right; }

/* inventory (23 Sep) -----------------------------------------------------------
   Two stores, side by side at the top: RJ's "#1 Thunder Bay & #2 Red Lake".
   Each is a big tap target that also says, before you open it, whether
   anything there needs reordering. */
.invlocs { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 12px; }
.invloc { position: relative; text-align: left; cursor: pointer; font: 14px Arial;
  background: #fff; border: 1px solid var(--warm); border-radius: 8px;
  padding: 11px 12px 10px; color: var(--ink); }
.invloc .num { position: absolute; top: 9px; right: 10px; font: 700 11px var(--mono);
  color: var(--gray); }
.invloc b { display: block; font-size: 16px; color: var(--navy); padding-right: 22px; }
.invloc em { display: block; font-style: normal; font-size: 12px; color: var(--gray); margin-top: 2px; }
.invloc i { display: block; font-style: normal; font: 700 10.5px/1.2 var(--mono);
  letter-spacing: .05em; text-transform: uppercase; color: var(--good); margin-top: 6px; }
.invloc i.warn { color: var(--amber); }
.invloc.on { background: var(--navy); border-color: var(--navy); }
.invloc.on b, .invloc.on .num { color: #fff; }
.invloc.on em { color: var(--copper-lt); }
.invloc.on i { color: #9FD3B2; }
.invloc.on i.warn { color: #F2B866; }
.invloc:focus-visible { outline: 3px solid var(--copper); outline-offset: 1px; }
.invbar { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; }
.invbar input { flex: 1; min-width: 0; }
.invbar .chip { border: 0; cursor: pointer; padding: 7px 11px; font-size: 11.5px; }
.invcard { align-items: stretch; border-left: 4px solid var(--good); }
.invcard.low { border-left-color: var(--amber); }
.invcard.out { border-left-color: var(--red); }
.invcard.over { border-left-color: var(--navy); }
.invlvl { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.invtrack { position: relative; flex: 0 0 90px; height: 6px; border-radius: 3px;
  background: #ECE6DC; overflow: visible; }
.invfill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 3px;
  background: var(--good); }
.invfill.low { background: var(--amber); }
.invfill.out { background: var(--red); }
.invfill.over { background: var(--navy); }
.invmin { position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px;
  background: var(--ink); opacity: .55; }
.invqty { flex: none; display: flex; flex-direction: column; align-items: flex-end;
  justify-content: center; gap: 2px; min-width: 64px; text-align: right; }
.invqty b { font-size: 22px; color: var(--navy); line-height: 1; }
.invstats { margin-top: 12px; }
.invmove { display: flex; gap: 10px; align-items: flex-start; padding: 8px 0;
  border-top: 1px solid var(--warm); font-size: 13px; }
.invchg { flex: none; min-width: 48px; font-weight: 700; }
.invchg.neg { color: var(--red); }
.invchg.pos { color: var(--good); }

/* Part photos (23 Sep) - a small square on each card, and the photo-or-reason
   block on Add a part, amber-edged until one of the two is given. */
.invthumb { position: relative; flex: none; width: 52px; height: 52px; border-radius: 6px;
  overflow: hidden; background: #ECE6DC; display: flex; align-items: center;
  justify-content: center; align-self: center; }
.invthumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.invthumb.none { font-size: 18px; opacity: .45; filter: grayscale(1); }
.invthumb i { position: absolute; right: 2px; bottom: 2px; font: 700 10px var(--mono);
  font-style: normal; background: rgba(20,31,56,.8); color: #fff; border-radius: 4px;
  padding: 0 4px; }
.invphoto { border: 1px solid var(--warm); border-radius: 8px; padding: 10px;
  margin-bottom: 10px; background: #FBF9F5; }
.invphoto.need { border-color: var(--amber); }
.invreasons { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.invreasons .chip { border: 0; cursor: pointer; padding: 6px 10px; font-size: 11px; }

/* Site directory (23 Sep) ---------------------------------------------------- */
.sitelist .rowbtn + .rowbtn { margin-top: 7px; }
.sitecard { align-items: center; }
.sitechips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 4px; }
.sitechips .chip { border: 0; }
button.chip { cursor: pointer; }
.sitehead { display: flex; gap: 12px; align-items: center; }
.sitehead b { display: block; font-size: 19px; color: var(--navy); }
.eqcard { align-items: center; }
.eqcard + .eqcard { margin-top: 7px; }
.eqfacts { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 12px; margin: 12px 0; }
.eqfacts div span { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--gray); }
.eqfacts div b { font-size: 14px; color: var(--ink); font-weight: 600; word-break: break-word; }
.siteinfo { margin-bottom: 10px; }
.siteinfo.hz { border-left: 4px solid var(--amber); }
.inforow { padding: 6px 0; border-top: 1px solid #EFE9DE; }
.inforow:first-of-type { border-top: 0; }
.inforow span { display: block; font-size: 11px; font-weight: 700; color: var(--navy); margin-bottom: 2px; }
.inforow b { font-weight: 400; white-space: pre-wrap; font-size: 14px; }
.inforow a { color: var(--copper); font-weight: 700; }

/* A device's history and the links between Site directory and Tech Service
   (23 Sep, v1.64.0) ------------------------------------------------------- */
.eqhist { margin: 4px 0 12px; padding-top: 10px; border-top: 1px solid #EFE9DE; }
.histrow { align-items: flex-start; padding: 9px 10px; }
.histrow + .histrow { margin-top: 6px; }
.histrow .tit { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden; }
.histrow .go { align-self: center; }
.hdot { flex: none; width: 10px; height: 10px; border-radius: 50%; margin-top: 5px;
  background: var(--copper); }
.hdot.high { background: var(--red); }
.hdot.done { background: var(--good); }
.hdot.off { background: var(--gray); }
.sitelink { margin: -4px 0 12px; border-left: 4px solid var(--copper); }
.sitelink b { color: var(--navy); font-size: 14px; }
.focusflash { border-color: var(--copper); box-shadow: 0 0 0 2px var(--copper);
  animation: focusflash 1.6s ease-out 2; }
@keyframes focusflash { 0% { background: #FCEBD7; } 100% { background: #fff; } }
@media (prefers-reduced-motion: reduce) { .focusflash { animation: none; } }
