/* MK0 house style.
   Values taken from MK0 BRAND/DASHBOARD-GUIDE.md and TOP-BAR-SPEC.md, which are
   canonical. Where the two disagree, the top-bar spec wins. */

:root {
  --pink: #FD4196;        /* Social Booth pink — the only accent */
  --pink-soft: #FFE3F0;   /* chip and badge backgrounds */
  --pink-mid: #FFB8D9;    /* borders on pink chips */
  --pink-deep: #9D1D5C;   /* text on a pink chip */
  /* The one blue. It already existed as the literal on `button.blue` (the
     Send Booth Cost button) and the electric-blue bills banner; named here so
     a chip can be blue without another literal. Michael, 30 Sep 2026, on the
     Heartbeat's paper chips: "highlight 'Rural' as blue and 'SB' as Pink". */
  --blue: #2563EB;
  --blue-soft: #DCEAFF;   /* chip and badge backgrounds */
  --blue-deep: #1E4FB8;   /* text on a blue chip */
  --ink: #1A1A2E;         /* all body text */
  --tile: #14141F;        /* dark tiles / app icon */
  --grey: #6B7280;        /* secondary text, table headers */
  --grey-light: #9CA3AF;  /* hints, disabled */
  --line: #E5E7EB;        /* every hairline and border */
  --bg: #F7F7F9;          /* page background */
  --card: #FFFFFF;        /* cards, tables, header */
  /* Raised: table headers, week banners, menus, ghost buttons.
     This existed only under data-crew="tech" until 18 Aug 2026, which is why
     the tech timesheet had white bands across a plum page — the grid was
     painting #FCFCFD literals that no theme could reach. Defined here, every
     crew gets its own. */
  --card-2: #FCFCFD;
  --warn: #B45309;
  --warn-bg: #FEF3C7;
  --ok: #0E9F6E;
  --bad: #DC2626;
  --bad-bg: #FEF2F2;      /* the wash behind a row that needs fixing */
}

* { box-sizing: border-box; }

/* Any author rule that sets `display` beats the browser's own
   [hidden] { display: none }, so `.field { display: block }` was enough to
   make every hidden element visible. Anything toggled with .hidden in JS
   depends on this line. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 15px;
  line-height: 1.5;
}

h1, h2, h3 { font-weight: 600; margin: 0; }

/* ---- header ------------------------------------------------------------ */

header {
  background: var(--card);
  border-bottom: 1px solid var(--line);
  padding: 14px 26px;
  display: flex; align-items: center; gap: 16px;
  position: sticky; top: 0; z-index: 20;
}
/* Never let the brand or the right-hand controls be squeezed by the switcher. */
header .brand, header .whoami, header > button { flex: 0 0 auto; }
header .brand div { white-space: nowrap; }
header .brand { display: flex; align-items: center; gap: 11px; }
header .brand b { font-size: 17px; font-weight: 600; letter-spacing: -0.2px; }
header .brand span { color: var(--grey); font-size: 12px; }
header .spacer { flex: 1; }
.whoami { font-size: 13px; color: var(--grey); }

/* ---- the Guide button and its panel (MK0 8.0) ----------------------------
   Drawn by api.js addGuideLink straight after the header spacer. It says
   flex: 0 0 auto itself, whatever its parent, or the switcher would be
   squeezed to make room (tests/test_booths_header.py). Michael, 17 Aug 2026:
   "make sure that in the navigation bar NOTHING changes size" -- it is drawn
   on every screen for the same person, so it never does. Compact on a phone
   so the first header row does not wrap. Tokens only: it must read on the
   tech crew's plum as well.
   The same font, padding and border as the switcher pills (.appswitch a,
   .navtop) and, like them, the body's line-height through `font: inherit`,
   so it is exactly their height. It used to force line-height 1.2 and sat
   4-6px shorter than every control beside it (1 Oct 2026). The row's height
   is set by the page's ghost small buttons, which are taller still, so this
   never makes the header taller either. */
header .guidelink {
  flex: 0 0 auto; white-space: nowrap;
  font-size: 13px; font-weight: 500;
  padding: 4px 11px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--card-2); color: var(--ink);
}
header .guidelink:hover { border-color: var(--pink); }
/* Hidden by fitGuide() (api.js) where its width would cost the header a row. */
header .guidelink.gl-off { display: none; }
@media (max-width: 700px) {
  header .guidelink {
    min-width: 40px; min-height: 40px; padding: 0 12px;
    display: inline-flex; align-items: center; justify-content: center;
  }
  header .guidelink .gl-word { display: none; }
  header .guidelink::before { content: "?"; font-size: 15px; }
}

/* The guide's own text. Shared by the Guide panel (every page) and the
   Academy's main column, because both show the same server-rendered html:
   h3/h4 with ac-h- ids, p, lists, code, one quote, tables, two callouts and
   links. Every text/background pair is two tokens from the same theme, so the
   tech crew's inverting tokens keep it readable. */
.ac-body { max-width: 72ch; font-size: 15px; line-height: 1.6; color: var(--ink); }
.ac-body h3, .ac-body h4 { scroll-margin-top: 80px; }
.ac-body h3 { font-size: 18px; margin: 24px 0 8px; }
.ac-body h4 { font-size: 15px; font-weight: 600; margin: 18px 0 6px; }
.ac-body > h3:first-child, .ac-body > h4:first-child { margin-top: 0; }
.ac-body p { margin: 0 0 12px; }
.ac-body ul, .ac-body ol { margin: 0 0 12px; padding-left: 22px; }
.ac-body li { margin: 3px 0; }
.ac-body li ul { margin: 4px 0 0; }
.ac-body code {
  background: var(--card-2); color: var(--ink);
  border: 1px solid var(--line); border-radius: 5px;
  padding: 1px 5px; font-size: 13px;
}
.ac-body blockquote {
  margin: 0 0 12px; padding: 8px 14px;
  border-left: 3px solid var(--pink-mid); color: var(--grey);
}
.ac-body blockquote p { margin: 0; }
.ac-body a { color: var(--pink-deep); }
/* A table scrolls inside itself, never the page: a three-column labels table
   would otherwise push a 375px screen sideways. */
.ac-body table {
  display: block; overflow-x: auto; border-collapse: collapse;
  width: auto; max-width: 100%; margin: 0 0 14px;
}
.ac-body th, .ac-body td {
  border: 1px solid var(--line); padding: 7px 10px;
  text-align: left; vertical-align: top;
}
/* thead th is nowrap app-wide (the data tables); a guide's headings wrap. */
.ac-body thead th {
  white-space: normal; background: var(--card-2); color: var(--grey);
}
.ac-callout {
  border-radius: 8px; padding: 10px 12px; margin: 0 0 12px; color: var(--ink);
}
.ac-callout.ac-tip { background: var(--pink-soft); border-left: 3px solid var(--pink); }
.ac-callout.ac-careful { background: var(--warn-bg); border-left: 3px solid var(--warn); color: var(--ink); }

.guidepanel .panel-body { display: flex; flex-direction: column; gap: 10px; }
.guidepanel .panel-body > p { margin: 0; }
.guidepanel .ac-body { max-width: none; }
.guidepanel .muted, .ticketpanel .muted, .tk-mine-item .muted { color: var(--grey); }
.gp-related-head {
  margin: 8px 0 4px; font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.6px; color: var(--grey);
}
.gp-related { margin: 0; padding-left: 20px; font-size: 14px; }
.gp-related a { color: var(--pink-deep); }

/* Lodge a ticket (ticket-form.js). Above the guide panel (55), below the
   toast (80), so a warning about the form is never painted under it. */
#ticketpanel { z-index: 57; }
.ticketpanel .panel-body { display: flex; flex-direction: column; gap: 12px; }
.ticketpanel .tk-field { display: block; margin: 0; }
.ticketpanel .tk-label {
  display: block; margin-bottom: 5px;
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.6px; color: var(--grey);
}
.ticketpanel .tk-field > p { margin: 0 0 6px; font-size: 13px; }
.ticketpanel select, .ticketpanel textarea, .ticketpanel input[type=file] {
  width: 100%;
}
.ticketpanel textarea { resize: vertical; }
.ticketpanel input[type=file] { padding: 7px 9px; }
.tk-progress { margin-right: auto; align-self: center; font-size: 13px; }
.tk-files { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.tk-file {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  background: var(--card-2); border: 1px solid var(--line); color: var(--ink);
  border-radius: 999px; padding: 3px 4px 3px 11px; font-size: 12.5px;
}
.tk-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.tk-file .tk-file-x {
  background: transparent; color: var(--grey); border: 1px solid var(--line);
  border-radius: 999px; padding: 0 7px; font-size: 13px; line-height: 1.5;
}
.tk-link { margin: 0; font-size: 13px; }
.tk-link code { word-break: break-all; color: var(--ink); }
.tk-done { display: flex; flex-direction: column; gap: 10px; padding: 6px 0; }
.tk-done h3 { font-size: 17px; }
.tk-done p { margin: 0; }
.tk-done a { color: var(--pink-deep); }
.tk-mine-head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 8px;
}
.tk-mine-item { padding: 12px 0; border-top: 1px solid var(--line); }
.tk-mine-list > .tk-mine-item:first-child { border-top: none; }
.tk-mine-top { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.tk-mine-meta { font-size: 12.5px; margin-top: 3px; }
.tk-mine-details { margin: 6px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; }
@media (max-width: 700px) {
  /* Under 16px iOS zooms the whole page when a box is tapped. */
  .ticketpanel select, .ticketpanel textarea, .ticketpanel input[type=file] {
    font-size: 16px;
  }
}

/* "<Page> isn't one of your pages." -- its dismiss, a small ghost ×. By id,
   so it outranks button.ghost further down and keeps the note's own colour. */
#deniednote .denied-x {
  background: transparent; color: inherit; border: 1px solid transparent;
  padding: 3px 8px; line-height: 1;
}
#deniednote .denied-x:hover { border-color: currentColor; filter: none; }

/* A phone: both panels fill the screen from its very top. On the pages that
   draw under the status bar (the Activator and Hustle, viewport-fit=cover,
   as the installed app), a flat 16px put the title and the ×, the one way
   out of the Guide panel on a phone, under the clock. The same inset the
   Activator's own header takes; 0 everywhere else. */
@media (max-width: 1024px) {
  .guidepanel .panel-head, .ticketpanel .panel-head {
    padding-top: calc(16px + env(safe-area-inset-top, 0px));
  }
}

/* The tech crew's plum. .panel-close:hover (below) paints a light grey under
   var(--ink), and --ink is near-white on plum: the × vanished while hovered.
   8.0 puts the × on every tech screen (the Guide panel, the ticket form, the
   Academy's library), so it says its own pair here, as button.ghost does. */
body[data-crew="tech"] .panel-close:hover { background: var(--card-2); color: var(--ink); }

/* ---- The page stripe -------------------------------------------------
   Michael, 13 Sep 2026, looking at the Build template screen: "Can you add a
   header for this page with a pink stripe across it for 'booth builder'".

   The global <header> above is deliberately quiet -- white, one hairline --
   because it carries the app switcher and has to sit on thirty-seven screens
   without shouting. The cost of that is a page that never says what it IS: you
   land on it and the first thing you read is body copy. This is the band that
   says it.

   A component, not a one-off, so the next page built inherits it instead of
   inventing a different one.

   ON THE COLOURS. --pink on white is 3.30:1. That clears WCAG AA for LARGE
   text (3:1) and fails it for normal text (4.5:1), so the page name is 19px/700
   -- above the 18.66px bold threshold where "large" starts -- and anything
   smaller goes in a white pill instead of sitting on the pink, where
   --pink-deep gives 7.6:1. Do not shrink the h1 below 19px or drop its weight
   without moving it off the pink.
   -------------------------------------------------------------------- */
.pagestripe {
  background: var(--pink);
  padding: 13px 26px;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}
.pagestripe h1 {
  /* 19px/700 is the accessibility floor here, not a taste choice -- see above. */
  margin: 0; font-size: 19px; font-weight: 700;
  letter-spacing: -0.3px; color: #fff;
}
/* Small text never sits directly on the pink. */
.pagestripe .where {
  background: #fff; color: var(--pink-deep);
  font-size: 12.5px; font-weight: 600;
  padding: 3px 10px; border-radius: 999px;
}
.pagestripe .stripe-side { margin-left: auto; display: flex; gap: 8px; }
@media (max-width: 700px) {
  .pagestripe { padding: 11px 16px; gap: 9px; }
  .pagestripe h1 { font-size: 19px; }   /* held, for the contrast reason above */
  .pagestripe .stripe-side { margin-left: 0; width: 100%; }
}

.badge {
  width: 15px; height: 15px; border-radius: 50%;
  border: 3px solid var(--pink); flex: none;
}

.appswitch {
  display: flex; align-items: center; gap: 6px;
  margin-left: 10px; padding-left: 14px;
  border-left: 1px solid var(--line);
  /* This used to scroll horizontally, because fourteen flat pills did not fit
     on a laptop. Grouping them into five headings removed that need — and the
     scroll container had to go with it, because `overflow` establishes a
     clipping context: the dropdowns rendered correctly and were then clipped
     out of view by their own parent, which looks exactly like a menu that does
     not open.
     Wraps instead, on the rare narrow window where five still will not fit. */
  flex-wrap: wrap; row-gap: 6px; min-width: 0;
}
.appswitch a, .appswitch .current { white-space: nowrap; }
/* One rule for both states, so a link and the current page are the same box.
   Only the colours below differ. */
.appswitch a, .appswitch .current, .navtop {
  font-size: 13px; font-weight: 500; text-decoration: none;
  padding: 4px 11px; border-radius: 999px; white-space: nowrap;
  border: 1px solid var(--line);
}
.appswitch a { color: var(--grey); background: #FCFCFD; }
.appswitch a:hover { color: var(--ink); background: #F3F4F6; border-color: #D1D5DB; }
.appswitch .current {
  color: var(--pink-deep); background: var(--pink-soft);
  border-color: var(--pink-mid); cursor: default;
}

/* ---- grouped switcher --------------------------------------------------
   Five headings fit where fourteen pills did not, so nothing lives behind a
   scroll edge any more. The group you are IN is pink and names the screen you
   are on, because "which drawer" and "which screen" are different questions
   and people are asking the second one. */

.navgroup { position: relative; }

.navtop {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px; font-weight: 500;
  padding: 4px 11px; border-radius: 999px; white-space: nowrap;
  border: 1px solid var(--line); background: #FCFCFD; color: var(--grey);
  cursor: pointer;
}
.navtop:hover { color: var(--ink); background: #F3F4F6; border-color: #D1D5DB; }

/* Where you are. Colour ONLY — never weight, never extra words.
 *
 * Michael, 17 Aug 2026: "make sure that in the navigation bar NOTHING changes
 * size." It did, twice over: the active pill went to font-weight 600, and it
 * also grew a "· Maintenance" suffix naming the screen. So the bar reflowed
 * every time you moved, and on a narrow window a pill could push the whole row
 * onto a second line just by being the one you were on. A navigation bar that
 * moves when you use it is one you stop trusting your aim on.
 *
 * The weight is now identical in both states and the suffix is gone. Which
 * screen you are on is still said twice — by the page's own heading, and by the
 * `.on` row inside the open menu — so nothing was lost but the reflow. */
.navgroup.on .navtop {
  color: var(--pink-deep); background: var(--pink-soft);
  border-color: var(--pink-mid);
}
.navwhere { display: none; }

.navcaret { font-size: 9px; opacity: 0.55; }
.navgroup.open .navcaret { opacity: 1; }

.navmenu {
  display: none; position: absolute; top: calc(100% + 6px); left: 0;
  min-width: 190px; z-index: 60;
  background: var(--card); border: 1px solid var(--line);
  border-radius: 10px; padding: 5px;
  box-shadow: 0 12px 28px rgba(17, 17, 26, 0.13);
}
.navgroup.open .navmenu { display: block; }

.navmenu a {
  display: block; padding: 7px 10px; border-radius: 7px;
  font-size: 13px; text-decoration: none; color: var(--ink);
  border: none; background: none; white-space: nowrap;
}
.navmenu a:hover { background: #F3F4F6; }
.navmenu a.on {
  color: var(--pink-deep); background: var(--pink-soft); font-weight: 600;
}

/* On a phone the menu spans the screen under the header instead of hanging
   off its own button. Anchored to the button's right edge it fixed the LAST
   group and broke the FIRST: Accounting's menu opened off the left edge with
   "eceipts / able / hours" showing (Drew, 28 Aug 2026: "The drop downs don't
   fit in the screen and get cut off when on iPhone"). With the group made
   static, the menu positions against the header, and the header is as wide
   as the phone. */
@media (max-width: 620px) {
  /* header is already position: sticky, which is positioned, so the menu
     anchors to it without touching that. */
  .navgroup { position: static; }
  .navmenu { left: 10px; right: 10px; top: calc(100% + 2px); min-width: 0; }
  .navmenu a { white-space: normal; }
}

/* ---- layout ------------------------------------------------------------ */

main { max-width: 1500px; margin: 0 auto; padding: 22px 26px; }

.card {
  background: var(--card); border: 1px solid var(--line);
  border-radius: 12px; overflow: hidden;
}

.strip {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px; margin-bottom: 18px;
}
.tile {
  background: var(--card); border: 1px solid var(--line);
  border-radius: 12px; padding: 12px 14px;
}
.tile .label {
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.6px; color: var(--grey);
}
.tile .value { font-size: 21px; font-weight: 600; font-variant-numeric: tabular-nums; }
.tile.accent .value { color: var(--pink); }

/* A count that should be zero and is not. Only applied when the number is
   non-zero, so a cleared list stops shouting rather than staying amber forever
   and being learned as decoration. */
.tile.warn { border-color: var(--warn); background: #FFFBEB; }
.tile.warn .value { color: #92400E; }

/* ---- controls ---------------------------------------------------------- */

button, .btn {
  font: inherit; font-size: 13px; font-weight: 500;
  border-radius: 10px; padding: 8px 14px; cursor: pointer;
  border: 1px solid transparent; background: var(--pink); color: #fff;
}
button:hover, .btn:hover { filter: brightness(0.96); }
button:disabled { opacity: 0.5; cursor: default; filter: none; }
/* A link dressed as a button (the Guide panel's Open in the Academy, the
   Academy's Open this page, the Heartbeat's Refunds): no underline, and a
   box of its own, so its padding never overlaps the lines around it and it
   reads as the button beside it rather than as a link. */
.btn { text-decoration: none; display: inline-block; }
button.ghost, .btn.ghost {
  background: transparent; border-color: var(--line); color: var(--ink);
}
button.ghost:hover { background: #F3F4F6; }
button.danger { background: var(--bad); }
/* The booth-cost send. BLUE on purpose — Michael, 20 Aug 2026: "can we have a
   specific 'Send Booth Cost' that is blue" — so it cannot be mistaken for the
   pink Xero send sitting beside it. Two money buttons the same colour is how
   somebody presses the wrong one at 5pm. */
button.blue, .btn.blue { background: #2563EB; color: #fff; }
button.small, .btn.small { padding: 5px 10px; border-radius: 8px; font-size: 13px; }

/* A money button and the sentence under it that says where the money goes. */
.send-stack { display: inline-flex; flex-direction: column; gap: 3px;
              align-items: stretch; vertical-align: top; }
.send-stack .sub {
  font-size: 10.5px; color: var(--grey); line-height: 1.3; text-align: center;
  max-width: 170px;
}

/* The maintenance-or-asset choice. Chips rather than a dropdown so both
   answers are on screen at once — a closed dropdown showing one value is how
   the OTHER value stops being remembered to exist.

   Two shapes, one wiring. The JOB choice (.kind-row) is a pair of equal
   option cards; the CENTRE choice (.centre-row) is a row of small pills.
   The first cut gave everything a 999px pill radius, and the moment
   "Asset cost → MK116 build job" wrapped in the narrow pane the pill
   became a lopsided egg — Michael, 21 Aug 2026: "It's messy as fuck."
   .kind-stack exists because the pane's .row > .control is a flex ROW:
   without one wrapper, the cards, their help line and the centre pills
   rendered as three squeezed side-by-side columns. */
.kind-stack { flex: 1; min-width: 0; }
.kind-row { display: flex; gap: 8px; }
.kind-chip {
  border: 1px solid var(--line); cursor: pointer;
  background: var(--card); color: var(--ink); line-height: 1.35;
}
.kind-row > .kind-chip {
  flex: 1 1 0; min-width: 0; border-radius: 12px; padding: 9px 12px;
  font-size: 13px; text-align: left;
}
.kind-row > .kind-chip b { font-weight: 600; display: block; }
.kind-row > .kind-chip .tag {
  display: block; color: var(--grey); font-size: 11.5px; margin-top: 1px;
}
.centre-row { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }
.centre-row > .kind-chip {
  border-radius: 8px; padding: 5px 11px; font-size: 12.5px;
  white-space: nowrap;
}
.centre-row .tag { color: var(--grey); font-size: 10.5px; }
.kind-chip.on {
  border-color: #2563EB; background: #EFF6FF; color: #1D4ED8;
}
.kind-chip.on .tag { color: #3B82F6; }
.kind-help { font-size: 11.5px; color: var(--grey); margin-top: 5px; }

input, select, textarea {
  font: inherit; font-size: 15px; color: var(--ink);
  border: 1px solid var(--line); border-radius: 10px;
  padding: 9px 12px; background: var(--card); width: 100%;
}
input:focus, select:focus { outline: 2px solid var(--pink-mid); outline-offset: 1px; }

/* A tick box with its words beside it, inside a dense grid cell.
 *
 * Michael, 7 Sep 2026: "Can you give me, spencer and harry a box to tick if we
 * are doing tech support on a Friday or Saturday night." The rule above is
 * app-wide — `input { width: 100% }` — so a tick box has to say otherwise or it
 * renders as a full-width bar with its label pushed off the cell. Here rather
 * than in timesheet.css because the rule it is undoing is here. */
label.tick {
  display: flex; align-items: center; justify-content: center; gap: 5px;
  margin: 5px auto 0; font-size: 10.5px; font-weight: 600;
  letter-spacing: 0; text-transform: none; color: var(--grey);
  white-space: nowrap; cursor: pointer;
}
label.tick input[type=checkbox] { width: auto; margin: 0; }

/* `.field`, not `label.field`.
 *
 * A field holding ONE control is a <label>. A field holding a GROUP of them --
 * the platform tick boxes, the twelve permission tick boxes -- must be a <div>,
 * because a <label> labels exactly one control: the first labelable element
 * inside it. Wrapping a group in a label silently made its heading a switch for
 * whichever box happened to be first, so clicking the word "Platform" toggled
 * `warehouse` and clicking "Extra permissions" toggled `manage_users` -- a
 * permission change with no click on a permission. Matching on the class alone
 * means both spellings style identically and the markup is free to be honest
 * about which one it is. */
.field { display: block; margin-bottom: 12px; }
.field > span {
  display: block; font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.6px; color: var(--grey); margin-bottom: 5px;
}

.chip {
  display: inline-block; font-size: 12px; font-weight: 500;
  border-radius: 999px; padding: 3px 10px;
  background: #F3F4F6; color: var(--grey);
}
.chip.pink { background: var(--pink-soft); color: var(--pink-deep); border: 1px solid var(--pink-mid); }
.chip.warn { background: var(--warn-bg); color: var(--warn); }
.chip.ok { background: #E7F6F0; color: var(--ok); }
.chip.bad { background: #FEF2F2; color: var(--bad); }

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

table { width: 100%; border-collapse: collapse; }
thead th {
  background: #FCFCFD; border-bottom: 1px solid var(--line);
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.6px; color: var(--grey);
  text-align: left; padding: 9px 14px; white-space: nowrap;
}
tbody td { padding: 11px 14px; border-bottom: 1px solid #F3F4F6; }
tbody tr:last-child td { border-bottom: none; }
tbody tr.clickable { cursor: pointer; }
tbody tr.clickable:hover { background: #FCFAFB; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
.sub { font-size: 12px; color: var(--grey-light); }
.dash { color: #D1D5DB; }

.empty { text-align: center; color: var(--grey); padding: 46px 20px; }

/* ---- messages ---------------------------------------------------------- */

.note {
  display: flex; gap: 9px; align-items: flex-start;
  font-size: 13px; border-radius: 10px; padding: 10px 12px; margin: 10px 0;
}
.note.warn { background: var(--warn-bg); color: var(--warn); }
.note.bad { background: #FEF2F2; color: var(--bad); }
.note.ok { background: #E7F6F0; color: var(--ok); }
.note.info { background: var(--card-2); color: var(--grey); }

/* A note that carries an ACTION.
 *
 * Michael, 5 Sep 2026, on a screenshot of the "waiting for Monday" banner:
 * "For the monday button, go ahead and do it/please fix." The button was the
 * last word of the sentence -- an inline-block in a wrapping paragraph -- so
 * the moment the message ran to a second line its 34px box sat on top of the
 * line above. There was never room for it in the text; it needs a column.
 *
 * `.note` is already a flex row, so the message takes the space it needs and
 * the button keeps its own. `min-width: 0` is what lets a long unbroken
 * Monday error wrap instead of pushing the button off the end -- a flex item's
 * default `min-width: auto` refuses to shrink below its content. */
.note-act .note-say { flex: 1 1 auto; min-width: 0; }
.note-act > button { flex: 0 0 auto; align-self: flex-start; }
@media (max-width: 620px) {
  /* On a phone the two columns leave the message about 140px. Stack them. */
  .note-act { flex-wrap: wrap; }
  .note-act .note-say { flex-basis: 100%; }
}

/* ---- modal ------------------------------------------------------------- */

.backdrop {
  /* FIXED, not absolute. `#overlay` is a static, zero-height div at the end of
     the body, so an absolutely positioned backdrop resolves `inset: 0` against
     the whole document and lands at the TOP of the page. Open a modal from
     anywhere you have scrolled to and it renders perfectly, a thousand pixels
     above the viewport — which looks exactly like a button that does nothing.
     Only ever noticed on short screens, where the top of the document and the
     top of the viewport are the same place. */
  position: fixed; inset: 0; background: rgba(15, 15, 25, 0.45);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 60px 16px; z-index: 50; overflow-y: auto;
}
.modal {
  background: var(--card); border-radius: 14px; width: 520px; max-width: 100%;
  padding: 22px 24px;
}
/* Wide enough for two tables side by side with the fields above them — the
   catalogue's item dialog, which now carries a price history and an order
   history. Lived in booths.css, which is loaded by exactly one screen, so
   `overlay(html, true)` anywhere else silently did nothing. */
.modal.wide { width: 660px; }
.modal h2 { font-size: 18px; margin-bottom: 4px; }

/* A heading inside a pane or a dialog. Generic, and here rather than in
   review.css so a screen that does not load the console stylesheet still gets
   a heading rather than unstyled body text. */
.section-label {
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.6px; color: var(--grey);
  margin: 20px 0 10px; display: flex; align-items: center; gap: 8px;
}
/* The one heading that has to be found rather than read past.
   Michael, 18 Aug 2026: "maybe we should have a more obvious UI saying BOOTH
   COSTS". Bigger, inked rather than grey, and with a rule under it, because it
   is the section a reviewer is scanning FOR. */
.section-label.big {
  font-size: 15px; letter-spacing: 0.3px; color: var(--ink);
  text-transform: uppercase; margin-top: 26px; padding-bottom: 8px;
  border-bottom: 2px solid var(--line);
}

/* Assign cost / Do NOT assign cost.
   Two buttons rather than a checkbox: a checkbox has an unlabelled off state,
   and "not a booth cost" is a decision somebody has to be able to SEE was
   taken, not infer from an empty box. */
.assign-toggle { display: flex; gap: 0; margin: 0 0 14px; }
.assign-toggle .seg {
  flex: 1; padding: 11px 14px; font-size: 13px; font-weight: 600;
  background: var(--card); color: var(--grey);
  border: 1px solid var(--line); cursor: pointer;
  transition: background .12s, color .12s;
}
.assign-toggle .seg:first-child { border-radius: 8px 0 0 8px; }
.assign-toggle .seg:last-child { border-radius: 0 8px 8px 0; border-left: none; }
.assign-toggle .seg:hover { background: var(--bg); }
.assign-toggle .seg.on {
  background: var(--pink); border-color: var(--pink); color: #fff;
}
/* The off side goes slate, not pink. Both halves lit in the accent would read
   as two live choices; only one of them is ever true. */
.assign-toggle .seg:last-child.on {
  background: var(--ink); border-color: var(--ink); color: #fff;
}

.modal .lede { color: var(--grey); font-size: 13px; margin-bottom: 16px; }
.actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 18px; }

/* ---- sign in ----------------------------------------------------------- */

.signin-wrap {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 24px;
}
.signin {
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  padding: 30px 28px; width: 400px; max-width: 100%;
}
.signin .mark { display: flex; align-items: center; gap: 11px; margin-bottom: 20px; }
.signin h1 { font-size: 21px; margin-bottom: 6px; }
.signin p.lede { color: var(--grey); font-size: 14px; margin: 0 0 20px; }
.signin input { font-size: 17px; padding: 12px 14px; }
.signin input.code {
  letter-spacing: 10px; text-align: center; font-size: 24px; padding: 14px;
}
.signin button { width: 100%; padding: 13px; font-size: 15px; margin-top: 4px; }
.signin .alt {
  background: none; border: none; color: var(--grey); font-size: 13px;
  padding: 12px 0 0; width: 100%; text-decoration: underline;
}

/* ---- responsive -------------------------------------------------------- */

@media (max-width: 1000px) {
  .scroll-x { overflow-x: auto; }
  .scroll-x table { min-width: 720px; }
}
@media (max-width: 860px) {
  header { flex-wrap: wrap; row-gap: 10px; padding: 12px 16px; }
  .appswitch {
    order: 10; width: 100%;
    margin: 0; padding: 10px 0 0;
    border-left: none; border-top: 1px solid var(--line);
    /* Wraps. It must NOT scroll — see the note on .appswitch above: `overflow`
       establishes a clipping context, and the dropdowns are absolutely
       positioned children of it, so they open correctly and are then sliced off
       at the container edge. On a phone that is the whole navigation, and it
       reads as a menu that is broken rather than one that is clipped. */
    flex-wrap: wrap; row-gap: 6px;
  }
  main { padding: 16px 14px; }
}
@media (max-width: 620px) {
  header { gap: 10px; }
  header .brand span, .whoami { display: none; }
}

/* The capture screen once had its own switcher variant, sitting UNDER a phone
   bar on its own row. It now uses the shared header like every other screen,
   so there is one switcher with one behaviour instead of two. */

/* ---- the searchable picker ---------------------------------------------
   One component for every MK, MTN and person choice. See static/js/picker.js
   for why this is not a <select>. */
.picker { position: relative; }
.picker-chips {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  border: 1px solid var(--line); border-radius: 10px;
  padding: 7px 9px; background: #fff; min-height: 40px;
}
.picker-chips:focus-within { border-color: var(--pink-mid); }
/* One chip at most, so there is nothing to wrap TO — and wrapping is what
   stopped the chip shrinking. See the note on .picker-chip below. */
.picker-chips.single { flex-wrap: nowrap; }
/* A long name must SHRINK, not shove.
 *
 * Damon, 17 August 2026, packing MK136: "a interface sizing issue". With
 * `flex-wrap: wrap` on `.picker-chips` and a chip that will not shrink, a long
 * item — "'BRASS TRIMMINGS' RETRO FIT L ANGLE 20x20x3M, BRUSHED LITE GOLD" —
 * takes the whole width and pushes the search box onto a SECOND line. That row
 * then stands ~35px taller than the three above it, the columns stop lining up
 * down the table, and it reads as the screen being broken.
 *
 * `min-width: 0` is the load-bearing half. A flex item refuses to shrink below
 * its content by default, so `max-width` and `text-overflow` alone did nothing —
 * the chip stayed full-size and the ellipsis never appeared. With it, the chip
 * gives way, the input keeps its 60px, and both stay on one line at any name
 * length. */
.picker-chip {
  background: var(--pink-soft); color: var(--pink-deep);
  border: 1px solid var(--pink-mid); border-radius: 999px;
  padding: 2px 9px; font-size: 12px; font-weight: 600; cursor: pointer;
  white-space: nowrap;
  flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
.picker-chip b { font-weight: 700; opacity: .6; margin-left: 2px; }
.picker-chip:hover b { opacity: 1; }
.picker-input {
  /* 120px was a floor, not a preference, and in a narrow table cell it was the
     thing that forced the wrap. `flex: 1 1 60px` lets the box give way to about
     a word's width before anything moves to a second line — enough to keep
     typing in, and small enough that the chip beside it never gets pushed off.
     A single-select picker with something already chosen barely needs it at
     all. */
  flex: 1 1 60px; min-width: 60px; border: none; outline: none;
  font-size: 14px; padding: 2px 0; background: transparent;
}
/* `fixed`, and the left/top/width are set in picker.js from the field's own
   box. Absolute positioning put this inside whatever clipping context the
   screen happened to have — `.card` (overflow:hidden, for the rounded corner)
   and `.scroll-x` (overflow-x:auto, which clips vertically too) — so the menu
   was cut off at the card's edge with its results hidden below the fold. */
.picker-list {
  position: fixed;
  background: var(--card); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: 0 12px 32px rgba(15,15,25,.16);
  max-height: 260px; overflow-y: auto; z-index: 80;
}
.picker-row {
  display: flex; gap: 8px; align-items: baseline;
  padding: 8px 11px; cursor: pointer; border-bottom: 1px solid #F5F5F7;
}
.picker-row:last-child { border-bottom: none; }
.picker-row:hover { background: #F9FAFB; }
.picker-row.on { background: var(--pink-soft); }
/* Opt-in per item (`disabled: true` in picker.js). Shown, greyed, and not
   takeable: the Import tab lists every tunnel in the Cloudflare account, and
   one another booth already holds is not on offer -- but hiding it would read
   as "not in Cloudflare". The row's sub says whose it is. */
.picker-row.off { opacity: .5; cursor: not-allowed; }
.picker-row.off:hover { background: transparent; }
.picker-row b { font-size: 13px; white-space: nowrap; }
.picker-row .sub { font-size: 12px; color: var(--grey); flex: 1; }
.picker-row .tick { color: var(--pink-deep); font-weight: 700; }
.picker-empty { padding: 10px 11px; font-size: 13px; color: var(--grey); }

/* A card's header strip.
   Lived in timesheet.css, which Ordering and the wording screen do not load —
   so on both of those it rendered with no layout at all and the button in it
   sat on top of the text beside it. Four screens use this; it belongs here. */
.card-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 11px 14px; border-bottom: 1px solid var(--line);
  font-size: 14px; font-weight: 600;
}
.card-head .spacer { flex: 1; }

/* A button that reads as a link — for a change of view sitting inside a line
   of explanatory text, where a real button would shout. */
.linky {
  background: none; border: none; padding: 0; font: inherit;
  color: var(--pink-deep); text-decoration: underline; cursor: pointer;
}
.linky:hover { filter: none; opacity: .75; }

/* An order line's item picker: wide, because a catalogue line is
   "SP-3222 — 1m 24v RGB 12w/m 60 leds/m SMD5050 IP20" and truncating it to the
   column width makes two different strips look identical. */
.item-pick { margin-bottom: 5px; }
.item-pick .picker-list { min-width: 420px; max-width: 92vw; }

/* Catalogue names run long — "JONDA - BOX ONLY - PRINT SLOT LEFT HAND SIDE -
   FRONT ACCESS" — and the shared .picker-row keeps its label on one line,
   which is right for "MK118" and wrong here: the row scrolled sideways and the
   end of the name, which is the part that distinguishes it from the four other
   Jonda box panels, was the part off screen. */
.item-pick .picker-row { align-items: flex-start; flex-wrap: wrap; }
.item-pick .picker-row b { white-space: normal; flex: 1 1 100%; }
.item-pick .picker-row .sub { flex: 1 1 100%; }
/* The cell must not clip the list that hangs out of it. */
.lines-edit td { overflow: visible; }
.lines-edit { overflow: visible; }

/* The picker in a tight space — a timesheet grid cell, a receipt split row, a
   bills dialog. Same component, smaller, and the dropdown given room to be
   read even though the cell it hangs off is 150px wide. */
.c-mk-pick, .pick-mount { min-width: 150px; }
.c-mk-pick .picker-chips,
.split-row .picker-chips,
.mk-row .picker-chips {
  min-height: 34px; padding: 4px 7px; gap: 4px;
}
.c-mk-pick .picker-input,
.split-row .picker-input,
.mk-row .picker-input { font-size: 13px; min-width: 80px; }
.c-mk-pick .picker-chip,
.split-row .picker-chip,
.mk-row .picker-chip { font-size: 11px; padding: 1px 7px; }

/* Wider than its host, so "Flour Child Richmond" is not truncated to fit a
   column sized for "MK118". Right-anchored would clip on a phone, so it grows
   rightwards from the left edge and is capped at the viewport. */
.c-mk-pick .picker-list,
.split-row .picker-list,
.mk-row .picker-list { right: auto; min-width: 280px; max-width: 90vw; }

/* A grid cell clips its overflow by default, which would cut the dropdown off
   at the row below. */
.grid-scroll table td { overflow: visible; }

/* A heading inside the picker list.
   Lets a caller float the relevant items to the top and SAY why they are at the
   top — a list that silently reorders reads as a list with a bug in it. */
.picker-head {
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .06em; color: var(--grey);
  padding: 9px 11px 5px; background: #FAFAFB;
  border-bottom: 1px solid #F0F0F3;
  position: sticky; top: 0;
}
.picker-head:first-child { border-top: none; }

/* ---- whose screen is this ----------------------------------------------- *

   Michael: "it's going to help us all know exactly who's who in the time sheet
   game." Three crews use the same screens on the same laptop in the same
   office, and the only thing saying which one you are looking at is a word in a
   dropdown.

   Every colour below is a TOKEN OVERRIDE, not a component rule. That is the
   whole design: `.card`, `button`, `input`, `.chip`, `table` and the rest are
   already written against the tokens, so a crew theme is ~20 declarations and
   every component follows — including ones built after this file was last
   touched. The handful of component rules further down exist only because a few
   places in this sheet hardcode a light grey instead of using a token; those are
   the exceptions, and each is marked.

   Warehouse is deliberately absent. Michael asked to keep their scheme exactly
   as it is, so `data-crew="warehouse"` inherits :root.

   The attribute is set by the server for single-crew screens, and by the grid
   for the timesheet — the one page that changes crew without navigating. */

/* ---- tech: the electric navy ------------------------------------------- *

   17 Aug 2026. This was a pale pink wash and Michael's verdict was "a little
   bit too like Legally Blonde made her first excel document" — fair, and worse
   than unflattering: a pale screen is the wrong instrument for a job done at
   7am in a car park and at 11pm in a dark pub, where a bright page is a torch
   in your face and the phone dims itself to compensate.

   So it goes dark, in the old app's own electric palette — hot pink #FF2E9A and
   cyan #1FD6FF, both lifted from timesheet-app/static/styles.css unchanged,
   because the crew already reads those as "the tech app".

   The ground is PLUM, not navy. The first pass here used the old app's deep
   navy and Michael's answer was that Hustle should be "the pink electric
   background instead of the blue" — which is right: navy is what the OFFICE
   theme leans on, and two dark blues in one estate is two screens nobody can
   tell apart at a glance. Pink is Social Booth's; the tech screens get it at
   full strength, and the bolt keeps the cyan so there is still a second colour
   doing the accenting.

   What is NOT carried over is the looseness — the 900-weight letter-spaced
   headings on every element, the glow on every button. The palette is the old
   one; the restraint is MK0's. */

body[data-crew="tech"] {
  /* Native controls — the select popover, the date picker wheel, the caret and
     the scrollbars — are drawn by the OS, not by us. Without this, iOS renders
     a white dropdown over a navy page and Safari paints black text into a dark
     input. It is one line and it is the difference between a dark theme and a
     dark background. */
  color-scheme: dark;

  --bg: #17061F;
  --card: #260C31;
  --card-2: #32113F;      /* raised: table headers, menus, ghost buttons */
  --line: #461B56;
  --ink: #FBEAF6;
  --grey: #C79CC0;
  --grey-light: #9E77A0;

  /* Pink stays the brand accent, lifted for a dark ground — #FD4196 on navy is
     muddy where #FF2E9A is not. */
  --pink: #FF2E9A;
  /* soft/deep INVERT on a dark theme: the chip becomes a dark plum and its text
     becomes the light pink. Every .chip.pink, .navtop.on and .appswitch
     .current in the app then reads correctly with no rule of its own. */
  --pink-soft: #4A1247;
  --pink-mid: #8A2A6E;
  --pink-deep: #FFD0EA;

  --cyan: #1FD6FF;        /* the electric second accent, from the old app */

  --warn: #FFC46B;
  --warn-bg: #3A2A0E;
  --ok: #3DD9A0;
  --bad: #FF6B7A;
  --bad-bg: #3E1220;
}

/* The page itself. A flat fill reads as "the lights went out"; the old app's
   radial lift at the top is what made it read as deliberate. Softened from the
   original — this one is a gradient you notice only if you look for it. */
body[data-crew="tech"] {
  background:
    radial-gradient(1100px 560px at 72% -12%, #45114C 0%, #24082E 46%, #17061F 100%)
    fixed;
}

body[data-crew="tech"] header {
  background: #12041A;
  border-bottom: 2px solid var(--pink);
}

/* The exceptions: places in this sheet that hardcode a light grey rather than
   reading a token. Each one would otherwise be a white slab on navy. */
body[data-crew="tech"] .appswitch a,
body[data-crew="tech"] .navtop           { background: var(--card-2); }
body[data-crew="tech"] .appswitch a:hover,
body[data-crew="tech"] .navtop:hover     { background: #3C1449; border-color: #5E2470;
                                           color: var(--ink); }
body[data-crew="tech"] .navmenu          { background: var(--card); }
body[data-crew="tech"] .navmenu a:hover  { background: var(--card-2); }
body[data-crew="tech"] button.ghost:hover { background: var(--card-2); }
body[data-crew="tech"] thead th          { background: var(--card-2); }
body[data-crew="tech"] .chip             { background: var(--card-2); color: var(--grey); }
/* The Heartbeat's amber chips on the plum theme: "part read" (sales-partial),
   the printer's own fault, the 2" cut note. The generic tech rule above
   outranks `.chip.sales-partial` in heartbeat.css (body + .chip beats
   .chip.x), so each shipped GREY on the only theme the Heartbeat has -- the
   trap 7.2 fixed for the paper chips below and nothing else. Tokens only:
   the tech set inverts --warn-bg / --warn. These sit BEFORE .chip.bad on
   purpose: a camera chip is `chip cut-note bad`, the two rules tie on
   specificity, and the later one wins, so bad must stay the later one
   (tests/test_heartbeat_screen.py). cut-queued needs no twin: the generic
   rule already gives it the grey it asks for, and its dashed edge survives. */
body[data-crew="tech"] .chip.sales-partial,
body[data-crew="tech"] .chip.printer-fault,
body[data-crew="tech"] .chip.cut-note    { background: var(--warn-bg); color: var(--warn); }
body[data-crew="tech"] .chip.ok          { background: #0E3A2C; color: var(--ok); }
body[data-crew="tech"] .chip.bad         { background: #3D1720; color: var(--bad); }
/* The Heartbeat's paper chips on the plum theme. The generic tech chip rule
   above (body + .chip) outranks `.chip.paper-sb` in heartbeat.css, which is
   why 2026-09-30.2 shipped "SB pink, Rural blue" and the screen kept showing
   grey — Michael: "These didn't get adjusted in that update". Same shape as
   .chip.ok / .chip.bad: a deep wash of the colour, the colour itself as ink. */
body[data-crew="tech"] .chip.paper-sb    { background: #4A1233; color: var(--pink); }
body[data-crew="tech"] .chip.paper-rural { background: #12264A; color: #7FAAFF; }
body[data-crew="tech"] .note.bad         { background: #3D1720; color: var(--bad); }
body[data-crew="tech"] .note.ok          { background: #0E3A2C; color: var(--ok); }
/* The last two light bands on a tech screen. Michael, 18 Aug 2026: "fix the
   weird white banners and just make it all the same colour scheme." */
body[data-crew="tech"] .note.info         { background: var(--card-2); color: var(--grey); }
body[data-crew="tech"] tbody tr.clickable:hover { background: var(--card-2); }
/* :not(.on) matters. `body[data-crew] .assign-toggle .seg` outranks
   `.assign-toggle .seg.on`, so without it the tech theme repainted the CHOSEN
   half back to the card colour and the pink fill vanished — the toggle looked
   like nothing was selected. */
body[data-crew="tech"] .assign-toggle .seg:not(.on) { background: var(--card); }
body[data-crew="tech"] .assign-toggle .seg:not(.on):hover { background: var(--card-2); }
body[data-crew="tech"] .assign-toggle .seg:last-child.on {
  background: var(--card-2); border-color: var(--pink-mid); color: var(--ink);
}
/* THE PAGE-NAME PILL, and why it was invisible.
 *
 * `.pagestripe .where` is a hardcoded WHITE pill whose text is
 * var(--pink-deep). On :root that token is #9D1D5C and gives 7.6:1 — the
 * figure the comment above .pagestripe quotes as the reason the band is safe.
 * On a dark crew --pink-deep INVERTS, to #FFD0EA, because every other thing
 * that reads it is light text on a dark plum chip. Pale pink on a white pill
 * is 1.36:1. The sheet's own documented invariant, silently reversed.
 *
 * It had never been seen because only three screens carry a page stripe and
 * the other two are warehouse, which inherits :root. The Activator, from
 * 15 Sep 2026, is the first data-crew="tech" page in MK0 to carry one —
 * Michael, looking at it: "Make sure you fix this - it's barely legible."
 *
 * So the pill goes dark on a dark crew rather than staying a light-theme
 * artefact: 16.8:1 for the text, and 5.7:1 for the pill against the hot pink
 * band it sits on. */
body[data-crew="tech"] .pagestripe .where {
  background: var(--bg); color: var(--ink);
}

/* A DISABLED BUTTON MUST STILL BE READABLE, and on a dark ground opacity is
 * not a way to say "not yet".
 *
 * `button:disabled { opacity: .5 }` blends both the face and the label halfway
 * into whatever is behind them. On white that still reads. On plum it puts the
 * label at 2.33:1 — and the Activator's RESTING state is a disabled button,
 * as is the whole twelve-second window while a session is starting, which is
 * the only feedback the person gets that their tap landed.
 *
 * Grey-on-raised-card instead: 7.0:1, obviously not-pink, obviously not
 * pressable. */
body[data-crew="tech"] button:disabled,
body[data-crew="tech"] .btn:disabled {
  opacity: 1;
  background: var(--card-2);
  color: var(--grey);
  border-color: var(--line);
}

/* The placeholder is the last of the navy era. Every other input on a tech
 * screen reads plum; this one was still #6B7FAD, a blue-grey, from before the
 * theme moved off navy (see the note at the top of this block). */
body[data-crew="tech"] .picker-input::placeholder { color: #8E6890; }

body[data-crew="tech"] .dash             { color: #6B4270; }
body[data-crew="tech"] .tile.warn        { background: var(--warn-bg); }
body[data-crew="tech"] .tile.warn .value { color: var(--warn); }

/* Inputs sit BELOW the card rather than level with it, so a field reads as a
   hole to type into rather than a panel. On a light theme the border does that
   work; on a dark one it does not. */
body[data-crew="tech"] input,
body[data-crew="tech"] select,
body[data-crew="tech"] textarea {
  background: #1C0725;
  color: var(--ink);
  border-color: var(--line);
}
body[data-crew="tech"] input::placeholder,
body[data-crew="tech"] textarea::placeholder { color: #8E6890; }
body[data-crew="tech"] input:focus,
body[data-crew="tech"] select:focus,
body[data-crew="tech"] textarea:focus {
  border-color: var(--cyan);
  outline: 2px solid rgba(31, 214, 255, 0.25);
  outline-offset: 0;
}

/* ---- office: the blue wash --------------------------------------------- */

body[data-crew="office"] {
  --bg: #EEF3FB;
  --line: #D8E2F2;
  --card: #FFFFFF;
}
body[data-crew="office"] header { border-bottom: 3px solid #2F5FA8; }

/* The strip is the same height on every crew, so a warehouse screen is not a
   pixel shorter than the other two and nothing below it shifts. */
body[data-crew="warehouse"] header { border-bottom: 3px solid var(--line); }

/* The picker on a dark crew. `.picker-chips` hardcodes #fff and the rows use
   light greys, and it is the single most-used control in the app — a white slab
   in the middle of a navy card is the first thing anybody would notice. */
body[data-crew="tech"] .picker-chips     { background: #1C0725; }
body[data-crew="tech"] .picker-chips:focus-within { border-color: var(--cyan); }
body[data-crew="tech"] .picker-input     { color: var(--ink); }
body[data-crew="tech"] .picker-input::placeholder { color: #6B7FAD; }
body[data-crew="tech"] .picker-list      { box-shadow: 0 12px 32px rgba(0,0,0,.55); }
body[data-crew="tech"] .picker-row       { border-bottom-color: #3A1447; }
body[data-crew="tech"] .picker-row:hover { background: var(--card-2); }
body[data-crew="tech"] .picker-row.off:hover { background: transparent; }
body[data-crew="tech"] .picker-head      { background: var(--card-2); color: var(--grey); }

/* ---- the switcher on a phone -------------------------------------------- *

   Seven groups wrap to three rows on a 375px screen and ate 217px of an 812px
   iPhone — 27% of the display gone to navigation before the tech has seen a
   single field. On the one screen that is genuinely phone-first, that is the
   difference between the clock being visible on open and not.

   So on a narrow screen the switcher collapses to the group you are IN, plus a
   button to see the rest. Nothing is removed — a tap expands the full set — and
   on a laptop this whole block does nothing. */

.navtoggle { display: none; }

@media (max-width: 700px) {
  header { padding: 10px 14px; gap: 10px; flex-wrap: wrap; }
  header .brand b { font-size: 16px; }
  header .brand span { display: none; }

  .navtoggle {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 40px; min-height: 40px; padding: 0 12px;
    border-radius: 999px; border: 1px solid var(--line);
    background: var(--card); color: var(--grey);
    font-size: 15px; line-height: 1;
  }

  .appswitch {
    margin-left: 0; padding-left: 0; border-left: none;
    order: 3; width: 100%;
  }

  /* Collapsed: only where you are. */
  header:not(.nav-open) .appswitch > *:not(.navgroup.on):not(.current) {
    display: none;
  }
  header:not(.nav-open) .appswitch { width: auto; order: 0; }

  header.nav-open .appswitch { display: flex; }

  /* The menus themselves are full width when open, so a child is a real
     thumb-sized row rather than a 13px link in a floating card. */
  header.nav-open .navmenu { position: static; box-shadow: none; min-width: 0; }
  header.nav-open .navmenu a { padding: 11px 12px; font-size: 15px; }
  header.nav-open .navgroup { width: 100%; }
  header.nav-open .navtop { width: 100%; justify-content: flex-start; }
}

/* The phased SimPro import. Three steps that fill in as they finish, because
   the whole thing takes about three minutes and a spinner for that long is
   indistinguishable from a hang. */
.import-steps { list-style: none; margin: 14px 0; padding: 0; }
.import-steps li {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; padding: 9px 12px; border: 1px solid var(--line);
  border-radius: 10px; margin-bottom: 7px; font-size: 14px;
  color: var(--grey-light);
}
.import-steps li.on   { border-color: var(--pink); color: var(--ink); }
.import-steps li.done { border-color: var(--ok); color: var(--ink); }
.import-steps li.done::after { content: "✓"; color: var(--ok); }

/* ---- which console am I in ---------------------------------------------- *

   Michael: "a clear PINK banner for receipts and a clear Blue banner in bills
   so any user can clearly see the difference."

   The two screens are deliberately near-identical — same three panes, same
   fields, same buttons — because they are the same job done to two kinds of
   document. That similarity is what makes a full-width colour bar necessary
   rather than decorative: without it the only thing telling you whether you are
   approving MONEY ALREADY SPENT or authorising MONEY ABOUT TO GO OUT is the
   word in the top-left corner.

   Full bleed, its own line, and it states the difference in words as well as
   colour — the colour is for the glance, the sentence is for the first week and
   for anybody who cannot separate pink from blue. */

.console-banner {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 9px 26px; color: #fff;
  font-size: 13px; line-height: 1.4;
}
.console-banner b { font-size: 14px; letter-spacing: .02em; }
.console-banner .what { opacity: .92; }
.console-banner .spacer { flex: 1; }

/* Receipts: Social Booth pink. Money already spent. */
.console-banner.receipts { background: var(--pink); }
/* Bills: electric blue. Money about to leave — a different colour for a
   different direction of travel.

   Michael asked for it brighter than the first attempt. #2F5FA8 was the office
   crew's navy, which read as a muted cousin of the pink bar rather than as its
   own signal; at a glance across the room the two bars were the same bar. This
   is a real blue and it carries white text at the same weight pink does. */
.console-banner.bills { background: #0A6BFF; }

/* The header carries a 3px rule along its bottom edge, and on an office screen
   that rule is navy. Directly above a pink or an electric-blue banner it reads
   as a third colour nobody chose. On the two consoles it takes the banner's own
   colour instead, so the top of the screen is one band of one colour — which is
   the whole point of the banner. */
body.console-receipts header { border-bottom-color: var(--pink); }
body.console-bills    header { border-bottom-color: #0A6BFF; }


/* ---- the side panel ----------------------------------------------------- *
 *
 * Michael, 17 August 2026: "Can we have the 'new order' button pop out on the
 * right hand side so it is a dual screen interface? If someone is in iPad,
 * iPhone or tablet it can take the entire screen."
 *
 * A centred modal over a dimmed page says "stop what you were doing". That is
 * right for a confirmation and wrong for starting an order, where the list of
 * what is already on order is exactly what you want to see WHILE you fill the
 * form in — is this supplier already coming on Thursday?
 *
 * So this is a panel, not a dialog: the page stays lit and stays readable, and
 * the panel takes the right-hand third. Below 1024px it takes the whole screen
 * instead, because there is no dual anything on a tablet.
 *
 * 1024 rather than the app's usual 900: 900 catches an iPad in portrait (768pt)
 * and MISSES it in landscape (1024pt), which would have given the one device
 * this was asked for two different behaviours depending on how it was held.
 */

.panel-wrap {
  position: fixed; inset: 0; z-index: 55;
  display: flex; justify-content: flex-end;
  /* No dim. The whole point is that what is behind stays readable. A shadow on
     the panel's edge is enough to lift it off the page. */
  pointer-events: none;
}
.panel-wrap.dim {
  background: rgba(17, 17, 26, 0.18); pointer-events: auto;
}

.panel {
  pointer-events: auto;
  width: 460px; max-width: 100%;
  background: var(--card);
  border-left: 1px solid var(--line);
  box-shadow: -14px 0 34px rgba(17, 17, 26, 0.13);
  display: flex; flex-direction: column;
  animation: panel-in 0.16s ease-out;
}
@keyframes panel-in {
  from { transform: translateX(24px); opacity: 0.4; }
  to   { transform: translateX(0); opacity: 1; }
}
/* Somebody who has asked for less movement gets none. */
@media (prefers-reduced-motion: reduce) {
  .panel { animation: none; }
}

.panel-head {
  display: flex; align-items: center; gap: 10px;
  padding: 16px 20px 12px; border-bottom: 1px solid var(--line);
}
.panel-head h2 { font-size: 17px; }
.panel-head .spacer { flex: 1; }
.panel-close {
  background: none; border: none; color: var(--grey);
  font-size: 20px; line-height: 1; padding: 2px 8px; border-radius: 8px;
}
.panel-close:hover { background: #F3F4F6; color: var(--ink); filter: none; }

.panel-body { flex: 1; overflow-y: auto; padding: 16px 20px; }
.panel-foot {
  border-top: 1px solid var(--line); padding: 14px 20px;
  display: flex; gap: 8px; justify-content: flex-end;
  /* Sits above the home indicator on a phone. */
  padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
}

/* A tablet or a phone: the whole screen. There is no second pane to preserve,
   and a 460px panel on a 768px screen leaves a useless 300px stripe. */
@media (max-width: 1024px) {
  .panel { width: 100%; border-left: none; box-shadow: none; }
  .panel-wrap { justify-content: stretch; }
  @keyframes panel-in {
    from { transform: translateY(16px); opacity: 0.4; }
    to   { transform: translateY(0); opacity: 1; }
  }
}


/* A button that reads as a link. Used for "I've forgotten my code" under the
   sign-in card: it must be reachable and obvious to somebody who is stuck, and
   must not compete with Continue for the person who is not. */
button.linkish {
  display: block; width: 100%; margin-top: 12px;
  background: none; border: none; padding: 6px;
  color: var(--grey); font-size: 13px; text-decoration: underline;
}
button.linkish:hover { color: var(--ink); background: none; filter: none; }

/* The toast floats ABOVE the modal layer.
 *
 * It was a plain in-flow .note sitting near the top of <main>, with no
 * positioning and no stacking of its own — while `.backdrop` is
 * `position: fixed; inset: 0; z-index: 50` and covers the entire screen.
 *
 * So every confirmation dialog in the app had the same failure: press the
 * button, the request fails, the catch calls toast() with a perfectly good
 * sentence explaining why — and it is painted UNDERNEATH the backdrop where
 * nobody can see it. The button re-enables and nothing else happens. Michael,
 * 18 Aug 2026, on the booth installer: "this finish and log booth button
 * doesn't work and doesn't 'complete' … worth checking a lot of the 'confirm'
 * box buttons." One cause, every dialog.
 *
 * 80 clears the backdrop (50) and the picker dropdowns (60). Fixed and
 * centred so it is in the same place whatever is behind it. It DOES take
 * clicks — that is how the dismiss button works — but it is one line tall and
 * pinned to the top, well clear of the buttons in a dialog below it.
 */
#toast:not([hidden]) {
  /* Pinned to ONE edge only.
   *
   * Three pages used to set `right/bottom` inline as well, and an element
   * given both `top` and `bottom` stretches to fill the gap between them —
   * which is how a one-line "Saved." became a green panel the height of the
   * screen, sitting over the bill somebody was reading with no way to shut
   * it. The inline styles are gone; this rule is the only one that positions
   * it, and it names every edge so nothing can half-override it again.
   */
  position: fixed;
  top: 16px;
  bottom: auto;
  left: 50%;
  right: auto;
  transform: translateX(-50%);
  z-index: 80;
  width: max-content;
  max-width: min(560px, calc(100vw - 32px));
  /* Bounded, and scrollable if a message is ever long. A notice can never
     again cover the thing it is telling you about. */
  max-height: 40vh;
  overflow-y: auto;
  margin: 0;
  padding-right: 38px;           /* room for the dismiss button */
  box-shadow: 0 10px 30px rgba(15, 15, 25, 0.28);
}

/* Dismissable, because Michael asked for one and because a notice that
   cannot be shut is a notice that gets in the way. Click anywhere on it
   works too — see toast() in api.js. */
#toast .toast-x {
  position: absolute; top: 6px; right: 8px;
  border: none; background: transparent; cursor: pointer;
  font-size: 16px; line-height: 1; padding: 4px 6px;
  color: inherit; opacity: .55; border-radius: 6px;
}
#toast .toast-x:hover { opacity: 1; background: rgba(0, 0, 0, .06); }

/* A capability and what it unlocks, one per line.
   These used to be twelve inline chips reading "push xero", "finalise booth"
   and so on — no way to tell which of the two Xero-ish ones you needed, which
   is how a correctly-set permission looked like a broken one. */
.cap-row {
  display: flex; gap: 9px; align-items: flex-start;
  padding: 7px 0; border-bottom: 1px solid #F4F4F6;
}
.cap-row:last-child { border-bottom: none; }
.cap-row input { width: auto; margin-top: 3px; flex: none; }
.cap-row b { text-transform: capitalize; font-size: 13.5px; }
.cap-help {
  display: block; font-size: 12px; color: var(--grey); line-height: 1.45;
  margin-top: 1px;
}

/* The Sales Tracker state grant on the People screen.
   Pills rather than a column of tick boxes: eight states in a row read as one
   answer to one question, where eight stacked rows read as eight decisions. */
.state-pills { display: flex; gap: 6px; flex-wrap: wrap; }
.state-pill {
  display: inline-flex; align-items: center; gap: 5px;
  border: 1px solid var(--line); border-radius: 999px; padding: 4px 11px;
  font-size: 12.5px; cursor: pointer; background: var(--card);
}
.state-pill.on { border-color: #2563EB; background: #EFF6FF; color: #1D4ED8; }
.state-pill input { width: auto; margin: 0; }

/* ---- the person card on the People screen -------------------------------
   Michael, 21 Aug 2026: "Can we upgrade this UI a little please to make it
   clearer. and bolden important items."

   Two things were wrong. The TIER — the only line on the card that says what
   this person can actually do — was set in the same grey as their email
   address. And seven buttons of identical weight put "Remove", which cannot
   be undone, one slip of the mouse away from "Edit details". */
.person-id {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 2px 0 14px;
}
.tier-chip {
  font-size: 12px; font-weight: 600; border-radius: 999px;
  padding: 3px 11px; background: var(--pink-soft); color: var(--pink-deep);
  border: 1px solid var(--pink-mid);
}
.tier-chip.off { background: #FEF2F2; color: var(--bad); border-color: #FECACA; }
.tier-chip.quiet {
  background: #F3F4F6; color: var(--grey); border-color: var(--line);
}
.person-meta { font-size: 13px; color: var(--grey); }

.person-actions {
  display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 18px;
}

/* One heading treatment for every block on the card, so "Pay rate" and
   "Signed in on 2 devices" stop being two different shapes. */
.card-label {
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.6px; color: var(--grey);
  margin: 18px 0 6px; padding-top: 14px; border-top: 1px solid var(--line);
}
.card-label b { color: var(--ink); font-weight: 700; }

/* The irreversible pair, alone, behind a rule. Red TEXT was the only thing
   separating them before, and red text on a button that looks like every
   other button is not a speed bump. */
.danger-zone { margin-top: 22px; padding-top: 14px;
               border-top: 1px solid var(--line); }
.danger-label {
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.6px; color: var(--bad); margin-bottom: 7px;
}
.danger-row { display: flex; gap: 8px; flex-wrap: wrap; }
button.ghost.danger, .btn.ghost.danger {
  background: transparent; color: var(--bad); border-color: #FECACA;
}
button.ghost.danger:hover { background: #FEF2F2; border-color: var(--bad); }

/* The purchase-order email, editable in the send dialog. Monospace so the
   quantity columns line up as they will in the supplier's inbox. */
.mail-edit { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; line-height: 1.45; width: 100%; }

/* A chip that is showing an answer nobody can change any more — a document
   already in Xero. Still legible, plainly not a control. */
.kind-chip.off { opacity: .55; cursor: default; pointer-events: none; }
