:root{
  --navy:#0f2036; --sidebar:#0d1c30; --navy2:#183256; --red:#c8262c;   /* the logo's own red, 2026-09-29 */
  --ink:#1c2733; --ink2:#5a6979; --line:#e4e8ee; --sunk:#f3f5f8; --bg:#f6f8fa;
}
*{box-sizing:border-box}
[hidden]{display:none!important}
body{margin:0;font:14px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;color:var(--ink);background:var(--bg)}
.brand-ea{color:var(--red);font-weight:900}
h1{font-size:20px;margin:0}

/* login (branded, two-panel: EA jobsite photo + form with the real logo) */
.login{position:fixed;inset:0;z-index:100;background:#fff;display:flex;padding:26px}
.login-card{flex:1;display:grid;grid-template-columns:1.05fr 0.95fr;gap:34px;overflow:hidden}
/* THE PHOTOGRAPH IS LANDSCAPE AND THIS PANEL IS TALLER THAN IT IS WIDE (measured: 0.84 on a laptop,
   0.69 in a smaller window), so a centred crop cut the man with the rake in half. The focal point sits
   right of centre instead, which keeps him whole at every width. One file, one number: the number is
   the thing to nudge if the design lead wants a different part of the picture. */
.login-photo{border-radius:16px;background:url("/img/login-photo.jpg") 75% center/cover no-repeat;min-height:0}
.login-form{padding:0 6%;display:flex;flex-direction:column;justify-content:center;gap:11px;width:100%;max-width:440px;margin:0 auto}
.login-logo{width:200px;max-width:64%;margin-bottom:16px}
.login-form .wb{font-size:26px;margin:0 0 6px}
.login-form label{display:flex;flex-direction:column;font-size:12px;color:var(--ink2);gap:4px;font-weight:600}
.login-form input{border:1px solid var(--line);border-radius:9px;padding:11px 12px;font:inherit;color:var(--ink)}
.login-form input:focus{outline:none;border-color:var(--navy2)}
.btn{background:var(--navy);color:#fff;border:none;border-radius:9px;padding:12px;font:inherit;font-weight:700;cursor:pointer;margin-top:6px}
.btn:hover{background:#0c2036}
.btn:disabled{opacity:.4;cursor:default;pointer-events:none}
/* Why a Save is greyed out, beside it (see showSaveState in app.js). In a narrow pop-up the words
   wrap and the buttons keep their one line. */
.savenote{font-size:12px;color:var(--ink2);align-self:center;margin-right:4px;text-align:right;line-height:1.3}
.modal-f .btn{flex-shrink:0;white-space:nowrap}
.err{color:#c0392b;font-size:12.5px}
.hint{color:var(--ink2);font-size:11.5px;margin-top:6px;border-top:1px solid var(--line);padding-top:10px}
/* The sign-in steps. Each step is its own stack so that hiding one leaves no gap behind it; the
   form's own `gap` would otherwise keep spacing a hidden block. */
#pw-step,#code-step,#alt-ways,#demo-login{display:flex;flex-direction:column;gap:11px}
#pw-step[hidden],#code-step[hidden],#alt-ways[hidden],#demo-login[hidden]{display:none}
/* The show-password eye sits inside the field. The input keeps its own padding on the right so the
   longest password never runs underneath the button. */
.pwwrap{position:relative;display:block}
.pwwrap input{width:100%;padding-right:42px}
.eye{position:absolute;right:4px;top:50%;transform:translateY(-50%);width:34px;height:34px;display:flex;
  align-items:center;justify-content:center;border:none;background:none;padding:0;cursor:pointer;color:var(--ink2);border-radius:8px}
.eye svg{width:19px;height:19px;display:block}
.eye:hover{color:var(--ink);background:var(--sunk)}
.eye[aria-pressed="true"]{color:var(--navy2)}
.eye:focus-visible{outline:2px solid var(--red);outline-offset:1px}
.codeintro{margin:0;color:var(--ink2);font-size:12.5px;line-height:1.5}
/* How many tries are left. Deliberately NOT red until it is the last one: an amber-free warning on
   the first wrong digit reads as an accusation. */
.hint2{color:var(--ink2);font-size:12px}
.hint2.last{color:#c0392b;font-weight:600}
/* A button that reads as a link. Used where the action is a way back rather than a way on, so it
   cannot be mistaken for the thing to press. */
.linky{background:none;border:none;padding:4px;font:inherit;font-size:12.5px;color:var(--ink2);
  text-decoration:underline;cursor:pointer;align-self:center}
.linky:hover{color:var(--ink)}
.linky:focus-visible{outline:2px solid var(--red);outline-offset:2px;border-radius:4px}
@media(max-width:720px){.login{padding:0}.login-card{grid-template-columns:1fr}.login-photo{display:none}.login-form{padding:24px}}

/* app shell */
#app{display:flex;min-height:100vh}
/* THE MENU STAYS PUT. "When you scroll down, it only needs to be the white part that needs to scroll
   down. The left side menu that needs to be a sticky one" (2026-09-14, 44:14); "I couldn't see the
   logout anymore" (44:05). The menu used to be as tall as the whole PAGE, so a long page carried its
   buttons off the top of the screen. It is now as tall as the WINDOW and stuck to its top: only the
   white area moves. The height comes from fitSideMenu in app.js, which allows for the text size (a
   bigger text size enlarges the menu too, and a plain 100vh would then run past the bottom); 100vh is
   only the starting value before that runs. */
.side{width:210px;flex:none;background:var(--sidebar);color:#cdd6e2;display:flex;flex-direction:column;padding:18px 0;
  position:sticky;top:0;align-self:flex-start;height:100vh;overflow:hidden}
.logo{padding:2px 20px 18px;flex:none}
.logo img{height:26px;display:block;max-width:100%}
/* When there are more buttons than fit (a small laptop, a big text size, the employee portal's screens
   still to come), the buttons scroll inside the menu and Sign out stays pinned below them. */
.nav{padding:0 12px;flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}
.nav::-webkit-scrollbar{width:8px}
.nav::-webkit-scrollbar-track{background:transparent}
.nav::-webkit-scrollbar-thumb{background:#2c4466;border-radius:4px}
.nav::-webkit-scrollbar-thumb:hover{background:#3b5a84}
@supports not selector(::-webkit-scrollbar){.nav{scrollbar-width:thin;scrollbar-color:#2c4466 transparent}}
.nav button{display:block;width:100%;border:none;background:none;font:inherit;padding:11px 14px;margin-bottom:4px;color:#c2cbd8;font-size:13px;font-weight:500;cursor:pointer;border-radius:9px;text-align:center}
.nav button.on{background:var(--red);color:#fff;font-weight:600}
.nav button:not(.on):hover{background:var(--navy2);color:#fff}
.nav button:focus-visible,.jobcard:focus-visible,.board .bar:focus-visible,tbody tr:focus-visible{outline:2px solid var(--red);outline-offset:1px}
.nav .apart{margin-top:18px;padding-top:20px;border-top:1px solid #1c3252}
.adminwrap{display:grid;gap:14px;max-width:760px}
.adminwrap .card h2{margin:0 0 4px;font-size:16px}
.adminwrap .card p{margin:0 0 8px;max-width:70ch;color:var(--ink)}
.adminwrap .card p:last-child{margin-bottom:0}
.adminrow{display:flex;gap:10px;align-items:baseline;padding:9px 0;border-bottom:1px solid var(--line)}
.adminrow:last-child{border-bottom:0}
.adminrow b{min-width:180px;flex:none}
.adminrow .off{color:var(--ink2)}
.whoruns{font-size:12px;color:var(--ink2)}
/* The way out at the foot of the menu. Red on this dark blue would disappear, so it is an outline that
   warms up when you reach for it. */
.signout-side{display:inline-flex;align-items:center;gap:8px;width:100%;justify-content:center;
  font:inherit;font-size:13px;font-weight:600;padding:9px 12px;border-radius:8px;cursor:pointer;
  color:#ffb4ab;background:transparent;border:1px solid #4a2a2e}
.signout-side svg{width:17px;height:17px}
.signout-side:hover{background:#3a1f22;border-color:#7a3a40;color:#ffd0c9}
.signout-side:focus-visible{outline:2px solid #ffb4ab;outline-offset:2px}
.who{padding:12px 20px 0;font-size:12px;color:#9fb0c4;display:flex;flex-direction:column;gap:4px;border-top:1px solid #1c3252;flex:none}
.link{background:none;border:none;color:#9fb0c4;text-decoration:underline;cursor:pointer;font:inherit;font-size:12px;padding:0;text-align:left}
/* THE MENU TUCKS AWAY, which is what gives the Long Term Calendar its width back. See the note above
   the menu code in app.js for the reasoning: the owner will not scroll that calendar sideways and it
   needs roughly twice the width it has, so the menu closes about a sixth of the gap and the smaller
   text sizes do the rest.
   Collapsed is a narrow rail, not nothing, so every screen stays one click away and nobody has to
   find their way back: each button keeps its initials, which is the design lead's own shorthand for
   them ("home is H, LTC"), and its full name on hover. The text itself is left in place and only
   made invisible, so the name a screen reader announces, and the name the checks read, is still the
   real one rather than two letters. */
.side.collapsed{width:64px}
.side.collapsed .logo{padding:2px 10px 14px}
.side.collapsed .menucap{padding:0 8px 10px;justify-content:center}
.side.collapsed .nav{padding:0 8px}
.side.collapsed .nav button{font-size:0;line-height:0;padding:11px 0}
.side.collapsed .nav button::before{content:attr(data-short);font-size:13px;line-height:1.3;font-weight:700;letter-spacing:.03em}
.side.collapsed .who{padding:12px 8px 0}
.side.collapsed .signout-side{font-size:0;line-height:0;gap:0;padding:9px 0}
.side.collapsed .menubtn svg{transform:rotate(180deg)}
.menucap{padding:0 12px 10px;flex:none;display:flex;justify-content:flex-end}
.menubtn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;
  border:1px solid #1c3252;background:transparent;color:#9fb0c4;border-radius:8px;cursor:pointer}
.menubtn svg{width:18px;height:18px;display:block}
.menubtn:hover{background:var(--navy2);color:#fff;border-color:#2c4466}
.menubtn:focus-visible{outline:2px solid var(--red);outline-offset:2px}

.main{flex:1;min-width:0;display:flex;flex-direction:column}
/* The header stays put. It carries who you are and the way out now, and "I couldn't see the logout
   anymore" once the page was scrolled was the complaint that made the menu sticky in the first place
   (2026-09-14, 44:05). Moving that block up here without pinning the header would have brought the
   problem straight back. */
.top{position:sticky;top:0;z-index:20;display:flex;flex-wrap:nowrap;align-items:center;gap:12px;padding:16px 24px;background:#fff;border-bottom:1px solid var(--line)}
/* ONE LINE, ALWAYS. At the biggest text size in a laptop window this bar wrapped onto three lines and
   stood 191 pixels tall, which pushed the Long Term table's totals row clean off the bottom of the
   screen: the text size buttons exist for people who need bigger text, so costing them the totals is
   the wrong trade. The title and the name give way first, because the menu already says which screen
   you are on and the circle already says who you are. Everything else keeps its size. */
#screen-title{min-width:0;flex:0 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ONE LINE IS A LAPTOP RULE, NOT A PHONE RULE. Holding nowrap on a phone did not keep the bar on one
   line, it pushed the whole page sideways: at 385px the bar needs about 530 and the page scrolled
   with it, which has been true since the block moved up here and is not what nowrap was for. Below
   700px it wraps instead, and the screen title goes, because the menu already says which screen you
   are on. The laptop case the nowrap rule exists for, 160% text in a narrow window, is untouched. */
@media (max-width:700px){
  .top{flex-wrap:wrap;row-gap:8px}
  #screen-title{display:none}
}
.top .who-name{min-width:0;flex:0 1 auto;overflow:hidden;text-overflow:ellipsis}
.who-name{font-size:13px;font-weight:600;color:var(--ink);white-space:nowrap}
.avatar{width:30px;height:30px;flex:none;border-radius:999px;background:var(--navy2);color:#fff;
  display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;letter-spacing:.02em}
.bell{position:relative;flex:none;width:32px;height:32px;border:1px solid var(--line);border-radius:999px;
  background:#fff;color:var(--ink2);display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
.bell svg{width:19px;height:19px}
.bell:hover{background:var(--sunk);color:var(--ink)}
.bell:focus-visible{outline:2px solid var(--navy2);outline-offset:2px}
.bell-n{position:absolute;top:-4px;right:-4px;min-width:17px;height:17px;padding:0 4px;border-radius:999px;
  background:var(--red);color:#fff;font-size:11px;font-weight:700;line-height:17px;text-align:center}
.sp{flex:1}
/* flex:none and nowrap because this sits mid-bar now rather than last. Squeezed, "No clashes" broke
   onto two lines and took the whole bar to 124px at the biggest text, which is the fault the one-line
   rule exists to stop. */
.badge{font-size:12px;font-weight:700;padding:5px 11px;border-radius:20px;flex:none;white-space:nowrap}
.badge.ok{background:#e7f6ea;color:#2f7d3a}
.badge.warn{background:#fdece9;color:#c0392b}
/* No overflow here. This box never actually scrolled (the page scrolls on the window), but declaring
   overflow made it a scrolling box all the same, and a held column sticks to the nearest scrolling
   box: the details on a job page were pinned to something that never moves, so they slid away with
   the page. Measured: the panel went from 206px down the screen to 494px above it on one scroll. */
.content{padding:20px 24px}

/* tabs */
/* The bottom margin is for a tab bar standing on its own above a table. Inside a .wk-head toolbar
   it is wrong twice over: a flex row with align-items:center centres each child's MARGIN box, so a
   14px bottom margin lifts the visible control 7px above the buttons beside it, and the gap the
   margin was for is already provided by the toolbar's own gap. Measured at 7px out on the Jobs
   screen: the button's centre sat at 182 and the tabs' at 175. */
.tabs{display:inline-flex;background:var(--sunk);border-radius:10px;padding:3px;margin-bottom:14px}
.wk-head .tabs{margin-bottom:0}
.tabs button{border:none;background:none;padding:7px 16px;border-radius:8px;font:inherit;font-weight:600;color:var(--ink2);cursor:pointer}
.tabs button.on{background:var(--navy);color:#fff}

/* conflicts */
.conf{border-left:4px solid var(--red);background:#fff6f4;border-radius:10px;padding:12px 16px;margin-bottom:14px}
.conf.clean{border-left-color:#3f9d54;background:#f1f9f3}
.conf h4{margin:0 0 6px;font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:#c0392b}
.conf.clean h4{color:#2f7d3a}
.conf ul{margin:0;padding-left:18px}
.conf li{margin:3px 0}

/* tables */
.tablewrap{overflow-x:scroll;border:1px solid var(--line);border-radius:12px;background:#fff}
table{border-collapse:collapse;width:100%;font-size:13.5px;white-space:nowrap}
th,td{text-align:left;padding:12px 14px;border-bottom:1px solid var(--line)}
th{color:var(--ink2);font-weight:600;font-size:11.5px;text-transform:uppercase;letter-spacing:.03em;background:#fafbfc}
/* A week can run past the height of the screen, and the column headings scrolled away with it, so
   halfway down a long week nothing said which column was which. The wrapper scrolls in both
   directions and the heading row stays pinned to the top of it. */
.tablewrap.tall{max-height:calc(100vh - 300px);overflow-y:auto}
.tablewrap.tall thead th{position:sticky;top:0;z-index:4}
/* a grouped header is two rows: the second sits below the first, never on top of it */
.tablewrap.tall table.grouped thead tr:nth-child(2) th{top:var(--th1,38px)}
th.grp{text-align:center;border-bottom:1px solid var(--line)}
th.sub{font-size:10.5px;color:var(--ink2)}
/* EACH END OF A MOVE IN ONE CELL: the job number leads, the place sits under it. Keeping them in one
   column is what let the second row of headings go. */
.end-j{display:block;font-weight:600}
.end-s{display:block;font-size:12.5px;color:var(--ink2);line-height:1.35}
/* the pinned first column has to sit above the pinned heading row where they cross */
.tablewrap.tall .sticky1 thead th:first-child{z-index:6}
tbody tr:last-child td{border-bottom:none}
td.jb{font-weight:700}
tr.warn td{background:#fff5f3}
tr.warn td:first-child{box-shadow:inset 3px 0 0 var(--red)}
.sub-h{font-size:14px;margin:18px 0 8px;font-weight:700}

/* cards */
.grid{display:grid;gap:16px}
.jobs{grid-template-columns:repeat(auto-fill,minmax(330px,1fr))}
.res{grid-template-columns:repeat(auto-fill,minmax(220px,1fr))}
/* CREW THIS WEEK on Resource Inventory (2026-09-16 check-in, 00:13:39 to 00:14:01): names down the side grouped by trade,
   the day labels on each trade's heading row, each day showing what that person is on. */
.crewgrid-card{margin-bottom:14px}
.crewgrid{width:100%;table-layout:fixed;border-collapse:separate;border-spacing:0;margin-top:8px}
.crewgrid th,.crewgrid td{padding:3px 6px;text-align:left;vertical-align:middle;border-bottom:1px solid var(--line)}
/* The app's table defaults are for the schedule tables: capitals on a grey heading band, and a whole row that opens on
   click. Here the names read as names, only a day opens anything, and the row under the mouse is shaded name to Saturday
   to help the eye across, never the labels row. */
.crewgrid th{text-transform:none;letter-spacing:normal;background:#fff}
.crewgrid tbody tr{cursor:default}
.crewgrid tbody tr.cg-head:hover td{background:#fff}
.crewgrid tbody tr:not(.cg-head):hover th{background:#eef3f9}
.crewgrid tbody tr:last-child td{border-bottom:1px solid var(--line)}
.crewgrid .cg-cname{width:118px}
.crewgrid .cg-cn{width:62px}
.crewgrid thead.cg-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.crewgrid .cg-name{font-size:13px;font-weight:400;color:var(--ink)}
.crewgrid .cg-n{font-size:13px;font-weight:700;white-space:nowrap}
.crewgrid .cg-head th,.crewgrid .cg-head td{padding:14px 6px 5px;background:#fff;border-bottom:1px solid #cfd6df;vertical-align:bottom}
.crewgrid .cg-head th{font-size:12px;font-weight:700;color:var(--ink)}
.crewgrid .cg-head .cg-dayh{font-size:11px;font-weight:700;color:var(--ink2);white-space:nowrap}
.crewgrid .cg-head .cg-dayh span{font-weight:400}
.crewgrid .cg-head .resmeta{display:inline;margin-left:8px;font-weight:400}
.crewgrid .cg-edit{margin-left:8px;font-size:12px}
.crewgrid .cg-day{padding:2px 3px;height:30px}
.crewgrid .cg-open{display:flex;flex-wrap:wrap;gap:3px;align-items:center;width:100%;min-height:26px;padding:2px;margin:0;border:0;border-radius:6px;background:transparent;cursor:pointer;font:inherit;text-align:left}
.crewgrid .cg-open:hover{background:#f2f5f9}
.crewgrid .cg-open:focus-visible{outline:2px solid #2F4C7E;outline-offset:1px}
.cg-chip{display:inline-block;font-size:11px;font-weight:700;line-height:1.3;padding:2px 6px 2px 5px;border-radius:5px;border-left:4px solid var(--job,#9aa8ba);color:#1c2733;white-space:nowrap}
.cg-chip.cg-drive{background:#e8eef8;color:#2F4C7E;border-left-color:#2F4C7E}
.cg-chip.cg-off{background:#eef0f3;color:#4a5563;border-left-color:#8a94a3}
.cg-chip.cg-more{background:#eef0f3;border-left-color:transparent}
/* Too narrow for the days side by side: the grid keeps its shape and scrolls inside its own box instead of the page. */
@media (max-width:760px){.cgwrap{overflow-x:auto}.crewgrid{min-width:640px}}
.card{background:#fff;border:1px solid var(--line);border-radius:12px;padding:14px 15px}
.card h3{margin:0;font-size:14px}
.card .no{color:var(--red);font-weight:700;font-size:12px;margin:2px 0 8px}
.chips{display:flex;flex-wrap:wrap;gap:5px}
.chip{background:var(--sunk);border:1px solid var(--line);border-radius:6px;padding:3px 8px;font-size:11px}
.chip.eq{background:#eaf1fb;border-color:#cfe0f5;color:#2F4C7E;font-weight:600}
.kv{display:flex;justify-content:space-between;font-size:12px;padding:2px 0;color:var(--ink2)}
.kv b{color:var(--ink);font-weight:600}
.wm{position:fixed;bottom:8px;left:50%;transform:translateX(-50%);background:var(--navy);color:#ffd25a;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:4px 11px;border-radius:20px;opacity:.9}

/* editable rows + modal */
tbody tr{cursor:pointer}
tbody tr:not(.totals):hover td{background:#eef3f9}
tbody tr.warn:hover td{background:#ffe9e5}
.modal-bg{position:fixed;inset:0;background:rgba(15,32,54,.45);display:flex;align-items:center;justify-content:center;z-index:50;padding:20px;overflow:auto}
.modal{background:#fff;border-radius:14px;width:min(440px,100%);max-height:calc(100vh - 40px);display:flex;flex-direction:column;box-shadow:0 24px 60px rgba(0,0,0,.35)}
.modal-h{display:flex;align-items:center;justify-content:space-between;padding:16px 18px;border-bottom:1px solid var(--line)}
.modal-h h3{margin:0;font-size:15px}
.x{border:none;background:var(--sunk);width:28px;height:28px;border-radius:7px;cursor:pointer;color:var(--ink2);font-size:14px}
.modal-b{padding:16px 18px;display:flex;flex-direction:column;gap:11px;overflow-y:auto;min-height:0}
.modal-b label{display:flex;flex-direction:column;font-size:12px;color:var(--ink2);gap:4px;font-weight:600}
.modal-b input,.modal-b select{border:1px solid var(--line);border-radius:8px;padding:9px 10px;font:inherit;color:var(--ink)}
.modal-f{display:flex;justify-content:flex-end;gap:8px;padding:14px 18px;border-top:1px solid var(--line)}
.btn.ghost{background:#fff;color:var(--ink2);border:1px solid var(--line)}
.btn.ghost:hover{background:var(--sunk)}
.toast{position:fixed;bottom:22px;left:50%;transform:translateX(-50%) translateY(20px);background:var(--navy);color:#fff;padding:10px 18px;border-radius:10px;font-size:13px;font-weight:600;opacity:0;pointer-events:none;transition:.25s;z-index:60}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}

/* home + board */
.cards3{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:16px;margin-bottom:6px}
/* HOME'S NUMBER CARDS AND THE WEATHER. At the same width as the number cards, the forecast's seven days ran past the
   card's edge at every laptop and desktop width up to 1600 pixels, and further at a bigger text size (Ron's width
   check, 2026-09-16 check-in, 00:34:24 to 00:35:56: "that's mostly just the weather part of it"). The weather now gets
   about one and a half number cards and never less than its seven days need at their widest (320px: every day at
   100% rain with a three-figure high needs 310, plus room for Windows fonts); where the row is too narrow for that,
   it takes a row of its own under the numbers. The breaks are measured on the room Home really has, so the text
   size setting, which zooms the whole app, moves them with it. */
.homecards{container:homecards / inline-size}
.homecards .cards3{grid-template-columns:repeat(4,minmax(0,1fr)) minmax(320px,1.5fr)}
.homecards .wxcard .wxrow{justify-content:space-between}
@container homecards (max-width: 944px){
  .homecards .cards3{grid-template-columns:repeat(4,minmax(0,1fr))}
  .homecards .wxcard{grid-column:1/-1}
}
@container homecards (max-width: 640px){
  .homecards .cards3{grid-template-columns:repeat(2,minmax(0,1fr))}
}
.st-t{font-size:12px;color:var(--ink2);font-weight:600}
.st-n{font-size:30px;font-weight:800;margin:4px 0;color:var(--ink)}
.st-s{font-size:11px;color:var(--ink2)}
.wxrow{display:flex;gap:12px;margin-top:10px}
.wx{text-align:center}
.wx-e{font-size:20px}
.wx-d{font-size:11px;color:var(--ink2)}
table.board td.daycell{min-width:96px;height:46px;vertical-align:middle}
.board .bar{border-radius:6px;padding:5px 8px;font-size:11px;font-weight:600;color:#fff;text-align:center}
.board .bar.grad{background:#5b7fb8}
.board .bar.pav{background:var(--navy)}
.board .bar.both{background:linear-gradient(90deg,#5b7fb8 50%,var(--navy) 50%)}
.board .bar[draggable]{cursor:grab}
.board .bar[draggable]:active{cursor:grabbing}
.board td.daycell.drop{background:#eef3f9}
.board .jn{font-size:10px;color:var(--ink2);font-weight:400;margin-top:1px}
tr.totals td{background:#0f2036;color:#fff;font-weight:700}
tr.totals td:first-child{color:#fff}

/* per-job color + auto-progress */
.jdot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:7px;vertical-align:middle}
.board .bar{cursor:pointer}
.jobcard{cursor:pointer;transition:box-shadow .12s}
.jobcard:hover{box-shadow:0 5px 16px rgba(0,0,0,.1)}
.jc-prog{position:relative;height:16px;background:#eef0f3;border-radius:8px;overflow:hidden}
.jc-prog span{position:absolute;left:0;top:0;bottom:0;background:var(--navy);border-radius:8px}
/* The figure sits INSIDE the filled part, in Kenneth's design, so it reads as belonging to it. It
   falls back outside the bar while the fill is too narrow to hold it. */
.jc-prog em{position:absolute;top:0;line-height:16px;font-size:10px;font-style:normal;font-weight:700;color:#fff}
.jc-prog em.out{color:var(--ink)}

/* weekly toolbar */
.wk-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:12px}
.btn.sm{padding:8px 13px;font-size:12.5px}
.btn.light{background:#fff;color:var(--ink);border:1px solid var(--line)}
.btn.light:hover{background:var(--sunk)}

/* job detail drawer */
.drawer-bg{position:fixed;inset:0;background:rgba(15,32,54,.4);z-index:55;display:flex;justify-content:flex-end}
/* 420px for a panel carrying the plan, the materials and every scheduled day was a column of
   wrapped text with a long scroll. It is wide enough for two columns now, and still full width on a
   phone. The day-by-day list is the bulk of it, so it gets its own column beside the summary. */
.drawer{width:min(980px,94vw);max-width:100%;background:#fff;height:100%;overflow:auto;box-shadow:-12px 0 40px rgba(0,0,0,.28);position:relative}
.drawer .x{position:absolute;top:14px;right:14px}
.dh{padding:22px 22px 18px;border-bottom:1px solid var(--line)}
.dh .jt{color:var(--red);font-weight:700;font-size:12px}
.dh h2{margin:2px 0 0;font-size:19px}
.db{padding:8px 22px 24px}
.dcol{min-width:0}
/* The first heading in a column must not push it down past its neighbour. */
.dcol>.sec:first-child{margin-top:6px}
/* The two columns and the panel's own right edge were all about 22 to 28px, and the values in the
   right column are right-aligned, so they sat hard against the edge with the drawer's scrollbar over
   them ("we need to increase the padding here its too close", GM, 2026-09-30). The right column now
   carries 32px on each side of its content. The left stays 22px because that is what lines the days
   up under the job number and name in the header; only the right side had the problem. */
/* The two columns, and the one line between them, are set up at the foot of this file where the whole
   panel becomes a fixed frame. Above 820px the heading is part of the left column rather than a band
   across the top, so the grid lives on the panel body and not on .db. */
/* Room for the close button so a long job name never runs under it. */
.dh{padding-right:56px}
/* Both columns, because the details are no longer nested inside .db. */
.db .sec,.dc-facts .sec{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--ink2);margin:16px 0 6px;font-weight:700}
/* "this one needs to be bigger, the grading ... the category name needs to be bigger than the title
   here. That way, it's easier to understand" (the design lead, 2026-09-30, circling GRADING and the
   day heading under it). It was the smaller of the two: 11px against the day's 14px. Only the word
   over a run of days grows. The labels in the right-hand column are a different thing, labelling a
   list rather than grouping cards, so they stay as they are. It stays uppercase, spaced and quiet, so
   it still reads as a group title and never competes with the job name at the top. */
.dc-days .sec{font-size:15px;letter-spacing:.04em;color:var(--ink)}
.drow{border-left:3px solid var(--line);padding:4px 0 6px 10px;margin-bottom:9px}
.drow-d{font-weight:700;font-size:12px}
.drow-m{font-size:12px;margin-top:1px}
.drow-s{font-size:11.5px;color:var(--ink2);margin-top:1px}
.drow-s.note{color:#8a5a00;background:#fff8ea;border-radius:6px;padding:3px 7px;display:inline-block;margin-top:4px}

/* ---- punch-list additions (tonnage/crew/tentative/notes, rosters, codes, long-term nav) ---- */
.muted{color:var(--ink2);font-size:12px}
.two{display:flex;gap:10px;flex-wrap:wrap}
.two>label{flex:1 1 150px;min-width:0}
.modal-b textarea{border:1px solid var(--line);border-radius:8px;padding:9px 10px;font:inherit;color:var(--ink);resize:vertical}
.modal-b .chk{flex-direction:row;align-items:center;gap:8px;font-weight:600;color:var(--ink);cursor:pointer}
.modal-b .chk input{width:auto}
/* click-to-add crew */
.crew-pick{display:flex;flex-wrap:wrap;gap:6px;border:1px solid var(--line);border-radius:8px;padding:8px;background:var(--sunk);max-height:min(46vh,300px);overflow-y:auto;align-content:flex-start}
.crewchip{border:1px solid var(--line);background:#fff;border-radius:14px;padding:4px 11px;font:inherit;font-size:12px;color:var(--ink2);cursor:pointer}
.crewchip:hover{border-color:#b7c6d8}
.crewchip.on{background:#183256;border-color:#183256;color:#fff;font-weight:600}
.crewchip.off{border-style:dashed}
.crewchip.off.on{background:#5a4a2a;border-color:#5a4a2a}
.modal-b .fld{display:flex;flex-direction:column;gap:4px}
.field-cap{font-size:12px;color:var(--ink2);font-weight:600}
/* tentative rows (amber; red conflicts still win via :not(.warn)) */
tr.tent:not(.warn) td{background:#fffaf0}
tr.tent:not(.warn) td:first-child{box-shadow:inset 3px 0 0 #E6A417!important}
tbody tr.tent:not(.warn):hover td{background:#fff4dc}
.tag-t{display:inline-block;font-size:9.5px;text-transform:uppercase;letter-spacing:.04em;font-weight:700;color:#8a5a00;background:#ffeccb;border:1px solid #f0d38f;border-radius:5px;padding:1px 5px;vertical-align:middle}
.noteflag{cursor:help;font-size:12px}
/* resource inventory extras */
.card h3 .ct{font-size:11px;color:var(--ink2);font-weight:600;background:var(--sunk);border-radius:10px;padding:1px 7px;margin-left:4px}
.card.wide{grid-column:1/-1}
.codes{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:6px 16px}
.crow{display:flex;align-items:center;gap:8px;font-size:12px}
.crow b{color:var(--ink);font-weight:600}
/* long-term month navigation */
.lt-nav{display:flex;align-items:center;gap:12px;margin:0 0 12px}
.loading{color:var(--ink2);font-size:13px;padding:40px 4px}
/* header search + frozen first column on wide tables */
/* It may shrink. A hard 180px floor made the bar wider than a phone screen once the order changed
   and the search stopped being the only thing with room to its right. */
.topsearch{border:1px solid var(--line);border-radius:8px;padding:7px 11px;font:inherit;font-size:13px;flex:0 1 180px;min-width:0;max-width:280px;background:#fff;color:var(--ink)}
/* Microsoft sign-in button + the "or" divider above the temporary login */
.msbtn{display:flex;align-items:center;justify-content:center;gap:10px;text-decoration:none;text-align:center}
.gbtn{margin-top:0}
.glogo{width:18px;height:18px;flex:none;border-radius:50%;background:#fff;border:1px solid var(--line);color:#4285f4;font-weight:800;font-size:12px;line-height:16px;text-align:center}
.mslogo{width:16px;height:16px;flex:none;background:
  linear-gradient(#f25022,#f25022) 0 0/7px 7px no-repeat,linear-gradient(#7fba00,#7fba00) 9px 0/7px 7px no-repeat,
  linear-gradient(#00a4ef,#00a4ef) 0 9px/7px 7px no-repeat,linear-gradient(#ffb900,#ffb900) 9px 9px/7px 7px no-repeat}
.or{display:flex;align-items:center;gap:10px;color:var(--ink2);font-size:11px;margin:4px 0}
.or::before,.or::after{content:"";flex:1;border-top:1px solid var(--line)}
#demo-login{display:flex;flex-direction:column;gap:10px}
.sticky1 thead th:first-child{position:sticky;left:0;z-index:3;background:#fafbfc}
.sticky1 tbody td:first-child{position:sticky;left:0;z-index:1;background:#fff;box-shadow:2px 0 0 rgba(0,0,0,.05)}
.sticky1 tbody tr:not(.totals):hover td:first-child{background:#eef3f9}
/* jobs + job editor (connected job records) */
.btn.sm{padding:8px 12px;font-size:12px;margin-top:0}
.chk.sm{flex-direction:row;align-items:center;gap:6px;font-size:12px;color:var(--ink2);font-weight:600;cursor:pointer}
.chk.sm input{width:auto}
.jbadge{display:inline-block;font-size:9.5px;text-transform:uppercase;letter-spacing:.04em;font-weight:700;border-radius:5px;padding:1px 6px;vertical-align:middle}
.jbadge.completed{background:#e6f4ea;color:#1e7a3d;border:1px solid #b7e0c4}
/* was #6b7a8b, which measures 3.81:1 on this background and fails AA at any size. This grey is
   5.07:1 and reads as the same neutral badge; the Completed badge beside it is already 4.73:1. */
.jbadge.archived{background:#eceff3;color:#5a6675;border:1px solid #d7dee6}
.jobcard .jc-h{cursor:pointer}
.jc-act{display:flex;gap:12px;margin-top:10px;border-top:1px solid var(--line);padding-top:8px}
.jc-act .link{font-size:12px;color:#2F4C7E;font-weight:600;text-decoration:none}
.jc-act .link:hover{text-decoration:underline}
.modal.wide{width:min(980px,100%)}
.three{display:flex;gap:10px;flex-wrap:wrap}
.three>label{flex:1 1 120px;min-width:0}
.tons-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:8px 12px}
tbody tr[data-editrow]{cursor:pointer}
tbody tr[data-editrow]:hover td{background:var(--sunk)}

.wknav{margin:0 0 10px}
.wknav .tag-t{vertical-align:middle}
.wx-t{font-size:12px;color:var(--ink2);font-weight:600}
.wx-r{font-size:11px;color:#2F4C7E}
.wx.wet{background:#eef3fb;border-radius:8px}
.statcols{display:grid;grid-template-columns:1fr 1fr;gap:22px}
@media (max-width:720px){.statcols{grid-template-columns:1fr}}
.crewchip.busy{border-color:#d98324;color:#8a4b00}
.crewchip.busy.on{color:#ffc46b;border-color:#d98324}
.clashnote{background:#fff4e5;border:1px solid #f0c48a;color:#7a4a06;border-radius:8px;padding:8px 10px;font-size:12px;margin-top:6px}
/* The "already booked" note beside a booked name or machine, drawn by the app so it shows at once
   (2026-09-16). No fade: a fade is a wait by another name. It never takes a click from the buttons. */
.chiptip{position:fixed;z-index:60;max-width:calc(100vw - 16px);padding:6px 10px;border-radius:7px;background:#0f2036;color:#fff;font-size:12px;font-weight:600;line-height:1.35;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;box-shadow:0 6px 18px rgba(15,32,54,.28);pointer-events:none}
.pdates{white-space:nowrap;font-size:12px}
.pdates .tag-t{margin-left:4px}
.tag-t.late{background:#fff4e5;color:#8a4b00;border-color:#f0c48a}
/* Lined up along the TOP: From and To carry their date written under the box, so lining up the
   bottoms pushed those two boxes above the others. The button drops by the caption above each box
   (a 12px line and the 4px gap) to sit level with them. */
.avform{display:flex;gap:10px;align-items:flex-start;flex-wrap:wrap;margin-top:12px}
.avform>.btn{margin-top:21px}
.avform label{display:flex;flex-direction:column;font-size:12px;color:var(--ink2);gap:4px}
.avform input{padding:6px 8px;border:1px solid var(--line);border-radius:6px;font:inherit}
.tag-d{display:inline-block;font-size:10px;text-transform:uppercase;letter-spacing:.04em;padding:1px 5px;border-radius:999px;background:#e8f5ec;color:#1c6b3a;border:1px solid #b7e0c5}
.tag-today{display:inline-block;font-size:10px;text-transform:uppercase;letter-spacing:.04em;padding:1px 5px;border-radius:999px;background:#e8eef8;color:#2F4C7E;border:1px solid #bccbe4;margin-left:2px}
/* The side panel's link to the days already past. The base .link grey is made for the dark menu. */
.drawer .dt-earlier-btn{display:block;color:#2F4C7E;font-weight:600;margin:2px 0 10px}
/* A day in the job panel: each figure on its own labelled line, at the app's normal 14px. Scoped to the panel,
   so the Weekly board's day pop-up, which shares the row classes, keeps its own compact look. */
.drawer .drow-d,.drawer .drow-m,.drawer .drow .drow-s.note{font-size:14px}
.drawer .drow{padding-bottom:8px;margin-bottom:12px}
.dlist{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:1px 12px;margin-top:3px;font-size:14px}
.dl{display:contents}
.dl-k{color:var(--ink2)}
.dl-v{color:var(--ink);overflow-wrap:anywhere}
/* A day in the job panel opens itself in the Weekly table, and says so. */
.drawer .drow[data-openday]{cursor:pointer;border-radius:0 8px 8px 0}
.drawer .drow[data-openday]:hover{background:#f6f8fb}
.drawer .drow[data-openday]:focus-visible{outline:2px solid #2F4C7E;outline-offset:2px}
.drow-open{margin-top:4px;font-size:14px;font-weight:600;color:#2F4C7E}
/* The Weekly row a day in the job panel opened, picked out for a moment so the eye lands on it. The animation
   ends on the row's own background, so done and flagged rows keep their colour afterwards. */
@keyframes rowflash{0%,55%{background-color:#fff1b8}100%{background-color:rgba(255,241,184,0)}}
tr.flash td{animation:rowflash 2.6s ease-out}
/* A FINISHED DAY SITS BACK, IT DOES NOT GLOW. This was a pale green, and the design lead asked why
   ("we need to investigate why this is green ... might be a bug", 2026-09-24). The green was not the
   bug, but it was the wrong colour for the job: the eye belongs on what still needs doing, and
   finished work was the only thing on the page wearing one. Green also already means no clashes on
   this same screen, so it was carrying two meanings at once, and at that paleness it carried neither
   (painted across a whole row it was all but invisible). This is the app's own colour for a surface
   that sits back, so nothing new enters the palette, and the DONE tag keeps the word: colour never
   carries the meaning on its own. */
tr.done td{background:var(--sunk)}

/* Phase 6: the selection column and the actions bar. The bar only carries buttons while something is
   selected, so a destructive action is never sitting there applying to nothing. */
th.pick,td.pick{width:34px;padding-left:14px;padding-right:0}
td.pick input,th.pick input{width:16px;height:16px;cursor:pointer;accent-color:var(--navy)}
.selbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:10px 0 0;padding:10px 14px;border:1px solid var(--line);border-radius:12px;background:#fff;font-size:13px}
.selbar.muted{color:var(--ink2);background:var(--sunk)}
.selbar.on{border-color:var(--navy);box-shadow:inset 3px 0 0 var(--navy)}
.btn.danger{color:var(--red);border-color:var(--red)}
.btn.danger:hover{background:#fff1ee}
tbody tr.picked td{background:#eef3f9}
.viewnote{font-size:12px;color:var(--ink2);margin:0 0 10px}

/* Phase 8.1: the read-only week Gantt. One bar per unbroken run of booked days, so a gap in a job
   reads as a gap rather than being papered over into one long bar. */
/* SEVEN EQUAL COLUMNS, AND BARS THAT LAND ON THEIR DAY'S EDGES. "Gun Chart needs to have seven
   columns ... and it needs to be fixed ... this one would be align left, but the rest would be
   center ... the start date needs to snap on the leftmost of that specific column ... if it's on the
   end date, it needs to snap on the right side" (the design lead, 2026-09-24). The columns used to
   size themselves to whatever was in them, so the day names above sat nowhere near the ends of the
   bars below, and each cell's side padding held a bar back from its own edges. The job column keeps
   a fixed width and the seven days divide what is left, equally, whatever the week holds. */
table.gantt{table-layout:fixed;width:100%}
table.gantt th:first-child,table.gantt td.gjob{width:240px;text-align:left}
table.gantt thead th+th{text-align:center}
table.gantt td.gcell{padding:6px 0;vertical-align:middle}
table.gantt .gbar{border-radius:6px;padding:7px 10px;font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer}
table.gantt .gbar:focus-visible{outline:2px solid var(--navy);outline-offset:2px}

.selbar .warnnote{color:var(--red);font-weight:600}

/* ---- wide dialogs lay out in two columns when the screen has room ---------------------------- */
.modal-b.split{gap:11px}
.mcol{display:flex;flex-direction:column;gap:11px;min-width:0}
@media (min-width:900px){
  .modal-b.split{flex-direction:row;gap:24px;align-items:flex-start}
  .modal-b.split>.mcol{flex:1 1 0}
}

/* ---- a schedule row's note, on its own line directly beneath it ------------------------------ */
/* Always visible. It reads as attached to the row above rather than as a row of its own: no top
   border, the same background as its parent when that parent is picked, tentative or done. */
.noterow>td{border-top:0;padding:0 12px 9px 12px;cursor:pointer}
.noterow .nmark{color:#7a4a06;margin-right:6px;font-weight:700}
.noterow .ntext{color:#7a4a06;background:#fff8ea;border:1px solid #f2dcb4;border-radius:6px;
  padding:3px 9px;display:inline-block;font-size:12.5px;line-height:1.35}
.noterow:hover .ntext{border-color:#d98324}
.noterow:focus-visible{outline:2px solid var(--navy2);outline-offset:-2px}
/* the note belongs to the row above it, so it takes that row's colour. `inherit` did not work here:
   it reads the note row's own parent, not the sibling it is attached to. */
tr.picked+.noterow>td{background:#eef3f9}
tr.tent:not(.warn)+.noterow>td{background:#fffaf0}
tr.done+.noterow>td{background:var(--sunk)}
tr.picked.tent+.noterow>td,tr.picked.done+.noterow>td{background:#eef3f9}

/* ---- the totals row stays in view once a list is long enough to scroll -------------------- */
/* It lives in a real <tfoot>. As the last row of <tbody> it could not lift off the bottom: a sticky
   box may not leave its containing block, and the table ended exactly where that row sat. */
.tablewrap.tall tfoot tr.totals td{position:sticky;bottom:0;z-index:3}
.sticky1 tfoot td:first-child{position:sticky;left:0;z-index:4}

/* ---- one Download control with its choices underneath it ------------------------------------- */
.menu{position:relative;display:inline-block}
.menu-list{position:absolute;top:calc(100% + 4px);right:0;z-index:20;background:#fff;
  border:1px solid var(--line);border-radius:8px;box-shadow:0 10px 26px rgba(0,0,0,.16);
  padding:4px;min-width:150px;display:flex;flex-direction:column}
.menu-list button{background:none;border:0;text-align:left;padding:8px 12px;border-radius:6px;
  font:inherit;font-size:13px;color:var(--ink);cursor:pointer}
.menu-list button:hover,.menu-list button:focus-visible{background:var(--sunk)}
/* the whole job card is the target now, so it needs the affordance the title used to carry */
.jobcard[role="button"]{cursor:pointer}
.jobcard[role="button"]:hover{border-color:#b7c6d8}
.jobcard[role="button"]:focus-visible{outline:2px solid var(--navy2);outline-offset:2px}
.jobcard .jc-act button{cursor:pointer}

/* the day is read from the date, so it is shown, not chosen */
.daynote{font-size:12px;color:var(--ink2);font-weight:600;margin-top:3px;min-height:15px}

/* ---- Gantt: unsaved drags ---------------------------------------------------------------- */
.draftbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;background:#fff8ea;
  border:1px solid #f2dcb4;border-radius:10px;padding:10px 12px;margin:0 0 10px;font-size:13px;color:#5c3d05}
.draftbar .dtag{background:#8a5f14;color:#fff;border-radius:5px;padding:2px 8px;font-size:11px;
  font-weight:700;text-transform:uppercase;letter-spacing:.04em}
.gbar.movable{cursor:grab}
.gbar.movable:active{cursor:grabbing}
.gbar.movable:focus-visible{outline:2px solid #0f2036;outline-offset:2px}
.gantt td.gcell.drop-on{background:#eef3f9;outline:2px dashed #b7c6d8;outline-offset:-3px}

/* progress set by hand, with what the recorded work says beside it */
.progpick{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:2px 0 4px}
.progpick select{border:1px solid var(--line);border-radius:8px;padding:7px 9px;font:inherit;font-size:13px;color:var(--ink);background:#fff}

/* the count that sits inside a list toggle */
.tabs button .ct{background:rgba(255,255,255,.18);border-radius:20px;padding:1px 7px;font-size:11px;margin-left:5px}
.tabs button:not(.on) .ct{background:var(--line);color:var(--ink2)}

/* ---- a job's own page ------------------------------------------------------------------------ */
.jobhead{background:#fff;border:1px solid var(--line);border-radius:12px;padding:18px 20px;margin-bottom:6px}
.jobhead .jt{color:var(--red);font-weight:700;font-size:12px}
.jobhead h2{margin:2px 0 10px;font-size:21px}
.jobmeta{display:flex;gap:22px;flex-wrap:wrap}
/* .kv is built for a card column, where space-between does the separating. In a row it collapses,
   so the label and its value ran together as "SourceUS Agg-Cloverdale". */
.jobmeta .kv{display:flex;gap:8px;align-items:baseline;border:0;padding:0}
.jnotes{display:flex;flex-direction:column;gap:8px}
.jnote{background:#fff;border:1px solid var(--line);border-left:3px solid #d98324;border-radius:8px;padding:9px 12px;cursor:pointer}
/* A job-level note is shown, not clickable: the only editor a click could open has no notes
   field. It must not look or behave like the row notes beside it. */
.jnote.static{cursor:default}
.jnote.static:hover{background:inherit}
.jnote:hover{border-color:#b7c6d8;border-left-color:#d98324}
.jnote:focus-visible{outline:2px solid var(--navy2);outline-offset:2px}

/* ---- sortable headings, and the job filter ---------------------------------------------------- */
th.sortable{cursor:pointer;user-select:none}
th.sortable:hover{color:var(--ink)}
th.sortable:focus-visible{outline:2px solid var(--navy2);outline-offset:-2px}
th.sortable.sorted{color:var(--ink)}
.filt{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--ink2);font-weight:600}
.filt select{border:1px solid var(--line);border-radius:8px;padding:7px 9px;font:inherit;font-size:13px;color:var(--ink);background:#fff}

/* sample data is never mistaken for the real schedule */
.demobar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;background:#eef3fb;border:1px solid #cfe0f5;
  border-radius:10px;padding:9px 12px;margin:0 0 12px;font-size:13px;color:#22405f}
.demobar .dtag{background:#2F4C7E;color:#fff;border-radius:5px;padding:2px 8px;font-size:11px;font-weight:700;
  text-transform:uppercase;letter-spacing:.04em}

/* Pick-or-type box (see enhanceCombos in app.js). The panel is position:fixed because .modal-b
   scrolls, and an absolutely positioned list inside it gets clipped at the first overflow edge. */
.combo-wrap{position:relative;display:block}
.combo-wrap>input{padding-right:30px;width:100%}
.combo-btn{position:absolute;right:1px;top:1px;bottom:1px;width:26px;border:none;background:transparent;
  cursor:pointer;color:var(--ink2);font-size:10px;line-height:1;border-radius:0 8px 8px 0}
.combo-btn:hover{background:var(--sunk);color:var(--ink)}
.combo-pop{position:fixed;z-index:200;background:#fff;border:1px solid var(--line);border-radius:10px;
  box-shadow:0 16px 40px rgba(0,0,0,.22);max-height:264px;overflow-y:auto;padding:4px;min-width:160px}
.combo-item{padding:8px 10px;border-radius:7px;cursor:pointer;font-size:14px;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.combo-item:hover,.combo-item.active{background:var(--sunk)}
.combo-item.on{font-weight:600}
.combo-note{padding:8px 10px;color:var(--ink2);font-size:12.5px}

/* Long Term: a completed or archived job must not read as ongoing when "Show completed / archived"
   is on. Tint only the background: the text keeps its normal colour so nothing gets harder to read. */
.sticky1 tbody tr.lt-completed>td{background:#f2faf5}
.sticky1 tbody tr.lt-archived>td{background:#f3f5f8}
.sticky1 tbody tr.lt-completed>td.jb{box-shadow:inset 4px 0 0 #1e7a3d}
.sticky1 tbody tr.lt-archived>td.jb{box-shadow:inset 4px 0 0 #5a6675}

/* Gantt: grips for making a job run longer or shorter. */
.gbar{position:relative}
.glabel{pointer-events:none}
.ghandle{position:absolute;top:0;bottom:0;width:10px;cursor:ew-resize;opacity:0;background:rgba(0,0,0,.28);
  border-radius:4px;transition:opacity .12s}
.gh-s{left:0} .gh-e{right:0}
/* Faintly visible at rest so the bar LOOKS resizable, and obvious on hover. */
.ghandle{opacity:.18}
.gbar:hover .ghandle,.gbar:focus-within .ghandle,.gbar:focus .ghandle{opacity:.55}
.ghandle:hover{opacity:.9!important}
body.gresizing{cursor:ew-resize;user-select:none}

/* Board: one chip per task, so a day that holds two pieces of work looks like two. */
.daycell{vertical-align:top}
.daycell .bar{display:block;margin-bottom:4px;font-size:11.5px;line-height:1.35;padding:5px 8px;white-space:normal}
.daycell .bar b{font-weight:700;opacity:.85}
.moretasks{display:block;width:100%;border:1px dashed var(--line);background:transparent;border-radius:7px;
  padding:3px 8px;font:inherit;font-size:11px;color:var(--ink2);cursor:pointer}
.moretasks:hover{background:var(--sunk);color:var(--ink)}
.dayhead{border:none;background:transparent;font:inherit;color:inherit;cursor:pointer;padding:0;text-transform:inherit;letter-spacing:inherit}
.dayhead:hover{text-decoration:underline}

/* Gantt: a tick under any day carrying more than one task for that job. */
.gtick{position:absolute;bottom:2px;width:5px;height:3px;border-radius:2px;background:currentColor;opacity:.75;
  transform:translateX(-50%);pointer-events:none}

/* One day, everything on it. */
#daymodal .modal{width:min(720px,100%)}
.dayrow{cursor:pointer;border-radius:0 7px 7px 0}
.dayrow:hover{background:var(--sunk)}
.dayrow:focus{outline:2px solid var(--ink);outline-offset:2px}

/* While an end is being dragged, the bar itself follows the cursor. It is lifted slightly so it
   reads as being handled, and it overlaps its neighbours rather than being clipped by them. */
.gbar.resizing{box-shadow:0 6px 18px rgba(0,0,0,.3);opacity:.92;transition:none}
.gbar.resizing .ghandle{opacity:.9}

/* "you've selected grade one": what the ticked machines add up to. */
.groupnote{margin-top:6px;font-size:12px;color:#1e7a3d;background:#e6f4ea;border:1px solid #b7e0c4;
  border-radius:7px;padding:5px 9px}

/* Resource Inventory: a card is read-only until you press Edit. */
.card.editing{outline:2px solid var(--ink);outline-offset:2px}
.cardacts{display:flex;gap:10px;align-items:center;margin-top:10px;flex-wrap:wrap}
.chip.on{padding-right:4px}
.chipx{border:none;background:transparent;cursor:pointer;color:inherit;opacity:.6;font-size:9px;padding:0 4px;line-height:1}
.chipx:hover{opacity:1;color:var(--red)}
.addrow{display:flex;gap:8px;margin-top:10px}
.addrow input{flex:1;min-width:0;border:1px solid var(--line);border-radius:8px;padding:7px 9px;font:inherit}
.card.warn{border-color:#f0dcb4;background:#fffdf7}

/* A note about the job, written where notes are read. */
.notebox{background:#fff;border:1px solid var(--line);border-radius:10px;padding:12px 14px;margin-bottom:10px}
.notebox textarea{width:100%;border:1px solid var(--line);border-radius:8px;padding:9px 10px;font:inherit;
  color:var(--ink);resize:vertical;margin-top:6px}
.notebox .field-cap{display:block}

/* Jobs card, following Kenneth's Adobe XD design: name and number, an open arrow, Location and
   Quarry side by side, then the five materials as columns with tonnage and days. */
.jobcard{display:flex;flex-direction:column;gap:12px}
.jobcard .jc-h{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
.jc-t h3{margin:0;font-size:15px;line-height:1.25}
.jobcard .no{font-size:12px;color:var(--ink2);margin-top:3px}
.jc-open{flex:none;width:26px;height:26px;border-radius:6px;background:var(--ink);color:#fff;
  display:flex;align-items:center;justify-content:center;font-size:13px;line-height:1}
.jobcard:hover .jc-open{background:var(--red)}
.jc-meta{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.jc-meta b{display:block;font-size:11.5px;font-weight:500;line-height:1.35;margin-top:2px}
.mh{font-size:10.5px;font-weight:700;color:var(--ink2);letter-spacing:.02em}
.jc-mat{display:grid;grid-template-columns:repeat(5,1fr);gap:6px}
.jc-mat .m{min-width:0}
.jc-mat .m{min-width:0;overflow:hidden}
.jc-mat .mh{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.jc-mat b{display:block;font-size:11px;font-weight:600;margin-top:3px;line-height:1.25;
  overflow-wrap:anywhere}
.jc-mat i{display:block;font-size:10.5px;font-style:normal;color:var(--ink2);margin-top:1px;
  overflow-wrap:anywhere}
.jc-none{margin:0}

/* The bar across the top of a table, from Kenneth's design: what you are looking at on the left,
   which period on the right with a round arrow either side of it. */
.sectionbar{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;
  background:var(--sunk);border:1px solid var(--line);border-radius:12px;padding:12px 18px;margin-bottom:12px}
.sb-t{margin:0;font-size:17px;font-weight:700;color:var(--ink)}
.sb-r{display:flex;align-items:center;gap:10px}
.sb-p{font-size:17px;font-weight:700;color:var(--ink)}
.rnd{width:32px;height:32px;border-radius:50%;border:1.5px solid var(--ink);background:transparent;
  color:var(--ink);cursor:pointer;font-size:15px;line-height:1;display:flex;align-items:center;justify-content:center}
.rnd:hover:not(:disabled){background:var(--ink);color:#fff}
.rnd:disabled{opacity:.35;cursor:default}
.resnote{margin-top:10px}
.resnote.unsaved{background:#fff8ea;border:1px solid #f0dcb4;border-radius:8px;padding:7px 10px;color:#8a5a00}
.resnote .dtag{margin-right:6px}

/* ---- the sideways scroll bar is visible, and the totals row is not jammed to the edge -------- */
/* "Binder days is being covered. Is there a way to actually side-scroll? The indicator for
   side-scrolling needs to be down below. That way it's easier for Holly to see where the scroll
   bars are" (2026-09-10, 13:34). macOS hides overlay scroll bars until something is scrolled, so a
   table that runs off the right edge looked like a table that simply lost its last columns. These
   are given a real, permanently visible bar. */
/* THE BAR IS DRAWN BY THE RULES BELOW, AND THE STANDARD PROPERTIES MUST NOT BE SET ALONGSIDE THEM.
   Chrome ignores every ::-webkit-scrollbar rule on an element that also sets `scrollbar-width` or
   `scrollbar-color`. Both were set here, so the custom bar was switched off and the element fell
   back to the macOS overlay bar, which is invisible until something is scrolled. Measured: with the
   two standard properties the element reserves 0px for a bar; without them it reserves 12px. That is
   why the slider still could not be seen after it was asked for the first time.
   Firefox, which has no ::-webkit-scrollbar, gets them through the @supports block further down. */
.tablewrap::-webkit-scrollbar{height:12px;width:12px;-webkit-appearance:none}
.tablewrap::-webkit-scrollbar-track{background:#eef0f3;border-radius:0 0 12px 12px}
.tablewrap::-webkit-scrollbar-thumb{background:#9aa8ba;border-radius:6px;border:2px solid #eef0f3}
.tablewrap::-webkit-scrollbar-thumb:hover{background:#7d8da2}
@supports not selector(::-webkit-scrollbar){
  .tablewrap{scrollbar-width:auto;scrollbar-color:#9aa8ba #eef0f3}
}
/* THE TOTALS ROW SITS FLUSH ON THE SCROLL BAR, WITH NO GAP BETWEEN THEM.
   It used to be lifted 12px to leave room for the bar, which was right while the bar was one of the
   Mac's overlay ones: those float over content and reserve nothing. The bar is a real one now and
   reserves its own 12px, so the lift was counted twice and left a strip of live content under the
   Totals row with the list showing through it ("at 125% overlap", 2026-09-12).
   A sticky bottom is measured against the scrollport, which already excludes the scroll bar, so 0
   puts the row straight on top of it. Measured at 125%, at six scroll positions: with the lift, a
   14px strip carrying 918 ink pixels of the row beneath; with 0, no strip at any of them. */
.tablewrap.tall tfoot tr.totals td{bottom:0}

/* ---- the totals row is a summary laid OVER the list, not the next row down -------------------
   "Too close" (2026-09-12): the last job on the list ran straight into the totals bar with a single
   hairline between them, so the eye read them as two rows of the same list and the last figure and
   the total of that column sat on top of each other.
   Three changes, and they only work together:
     1. CLEARANCE under the last row, inside the scrolling area, so reaching the bottom of the list
        still leaves a gap before the summary instead of ending flush against it.
     2. A LIP above the bar. The row is pinned, so the list passes underneath it; with nothing to
        cast a shadow there was no way to tell that from the list simply stopping.
     3. A little more HEIGHT on the bar itself. It carries the total of every column and had exactly
        the weight of one row of data, which is the wrong weight for the one row you look for. */
.tablewrap.tall tbody tr:last-child td{padding-bottom:26px}
.tablewrap.tall tfoot tr.totals td{box-shadow:0 -10px 14px -8px rgba(15,32,54,.34)}
tr.totals td{padding-top:15px;padding-bottom:15px}
/* The header is pinned for the same reason and had the same problem at the other end: rows slid
   under it and looked cut off rather than continuing. The two lips are deliberately a matched pair.
   The held columns cast their own shadow sideways (.frz-edge), so the corner cells list both and
   neither one wins. */
.tablewrap.tall thead th{box-shadow:0 10px 14px -8px rgba(15,32,54,.22)}
/* The pinned header and the pinned Totals row each carry their own lift as well as the edge rule. */
.tablewrap.tall .sticky1 thead th.frz-edge{box-shadow:inset -2px 0 0 0 #8fa3ba,0 10px 14px -8px rgba(15,32,54,.22)}
.tablewrap.tall .sticky1 tfoot tr.totals td.frz-edge{box-shadow:inset -2px 0 0 0 rgba(255,255,255,.5),0 -10px 14px -8px rgba(15,32,54,.34)}
/* "It's also very close to the edge. If you could add a bit of padding inside, like a space"
   (2026-09-10, 04:55): the totals row sat flush against the bottom of the window. */
.tablewrap.tall{max-height:calc(100vh - 330px)}  /* replaced at runtime by fitTallTables */
#content{padding-bottom:28px}

/* Text size, for anyone who finds the default too small. */
.textsize{display:flex;align-items:center;gap:2px;border:1px solid var(--line);border-radius:999px;
  padding:2px;background:#fff;flex:none}
.textsize button{border:none;background:transparent;cursor:pointer;font:inherit;font-weight:700;
  font-size:12px;color:var(--ink);width:28px;height:24px;border-radius:999px;line-height:1}
.textsize button:hover:not(:disabled){background:var(--sunk)}
.textsize button:disabled{opacity:.3;cursor:default}
.textsize span{font-size:11px;color:var(--ink2);min-width:34px;text-align:center}

/* ---- how far along a job is ------------------------------------------------------------------ */
/* Was a full-width black band with the figure squeezed into its left end. Now it names what it is
   measuring, puts the number where the eye lands, keeps the track short enough to read at a glance,
   and says underneath where the figure came from. */
.prog{max-width:420px;margin-top:12px}
.prog-h{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
.prog-l{font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink2)}
.prog-n{font-size:20px;font-weight:700;line-height:1;color:var(--ink);font-variant-numeric:tabular-nums}
.prog-t{position:relative;height:8px;border-radius:999px;background:#e7ebf0;overflow:hidden;margin-top:6px}
.prog-t span{position:absolute;left:0;top:0;bottom:0;border-radius:999px;background:var(--navy);
  transition:width .25s ease}
.prog.done .prog-t span{background:#1e7a3d}
.prog.done .prog-n{color:#1e7a3d}
.prog.none .prog-t span{background:#c3ccd8}
.prog-f{margin-top:6px;font-size:11.5px;color:var(--ink2)}
.prog.compact{margin-top:8px}
.prog.compact .prog-n{font-size:15px}

/* ---- job page: what the job is on the left, what is booked on the right ---------------------- */
.jobsplit{display:block}
.jobmain{min-width:0}
.jobside{display:flex;flex-direction:column;gap:12px;margin-top:18px}
@media (min-width:1180px){
  .jobsplit{display:grid;grid-template-columns:340px minmax(0,1fr);gap:20px;align-items:start;margin-top:18px}
  .jobside{margin-top:0;position:sticky;top:calc(var(--topH,64px) + 12px);max-height:calc(100vh - var(--topH,64px) - 24px);overflow:auto}
}
.notecard{display:flex;flex-direction:column;gap:10px}
.notecard-h{display:flex;align-items:center;justify-content:space-between;gap:10px}
.jnotes{display:flex;flex-direction:column;gap:8px}
.jnote{display:flex;align-items:center;justify-content:space-between;gap:10px;border:1px solid var(--line);
  border-radius:10px;padding:10px 12px;background:#fff}
.jnote:not(.static){cursor:pointer}
.jnote:not(.static):hover{border-color:#b7c6d8;background:var(--sunk)}
.jnote-b{min-width:0}
.jnote .ntext{font-size:13px;font-weight:600;line-height:1.35;color:var(--ink)}
.jnote .drow-s{margin-top:2px}
.jgo{flex:none;width:26px;height:26px;border-radius:50%;border:1.5px solid var(--ink);color:var(--ink);
  display:flex;align-items:center;justify-content:center;font-size:13px;line-height:1}
.jnote:hover .jgo{background:var(--ink);color:#fff}
/* A mix she built herself reads differently from one of their presets. */
.groupnote.mix{color:#8a5a00;background:#fff8ea;border-color:#f0dcb4}

/* ---- the columns that stay put while the rest scrolls ---------------------------------------- */
/* "The ones that need to be frozen are the job number, the date, the start, and end, and then the
   shift, and then the job. And then the rest can be scrolled" (2026-09-01, 51:38). */
.sticky1 td.frz,.sticky1 th.frz{position:sticky;z-index:3;background:#fff}
.sticky1 thead th.frz{z-index:7}
.sticky1 tbody tr:nth-child(even) td.frz{background:#fbfcfd}
.sticky1 tbody tr:hover td.frz{background:#eef3f9}
.sticky1 tbody tr.picked td.frz{background:#eef3f9}
.sticky1 tfoot tr.totals td.frz{background:#0f2036}
/* The held columns must carry the ROW's colour, not a flat white: a clash-flagged row was pink
   across the scrolling half and white across the held half, which read as two different rows. */
.sticky1 tbody tr.warn td.frz{background:#fff5f3}
/* A ROW'S COLOUR RUNS THE WHOLE WAY ACROSS. The held columns are painted solid so that rows passing
   under them do not show through when the table is scrolled sideways, and that solid white beat the
   row's own colour: a finished row came out white across the held half and tinted across the rest,
   with the seam exactly where the holding stops. The line above did it for a flagged row; a done row
   and a picked one were missed. Measured on the running page before and after. */
.sticky1 tbody tr.done td.frz,.sticky1 tbody tr.done td:first-child{background:var(--sunk)}
.sticky1 tbody tr.picked td.frz,.sticky1 tbody tr.picked td:first-child{background:#eef3f9}
.sticky1 tbody tr.warn td:first-child{background:#fff5f3}
.sticky1 tbody tr.warn:hover td.frz{background:#ffe9e5}
.sticky1 tbody tr.picked.warn td.frz{background:#eef3f9}
/* A clear edge where the held columns stop, so the join reads as a panel rather than as a fault. */
/* WHERE THE LOCKED PART ENDS AND THE SCROLLING PART BEGINS.
   The held columns had only a hairline and a faint shadow between them and the rest, so there was
   nothing to tell you which part stays put when you drag sideways ("since the table is locked we
   need a margin or indicator here", 2026-09-12). It is a real rule now, with a soft shadow to its
   right so the scrolling columns read as passing underneath it. */
/* AN INSET SHADOW, WHICH IS THE ONLY ONE THAT SURVIVES HERE. Two obvious ways to draw this line
   both fail on a held column, and both fail silently with the computed style still reading back
   correctly (sampled across the boundary in both cases: every pixel white).
     An OUTER shadow is painted outside the cell's box, and the cells painted after it put their own
     backgrounds straight over the top.
     A BORDER is no better, because this table collapses its borders, and a collapsed border belongs
     to the table's grid rather than to the cell. The cell is pinned and travels; the grid does not,
     so the line stays behind with the scrolling columns.
   An inset shadow is painted inside the cell, over its own background, so it travels with it and
   nothing can cover it. */
.sticky1 td.frz-edge,.sticky1 th.frz-edge{box-shadow:inset -2px 0 0 0 #8fa3ba}
.sticky1 tfoot tr.totals td.frz-edge{box-shadow:inset -2px 0 0 0 rgba(255,255,255,.5)}
/* THE HELD HALF OF THE TOTALS BAR SITS ABOVE ITS SCROLLING HALF. Every totals cell is pinned to the
   bottom at the same level, and at the same level the cell that comes later in the row is painted
   on top. So when the table was dragged sideways the scrolling figures were painted over the held
   columns: "Base: 499" under Week, and the word Totals covered (2026-09-14). The job rows never did
   this, because only their held cells are pinned. */
.tablewrap.tall .sticky1 tfoot tr.totals td.frz{z-index:5}

/* A machine already committed elsewhere that day: greyed, never hidden, never blocked. */
.crewchip.busy{border-color:#d9c3a0;color:#8a6a3a;background:#faf7f2;opacity:.75}
.crewchip.busy.on{opacity:1;background:#5a4a2a;border-color:#5a4a2a;color:#ffe0ad}

/* ---- year plan: the two checklist columns and the progress column ----------------------------
   The bands are the client's own: under 50 needs attention, 50-99 is on its way, 100 is done.
   Each state carries a word as well as a colour, because colour alone is unreadable to the roughly
   one man in twelve who cannot separate red from green, and these are the columns being scanned. */
.ltflag,.ltprog{white-space:nowrap}
.fl{display:inline-block;min-width:38px;text-align:center;padding:2px 8px;border-radius:999px;
    font-size:11.5px;font-weight:700;line-height:1.5}
.fl.yes{background:#e6f4ea;color:#1e7a3d}
.fl.no{background:#fdecea;color:#b3261e}
.ltp{display:inline-flex;align-items:center;gap:7px}
.ltp-t{position:relative;display:inline-block;width:54px;height:6px;border-radius:999px;background:#e7ebf0;overflow:hidden}
.ltp-t span{position:absolute;left:0;top:0;bottom:0;border-radius:999px}
.ltp b{font-size:12px;font-weight:700;font-variant-numeric:tabular-nums;min-width:32px;text-align:right}
.ltp.low .ltp-t span{background:#d93025}   .ltp.low b{color:#b3261e}
.ltp.part .ltp-t span{background:#8fce9e}  .ltp.part b{color:#2f7d46}
.ltp.full .ltp-t span{background:#1e7a3d}  .ltp.full b{color:#1e7a3d}

/* When a reference list was last changed, and by whom. Quiet on purpose: it is provenance, not a
   warning, and it sits on every card that has ever been touched. */
.resmeta{margin-top:9px;font-size:11.5px;color:var(--ink2)}

/* ---- one toolbar, one control height, one corner radius ---------------------------------------
   "The buttons are not perfectly matched in shape and alignment" (2026-09-12), and they were not: a
   primary button with 9px corners standing 33px tall, next to a filter group with 10px corners
   standing 40px tall, on the same line. The design draws the primary action as a full pill, so
   everything on the row is a pill and everything on the row is the same height. */
.wk-head{--ctl:38px}
.wk-head .btn.sm{height:var(--ctl);margin-top:0;padding:0 17px;border-radius:999px;
  display:inline-flex;align-items:center;gap:7px;line-height:1}
.wk-head .btn.sm .plus{font-size:15px;font-weight:700;line-height:1;margin-top:-1px}
.wk-head .tabs{height:var(--ctl);padding:4px;border-radius:999px;display:inline-flex;align-items:center}
.wk-head .tabs button{height:calc(var(--ctl) - 8px);padding:0 15px;border-radius:999px;
  display:inline-flex;align-items:center;gap:6px;line-height:1}

/* The job summary is a card in the left column now, not a band across the top. */
.jobside .jobhead{margin-bottom:0}
.jobside .prog{max-width:none}

/* A board row is one target: anywhere on it opens that job. */
tr.brow{cursor:pointer}
tr.brow:hover td{background:#eef3f9}
tr.brow:focus-visible{outline:2px solid var(--navy2);outline-offset:-2px}

/* The text size reading doubles as the way back to normal. */
.textsize #ts-now{border:0;background:none;cursor:pointer;padding:2px 4px;border-radius:6px;
  font:inherit;font-size:11px;font-weight:700;color:var(--ink2);min-width:38px;text-align:center}
.textsize #ts-now:hover:not(:disabled){background:var(--sunk);color:var(--ink)}
.textsize #ts-now:disabled{cursor:default}

/* The format hint under a job-number box, and what it looks like when the box cannot be repaired. */
.fmt{display:block;margin-top:4px;font-size:11px;color:var(--ink2);font-weight:400}
.fmt.bad{color:#b3261e}
input.badfmt{border-color:#e0a49d;background:#fffafa}

/* Moving a Gantt bar: it travels with the pointer while the button is held, and the day it will
   land on is lit in the header. */
.gbar.movable{cursor:grab}
.gbar.moving{cursor:grabbing;box-shadow:0 8px 20px rgba(15,32,54,.34);opacity:.96;pointer-events:none}
body.gmoving{cursor:grabbing;user-select:none}
#gantt thead th.drop-on{background:#e7eef7;box-shadow:inset 0 -3px 0 var(--navy2)}

/* On the Gantt the job number is the way through to the full page, so it reads as a target. The
   bars beside it keep the side panel. */
#gantt td.gjob{cursor:pointer}
#gantt td.gjob:hover{background:#eef3f9}
#gantt td.gjob:hover .jn{color:var(--ink)}
#gantt td.gjob:focus-visible{outline:2px solid var(--navy2);outline-offset:-2px}

/* ---- Home: resource statistics and notes side by side ------------------------------------------
   ONE ROW WITH ONE BOTTOM EDGE. The first version sat two cards of different heights next to each
   other, which left a 172px empty block under the statistics and packed name and days into the left
   of each half with a 283px empty band before Equipment began. That moved the "too far away" Kenneth
   described inside the card rather than removing it. The cards now stretch to the same height, each
   half of the statistics fills its width, and on a narrow window the two stack. */
.homesplit{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:16px;align-items:stretch;margin-top:16px}
.homesplit > .card{display:flex;flex-direction:column}
@media (max-width:1100px){.homesplit{grid-template-columns:1fr}}
.statcard-home .statcols{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:0;margin-top:12px}
.statcard-home .statsec{min-width:0}
.statcard-home .statsec:first-child{padding-right:22px}
.statcard-home .statsec + .statsec{padding-left:22px;border-left:1px solid var(--line)}
@media (max-width:720px){
  .statcard-home .statcols{grid-template-columns:1fr}
  .statcard-home .statsec:first-child{padding-right:0}
  .statcard-home .statsec + .statsec{padding-left:0;border-left:0;border-top:1px solid var(--line);padding-top:12px;margin-top:12px}
}
.statcard-home .sec{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--ink2);margin:0 0 6px}
/* Name, then days straight after it, then the week strip across the rest of the row. The name and
   days columns are only as wide as their longest entry, so every day count lines up beside its name. */
/* The week strip's column may shrink below 120px. A hard floor made this grid 207px wide inside an
   80px card on a phone, and everything in it spilled out of the screen. The strip is a row of small
   boxes sized by fraction, so it reads fine narrower; it just must not force the grid wider than the
   card holding it. */
.statlist{display:grid;grid-template-columns:max-content max-content minmax(0,1fr);column-gap:14px;row-gap:11px;align-items:center;margin:8px 0 10px}
.statlist .kv,.statlist .kvhead{display:contents}
.statlist .kv span{font-size:13px;line-height:1.35;color:var(--ink)}
.statlist .kv b{font-size:13px;line-height:1.35;color:var(--ink);font-weight:700;white-space:nowrap}
/* THE WEEK STRIP: one small box per working day, Monday to Saturday, filled on the days that person or
   machine is taken. In a soft blue rather than the schedule navy, so a fully booked week stays calm to look at. */
/* Six columns, Monday to Saturday; statsCard sets --wkdays to 7 only in a week with a real Sunday booking. */
.wkstrip{display:grid;grid-template-columns:repeat(var(--wkdays,6),minmax(0,1fr));gap:3px}
.wkday{display:block;height:14px;border-radius:3px;background:#edf1f5}
.wkday.on{background:#7f9bbb}
/* A booked day opens what booked it (2026-09-16 check-in, 00:11:09): it says so under the mouse and when reached by
   keyboard, and keeps its size and colour otherwise. */
.wkday[data-resday]{cursor:pointer}
.wkday[data-resday]:hover{background:#5f7fa3}
.wkday[data-resday]:focus-visible{outline:2px solid #2F4C7E;outline-offset:1px}
.wkhead span{font-size:10px;font-weight:700;line-height:1;color:var(--ink2);text-align:center}
/* Notes on Home: tighter rows, and "Open job" sits straight after the date instead of an arrow
   stranded at the far edge of a wide row. The job page keeps its own arrow, from the design. */
.homesplit .notecard .jnotes{gap:6px}
.homesplit .notecard .jnote{padding:8px 12px}
.homesplit .notecard .nopen{margin-left:8px;color:var(--navy2);font-weight:600;white-space:nowrap}
.homesplit .notecard .jnote:hover .nopen{text-decoration:underline}

/* ---- job photos: the feature photo on the job page, and the gallery it opens -------------------
   "We'll have basically like a feature photo in there. But once they click on it, it would open up a
   gallery ... Make it into squares" (2026-09-14, 42:09 to 42:40). The feature photo is as wide as its
   card; the gallery is squares, Before above After; one photo opens large on a dark ground so the
   work in it is what the eye goes to. */
.photocard{display:flex;flex-direction:column;gap:10px}
.photocard .notecard-h .btn.sm{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.photocard .notecard-h .plus{font-size:14px;font-weight:700;line-height:1}
.pfeature{position:relative;display:block;width:100%;aspect-ratio:4/3;padding:0;border:0;border-radius:10px;
  overflow:hidden;background:#e9eef4;cursor:zoom-in}
.pfeature img{display:block;width:100%;height:100%;object-fit:cover;transition:transform .25s ease}
.pfeature:hover img{transform:scale(1.03)}
.pfeature:focus-visible{outline:3px solid var(--navy2);outline-offset:2px}
.pkind,.pcount,.galstar{position:absolute;font-size:11px;font-weight:700;line-height:1;padding:5px 9px;border-radius:999px;pointer-events:none}
.pkind{left:8px;top:8px;background:rgba(255,255,255,.94);color:var(--ink);text-transform:uppercase;letter-spacing:.05em}
.pcount{right:8px;bottom:8px;background:rgba(15,32,54,.84);color:#fff}
.pempty{border:1.5px dashed #c8d2de;border-radius:10px;padding:18px 14px;text-align:center;color:var(--ink2);font-size:12.5px;line-height:1.55}

/* Four squares to a row at full width: three photos and the add tile, with no empty column beside them. */
.modal.gallery{width:min(920px,100%)}
.galhead{min-width:0}
.galhead .muted{font-size:12px;margin-top:2px}
.galstatus{font-size:12.5px;font-weight:600;line-height:1.45;color:#1e5a2f;background:#eef7f0;border:1px solid #cfe7d5;border-radius:8px;padding:8px 11px}
.galstatus.bad{color:#8a2a1f;background:#fdf0ee;border-color:#f2cfc9}
.galsec-h{margin:2px 0 8px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--ink2)}
.galsec-h .ct{margin-left:4px;font-size:11px;color:var(--ink2);background:var(--sunk);border-radius:10px;padding:1px 7px;letter-spacing:0}
.galsec + .galsec{margin-top:8px}
.galgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:12px}
.galtile{position:relative;display:block;width:100%;aspect-ratio:1/1;padding:0;border:0;border-radius:10px;overflow:hidden;
  background:#e9eef4;cursor:zoom-in}
.galtile img{display:block;width:100%;height:100%;object-fit:cover;transition:transform .25s ease}
.galtile:hover img{transform:scale(1.04)}
.galtile:focus-visible{outline:3px solid var(--navy2);outline-offset:2px}
.galstar{left:6px;top:6px;background:rgba(15,32,54,.84);color:#fff}
.galtile.add{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;border:1.5px dashed #9fb0c4;
  background:#f6f8fb;color:var(--navy2);font:inherit;font-size:12.5px;font-weight:600;cursor:pointer;text-align:center;padding:8px}
.galtile.add .plus{font-size:28px;font-weight:400;line-height:1}
.galtile.add:hover{background:#eef3f9;border-color:var(--navy2)}
.galtile.add:disabled{opacity:.5;cursor:default}
.galview{display:flex;flex-direction:column;gap:10px}
.galview-top{display:flex;align-items:center;justify-content:space-between;gap:10px}
.galpos{font-size:12.5px;font-weight:700;color:var(--ink2);white-space:nowrap}
.galstage{position:relative;display:flex;align-items:center;justify-content:center;height:min(62vh,640px);
  background:#0f2036;border-radius:10px;overflow:hidden}
.galstage img{display:block;max-width:100%;max-height:100%;object-fit:contain;
  background-position:center;background-size:contain;background-repeat:no-repeat}
.galnav{position:absolute;top:50%;transform:translateY(-50%);width:44px;height:44px;border:0;border-radius:50%;
  background:rgba(255,255,255,.92);color:var(--ink);font-size:30px;line-height:1;cursor:pointer;
  display:flex;align-items:center;justify-content:center;padding:0 0 3px;box-shadow:0 2px 10px rgba(0,0,0,.3)}
.galnav:hover{background:#fff}
.galnav:focus-visible{outline:3px solid #fff;outline-offset:2px}
.galnav.prev{left:12px}
.galnav.next{right:12px}
.galfoot{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px}
.galacts{display:flex;flex-wrap:wrap;gap:8px}
@media (max-width:560px){
  .galgrid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .galstage{height:50vh}
  .galnav{width:38px;height:38px;font-size:26px}
}

/* ---- the job's site plan -------------------------------------------------------------------------
   Under the schedules and as wide as them, the whole plan in view however tall it is, and full screen
   on a click (2026-09-14, 42:50 to 43:12). */
.siteplan .spframe{display:block;width:100%;padding:0;border:1px solid var(--line);border-radius:12px;background:#fff;overflow:hidden}
.siteplan .spimg{cursor:zoom-in}
.siteplan .spimg img{display:block;width:100%;height:auto;max-height:80vh;object-fit:contain;background:#fff}
.siteplan .spimg:focus-visible{outline:3px solid var(--navy2);outline-offset:2px}
.siteplan .sppdf{display:block;width:100%;height:78vh;border:0}
.siteplan .spempty,.pv-body .spempty{border:1.5px dashed #c8d2de;border-radius:12px;padding:26px 16px;text-align:center;color:var(--ink2);font-size:13px;background:#fff}
.siteplan .spfoot{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px;margin-top:8px}
.siteplan .galstatus{margin-bottom:8px}
.siteplan .wk-head .btn.sm + .btn.sm{margin-left:8px}
.planview{position:fixed;inset:0;z-index:70;display:flex;flex-direction:column;background:#0f2036}
.planview[hidden]{display:none}
.pv-bar{display:flex;align-items:center;gap:10px;padding:10px 16px;background:#fff;border-bottom:1px solid var(--line)}
.pv-bar h3{margin:0;font-size:15px}
.pv-bar a.btn{text-decoration:none;display:inline-flex;align-items:center;margin-top:0}
.pv-body{flex:1;min-height:0;display:flex;align-items:center;justify-content:center;padding:16px}
.pv-body img{display:block;max-width:100%;max-height:100%;object-fit:contain;background:#fff;border-radius:6px}
.pv-body object{display:block;width:100%;height:100%;background:#fff;border-radius:6px}

/* A DAY'S OWN PROGRESS (2026-09-16 check-in, 00:25:09): the percent on a part-done row in the Weekly table, and on the
   side panel's day card a thin line, the tons against the plan, what is left and why it stopped. */
.tag-p{display:inline-block;font-size:10px;font-weight:700;letter-spacing:.02em;padding:1px 6px;border-radius:999px;background:#e8eef8;color:#2F4C7E;border:1px solid #bccbe4;margin-left:4px}
.dprog{margin:4px 0 3px}
.dprog-t{height:6px;max-width:240px;border-radius:999px;background:#e3e8ee;overflow:hidden}
.dprog-t span{display:block;height:100%;border-radius:999px;background:#2F4C7E}
.dprog-n{margin-top:3px;font-size:14px;font-weight:600;color:var(--ink)}
.dprog-r{font-size:14px;color:var(--ink2)}

/* THE JOB PANEL THAT OPENS FROM HOME: the days on the left and scrolling, what the job IS on the right
   and staying put. "Need to make sure that this one, all the details, needs to be static ... and move
   to the right. So switch this here. To here ... This one would be scrolling" (the design lead,
   2026-09-24). The drawer is the thing that scrolls, so a column held inside it genuinely holds. */
/* Holding the details with sticky meant they still travelled the first ~205px before catching, which is
   the movement GM saw. Nothing above them scrolls now: the panel itself is a fixed column (heading, then
   the two columns filling what is left) and the scrolling happens inside the days. So the heading and the
   details do not move at all, which is what "this one would be static" asks for. Only above 820px, where
   there are two columns to hold apart; a narrow panel still scrolls as one piece. */
@media (min-width:820px){
  .drawer{display:flex;flex-direction:column;overflow:hidden}
  /* ONE LINE, THE WHOLE HEIGHT, AND THE DETAILS LEVEL WITH THE HEADING. "So you see this line right
     here, right? So that needs to come across like exactly that ... And then this one needs to extend.
     So all of these should extend up above. That way, all the details, this one and this one, would be
     connected together ... That's just one line" (the design lead, 2026-09-30, drawing on a screenshot
     of this panel: a red line down the full height, then the days boxed with an arrow up to the job
     name, then the details circled with arrows pulling them up to the heading).
     The heading used to be a band across the whole width with two short columns under it, which left
     the top right empty and broke the line in half. The panel body is now the grid: the heading and
     the days are the left column, the details are the right column running top to bottom, and the
     border between them is the single line he drew. */
  #detail-body{display:grid;flex:1;min-height:0;
    grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);grid-template-rows:auto minmax(0,1fr)}
  #detail-body > .dh{grid-column:1;grid-row:1;padding-right:22px}
  #detail-body > .db{grid-column:1;grid-row:2;min-height:0;overflow:hidden;display:block;padding:8px 24px 24px 22px}
  /* 56px on the right so the first line of details never runs under the close cross, which is the
     same reason the heading used to carry it. */
  #detail-body > .dc-facts{grid-column:2;grid-row:1 / span 2;min-height:0;overflow:auto;
    border-left:1px solid var(--line);padding:22px 56px 24px 32px}
  .db .dc-days{overflow:auto;height:100%;min-height:0;padding-right:12px}
}

/* ---------- moves the app made, and the ones it wants to change ----------
   Holly is meeting a tool that creates rows on her behalf. Everything here exists to answer two
   questions at a glance: did I put this here, and is anything waiting on me. */

/* "auto": a quiet label, not a warning. An app-made row is normal and correct, so it must not look
   like a problem; it only has to be distinguishable from one she typed. */
.genbadge{display:inline-block;margin-left:6px;padding:1px 5px;border-radius:5px;font-size:10px;
  font-weight:700;letter-spacing:.06em;text-transform:uppercase;vertical-align:1px;
  background:var(--navy2);color:#cdd6e2;cursor:help}
tr.genrow td:first-child{box-shadow:inset 3px 0 0 var(--navy2)}
/* The offered driver. It has to read as a question, not as data: a name sitting in this column in
   ordinary type would be indistinguishable from one she committed, which is the whole thing GM asked
   us not to do. So it is lighter, it carries its question mark, and it is dotted underneath the way
   an unfinished thing is. On hover it becomes a button, because that is when it is about to be one. */
.drvhint{font:inherit;color:var(--ink2);background:none;border:0;padding:1px 4px;margin:-1px -4px;
  border-radius:5px;cursor:pointer;opacity:.75;text-decoration:underline;
  text-decoration-style:dotted;text-underline-offset:3px}
.drvhint:hover{opacity:1;color:var(--navy2);background:#eef1f6;text-decoration:none}
.drvhint:focus-visible{outline:2px solid var(--navy2);outline-offset:1px;opacity:1}
.drvhint[disabled]{opacity:.4;cursor:default}
/* The same fact in words, directly above the table it describes. Carries the badge's own colour on
   its left edge so the eye joins the sentence to the marked rows without being told to. */
.gencount{margin:8px 0 -4px;padding:6px 10px;border-left:3px solid var(--navy2);
  background:#f4f6f9;border-radius:0 6px 6px 0;font-size:12px;color:var(--ink2)}
/* A done move is still a row she may need to read, so it is softened rather than struck through. */
tr.donerow td{color:var(--ink2)}
tr.donerow .tick.on{color:#1f7a3d}

/* The done tick: a real button, sized so it is reachable without being the loudest thing in the row. */
.tick{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;padding:0;
  border:1px solid var(--line);background:#fff;border-radius:5px;font:inherit;font-size:13px;
  font-weight:700;line-height:1;color:var(--ink2);cursor:pointer}
.tick.on{border-color:#1f7a3d;color:#1f7a3d;background:#eef7f1}
.tick:hover:not(:disabled){border-color:var(--navy2)}
.tick:disabled{opacity:.5;cursor:default}
.tick:focus-visible{outline:2px solid var(--red);outline-offset:2px}
span.tick{cursor:default}

/* The panel offering changes. Deliberately not an alert: a reschedule is routine, and the panel's job
   is to be noticed on the screen she is already on, not to interrupt her. */
.movepanel{border:1px solid var(--line);border-left:4px solid var(--navy2);border-radius:10px;
  background:#f7f9fc;padding:12px 14px;margin-top:18px}
.mp-head{display:flex;align-items:center;gap:10px;margin-bottom:6px;font-size:14px}
.mp-row{display:flex;align-items:center;gap:12px;padding:8px 0;border-top:1px solid var(--line)}
.mp-row > div:first-child{flex:1;min-width:0}
.mp-row b{font-size:13px;font-weight:600}
.mp-why{font-size:12px;color:var(--ink2);margin-top:2px}
.movepanel .mp-why:last-child{margin-top:8px}
@media (max-width:700px){.mp-row{flex-wrap:wrap}}

/* The whole-job estimate, boxed off from the per-week figures underneath it. The two sets use the
   same five material names, so the box and its own heading are what stop them being confused: a
   person typing 3,846 into the wrong Stone field would be planning a single week of the whole job. */
.estbox{border:1px solid var(--line);border-radius:10px;padding:12px 14px 4px;margin:0 0 14px;background:#f9fbfd}
.estbox legend{font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ink2);padding:0 6px}
.estbox .tons-grid{margin:0}

/* Estimate against actual, on a job. Four figures that are never reconciled into one another. */
.tonbox{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin:10px 0 0}
.toncard{border:1px solid var(--line);border-radius:9px;padding:9px 11px;background:#fff}
.toncard h4{margin:0 0 6px;font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ink2)}
.tonline{display:flex;justify-content:space-between;gap:10px;font-size:13px;padding:1px 0}
.tonline span:last-child{font-variant-numeric:tabular-nums;font-weight:600}
.tonline.left span:last-child{color:#1f7a3d}
/* An over-run is the case the owner asked to be able to see, so it is stated, not hidden. */
.tonline.over span:last-child{color:var(--red)}
.tonnote{font-size:12px;color:var(--ink2);margin-top:6px;padding-top:6px;border-top:1px solid var(--line)}

/* ---------- The Long Term Calendar, tightened ----------
   Asked for in the owner's own words on 2026-10-01, about this table: "take the padding to next to
   nothing on all this to make it super tight, and go from there" ([45:46]), "like Starbucks
   Crawfordsville may have to be wrapped" ([45:33]), and the goal behind both, "we need a horizontal
   single page view" ([49:49]).

   MEASURED FIRST, at 1600 wide with the whole year showing: the table needed 2,560px and had 1,459,
   so it was 1,101px short across 21 columns, with uniform 46px rows. The three places that width was
   going were not the obvious ones. Quarry was the widest column at 277px and the job name second at
   242px, and three more columns were wide because of their HEADING rather than their data: "Asphalt
   Truck Count" alone took 178px to show a number that is never more than two digits.

   SO THE TYPE SIZE IS NOT TOUCHED. The space comes from padding, from letting three text columns wrap,
   and from letting the headings stack. That is also what the outside evidence says to do: mature design
   systems ship several table densities at one constant type size, and the one recent controlled study
   on dense tables found smaller cell padding measurably improved how fast people completed tasks.
   Shrinking the letters was the other option and it is the one that makes a table unreadable. */
table.lt th, table.lt td{padding:5px 8px}
/* Headings stack instead of setting the column's width. A two-word heading over a two-digit number is
   the single biggest avoidable waste of width in this table. */
table.lt thead th{white-space:normal;line-height:1.2;vertical-align:bottom}
/* The three text columns may wrap; every figure still keeps its one line, because a number broken
   across two lines is a misreading waiting to happen. */
table.lt td.ltname,table.lt td.ltcode{white-space:normal;line-height:1.25;max-width:150px;
  /* A COLUMN OF WORDS NEEDS MORE ROOM BESIDE IT THAN A COLUMN OF FIGURES DOES. Flagged by GM on
     2026-10-03 looking at the Quarry column: "looks squeezed". Measured at the width he was using, the
     Quarry cell was 115px wide with 8px either side, so a name like "US Agg-Cloverdale" wrapped to fill
     the whole 99px between them and ran right up against the line separating it from Stone. The job
     name beside it had 20px of slack and looked fine, which is why only these two are widened: a column
     of two-digit numbers does not need the same breathing room, and giving it to all 21 would cost the
     width this screen does not have. */
  padding-left:12px;padding-right:12px}
table.lt td.ltname{min-width:120px}
/* A FLOOR FOR THE QUARRY COLUMN, so the names that CAN sit on one line do. Giving the column its space
   back pushed almost every quarry onto two lines, which trades a squeeze for a taller table. Their
   names are a short known set and all but one ("US Agg FR/Milestone Crushed Concrete") fit inside this
   on a single line; that one still wraps, which is correct, because it is genuinely long. */
table.lt td.ltcode{min-width:142px}
/* The first column AFTER the pinned block sits directly against its edge line, so it gets the matching
   space on its own side. Without it the gap is one-sided and still reads as a squeeze. */
table.lt .frz-edge + td,table.lt .frz-edge + th{padding-left:14px}
/* The totals row is the thing people read off the bottom, so it keeps a little more room than the
   rows above it and stays on one line. */
table.lt tfoot td{padding:7px 8px;white-space:nowrap}

/* The year plan's totals bar sits on the bottom of its box, in a single month exactly as in a whole
   year. The table is told to fill the box; the empty spacer row takes all the leftover height, so the
   real rows keep theirs and the bar lands at the foot. */
table.lt{height:100%}
.ltspacer td{height:100%;padding:0;border-bottom:0;background:#fff}
.ltspacer:hover td{background:#fff}

/* =================================================================================================
   ACCOUNTS: claiming an account, the people screen, the person panel, the invite link, My account.
   Decision 002 (2026-10-07): the portal owns the accounts, so all of this is now the app's job.

   NOTHING NEW IS INVENTED HERE. Every colour is one of the five already in :root, every box reuses
   .card / .modal / .drawer / .btn, and the one typeface added is the browser's own monospace, used
   only where a person has to copy characters by eye and must not confuse O with 0.
   ================================================================================================= */

/* ---- claiming an account (the invite and reset link) -------------------------------------------
   The same two-panel frame as the sign-in screen, because it IS the sign-in screen for somebody who
   has not got a password yet. It reuses .login's own classes rather than copying them, so the day the
   photograph or the logo changes, it changes in both places. */
.login-form .claimsub{margin:0 0 4px;color:var(--ink2);font-size:12.5px;line-height:1.5}
.login-form .claimwho{font-weight:700;color:var(--ink)}
/* The steps are stacked the way #pw-step and #code-step are, for the same reason: a hidden block
   must not leave the form's own gap behind it. */
#claim-check,#claim-bad,#claim-pw,#claim-totp,#claim-codes{display:flex;flex-direction:column;gap:11px}
#claim-check[hidden],#claim-bad[hidden],#claim-pw[hidden],#claim-totp[hidden],#claim-codes[hidden]{display:none}
/* The claim screen runs longer than the sign-in one (two passwords, then a setup key, then ten
   codes), so its column scrolls inside the card instead of pushing the page about. */
.login-form.tall{justify-content:flex-start;padding-top:30px;padding-bottom:30px;overflow-y:auto;max-height:100%}

/* THE SETUP KEY, TYPED BY HAND. There is no QR code on day one (build spec E2): four people do this
   once, every authenticator app accepts a typed key, and a vendored encoder is a dependency this
   repo does not have. So the key has to be genuinely readable: monospaced, grouped in fours by the
   server, and big enough to copy off the screen by somebody wearing glasses. */
.setupkey{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:17px;font-weight:700;
  letter-spacing:.08em;background:var(--sunk);border:1px solid var(--line);border-radius:9px;
  padding:12px 13px;word-break:break-all;line-height:1.6;color:var(--ink);user-select:all}
.applist{margin:0;padding-left:18px;color:var(--ink2);font-size:12.5px;line-height:1.7}
.applist li{margin:0}

/* THE TEN BACKUP CODES, SHOWN ONCE AND NEVER AGAIN. Large, monospaced, two columns where there is
   room. Somebody is reading these off a screen to copy onto paper, so the only thing that matters is
   that no character can be mistaken for another one. */
.codesbox{background:var(--sunk);border:1px solid var(--line);border-radius:10px;padding:13px}
.codesgrid{display:grid;grid-template-columns:1fr;gap:6px 18px;margin:0;padding:0;list-style:none}
@media (min-width:560px){.codesgrid{grid-template-columns:1fr 1fr}}
.codesgrid li{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:15px;
  font-weight:700;letter-spacing:.04em;color:var(--ink);user-select:all}
.codesact{display:flex;gap:8px;flex-wrap:wrap}
.codesact .btn{margin-top:0}
/* The tick that has to be ticked. Bigger than the app's other checkboxes on purpose: it is the one
   control standing between somebody and losing their way back in. */
.codeschk{display:flex;align-items:flex-start;gap:9px;font-size:13px;color:var(--ink);font-weight:600;cursor:pointer;line-height:1.45}
.codeschk input{width:18px;height:18px;flex:none;margin-top:1px}

/* PRINTING THE CODES, WHICH IS THE WAY WE LEAD. A downloaded file on a shared office machine is ten
   single-use bypass codes sitting in the Downloads folder for ever, so there is no download button
   anywhere in this app. Printing goes through the browser's own dialogue, which also offers Save as
   PDF for anyone who wants that, and makes it their deliberate choice rather than ours.
   Print hides the whole app and prints the codes alone: a print of the sign-in screen with the codes
   somewhere down it is not something anybody would keep. */
.printonly{display:none}
@media print{
  body.printing-codes>*{display:none!important}
  body.printing-codes .printonly{display:block!important;position:static}
  body.printing-codes .printonly .codesgrid li{font-size:14pt}
  body.printing-codes .printonly h2{font-size:16pt;margin:0 0 4pt}
  body.printing-codes .printonly p{font-size:10pt;color:#000;margin:0 0 10pt;max-width:none}
}

/* ---- the people screen -------------------------------------------------------------------------
   The client's headline ask. It sits on the Admin tab, which keeps its place and its gap in the menu. */
.people-head{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-bottom:12px}
.people-head .sp{flex:1;min-width:0}
.people-find{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:12px}
.people-find input[type="search"]{flex:1 1 220px;min-width:0;border:1px solid var(--line);border-radius:9px;
  padding:9px 11px;font:inherit;color:var(--ink);background:#fff}
.people-find select{border:1px solid var(--line);border-radius:9px;padding:9px 10px;font:inherit;color:var(--ink);background:#fff}
.people-find input:focus,.people-find select:focus{outline:none;border-color:var(--navy2)}
.people-count{font-size:12px;color:var(--ink2)}
/* A SWITCHED-OFF ROW IS VISIBLY SET APART. Not red: nothing is wrong with it, it is simply off, and
   red on this screen would read as a fault on a row somebody deliberately made. Greyed, with a grey
   edge in the same place the clash rows carry their red one, so the eye finds it the same way. */
table.people tr.susp td{color:var(--ink2);background:#fafbfc}
table.people tr.susp td:first-child{box-shadow:inset 3px 0 0 #9aa8ba}
table.people tr.susp td.pname{font-weight:600}
table.people td.pname{font-weight:700;white-space:normal}
table.people td.pmail{color:var(--ink2);white-space:normal}
.pstate{display:inline-block;font-size:10px;text-transform:uppercase;letter-spacing:.04em;font-weight:700;
  border-radius:999px;padding:1px 7px;border:1px solid var(--line);background:var(--sunk);color:var(--ink2)}
.pstate.active{background:#e8f5ec;color:#1c6b3a;border-color:#b7e0c5}
.pstate.invited{background:#e8eef8;color:#2F4C7E;border-color:#bccbe4}
.pstate.suspended{background:#eef0f3;color:#4a5563;border-color:#d7dee6}
.people-more{margin-top:12px;display:flex;align-items:center;gap:10px}

/* ---- the person panel, in the shape of the job panel -------------------------------------------- */
.pp-head{padding:22px 56px 18px 22px;border-bottom:1px solid var(--line)}
.pp-head .pl{color:var(--red);font-weight:700;font-size:12px}
.pp-head h2{margin:2px 0 0;font-size:19px}
.pp-head .pmail{color:var(--ink2);font-size:13px;margin-top:3px;word-break:break-all}
.pp-body{padding:8px 22px 28px}
.pp-body .sec{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--ink2);margin:18px 0 7px;font-weight:700}
.pp-act{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.pp-act .btn{margin-top:0}
.pp-row{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end;margin-bottom:4px}
.pp-row label{display:flex;flex-direction:column;font-size:12px;color:var(--ink2);gap:4px;font-weight:600;flex:1 1 220px;min-width:0}
.pp-row input,.pp-row select{border:1px solid var(--line);border-radius:8px;padding:9px 10px;font:inherit;color:var(--ink);background:#fff}
.pp-row input:focus,.pp-row select:focus{outline:none;border-color:var(--navy2)}
.pp-row .btn{margin-top:0;flex:none}
/* THE GUARD RAIL, DRAWN RATHER THAN ONLY ENFORCED. The server refuses these moves whatever the screen
   does; this is what stops anybody getting as far as being refused. The reason sits beside the greyed
   button and is always visible: a tooltip is not a reason, because it is not there until you go
   looking for one, and by then you have already pressed the button. */
.whynot{font-size:12px;color:var(--ink2);line-height:1.4;flex:1 1 240px;min-width:0}
.pp-note{font-size:12.5px;color:var(--ink2);line-height:1.5;margin:0 0 10px;max-width:68ch}
.pp-err{color:#c0392b;font-size:12.5px;margin-top:8px}

/* ---- the invite / reset link ------------------------------------------------------------------- */
.linkbox{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.linkbox input{flex:1 1 240px;min-width:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:12.5px;border:1px solid var(--line);border-radius:8px;padding:10px 11px;color:var(--ink);background:var(--sunk)}
.linkbox .btn{margin-top:0;flex:none}
/* The warning is a statement of fact, not an alarm, so it is the app's own note colour rather than
   red: until somebody uses this link it opens the account as surely as the password would. */
.linkwarn{background:#fff8ea;border:1px solid #f0d38f;color:#8a5a00;border-radius:8px;padding:10px 11px;font-size:12.5px;line-height:1.5}

/* ---- my account --------------------------------------------------------------------------------- */
.acctwrap{display:grid;gap:14px;max-width:760px}
.acctwrap .card h2{margin:0 0 4px;font-size:16px}
.acctwrap .card p{margin:0 0 8px;max-width:70ch;color:var(--ink)}
.acctwrap .card p:last-child{margin-bottom:0}
.acct-row{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end;margin-top:10px}
.acct-row label{display:flex;flex-direction:column;font-size:12px;color:var(--ink2);gap:4px;font-weight:600;flex:1 1 200px;min-width:0}
.acct-row input{border:1px solid var(--line);border-radius:8px;padding:9px 10px;font:inherit;color:var(--ink);background:#fff}
.acct-row input:focus{outline:none;border-color:var(--navy2)}
.acct-row .btn{margin-top:0;flex:none}
/* The top-bar name and circle are now the way in to this page, so they have to look pressable while
   staying the same block the design lead ordered left to right on 2026-09-30. */
.whobtn{display:inline-flex;align-items:center;gap:12px;background:none;border:1px solid transparent;
  border-radius:999px;padding:2px 2px 2px 10px;font:inherit;cursor:pointer;min-width:0;flex:0 1 auto}
.whobtn:hover{background:var(--sunk);border-color:var(--line)}
.whobtn:focus-visible{outline:2px solid var(--red);outline-offset:2px}

/* A button that reads as a link. Used on the weather tile to reach the map, where the per-site
   forecasts live. A full button there would compete with the forecast it sits above. */
.linkbtn{background:none;border:0;padding:0;color:#c8102e;cursor:pointer;font:inherit;text-decoration:underline;text-underline-offset:2px}
.linkbtn:hover{opacity:.8}
.linkbtn:focus-visible{outline:2px solid currentColor;outline-offset:2px;border-radius:2px}

/* ---- Location: drive time on the left, the map with its route on the right ----------------------
   ASKED FOR ON 2026-10-09 by the operations lead, who opened Google's directions page on the call and
   said the screen should be "the same as that" left side: From above To, the answer under them with
   the time on its right, and the map beside it showing the route. The two used to be separate cards,
   one above the other, with From and To each half a page wide so their arrows sat far from the text.
   The left column is capped so a box is about as wide as a job name. On a narrow window it stacks,
   drive time first, because a two-column map on a phone leaves neither half usable. */
.locsplit{display:grid;grid-template-columns:minmax(16rem,21rem) minmax(0,1fr);gap:16px;align-items:start}
.locsplit .dtpanel label{display:block;font-size:12px;font-weight:600;color:var(--ink2);margin-bottom:8px}
.locsplit .dtpanel select{display:block;width:100%;margin-top:3px;font:inherit;color:var(--ink);padding:6px 8px;border:1px solid var(--line);border-radius:8px;background:#fff}
.locsplit .dtpanel #dt-go{margin-top:2px}
/* WRAPS RATHER THAN SQUEEZES. In a narrow column the time kept its width and the names were crushed
   to one letter per line (seen at phone width, 2026-10-13); now the names keep a readable width and
   the time drops beneath them when both cannot fit side by side. */
.dtrow{display:flex;flex-wrap:wrap;justify-content:space-between;gap:4px 12px;align-items:flex-start;border-top:1px solid var(--line);padding-top:10px;margin-top:12px}
.dtrow .dt-names{flex:1 1 7rem;font-size:12.5px;color:var(--ink2);line-height:1.4;min-width:0;overflow-wrap:break-word}
/* CAPPED, so the longest band ("About 2 and a half hours") wraps inside its own corner rather than
   dropping the whole time under the names. On the live site "About half an hour" alone did that at
   laptop width, because the tests had only ever used the shorter "About an hour". */
.dtrow .dt-time{text-align:right;flex:0 1 auto;min-width:0;max-width:9.5rem;margin-left:auto}
.dtrow .dt-time strong{display:block;font-size:1.15rem;line-height:1.2;color:#1a7f37}
.dtrow .dt-time span{font-size:12px;color:var(--ink2)}
.dt-caveat{font-size:12px;color:var(--ink2);margin:8px 0 6px;line-height:1.4}
@media (max-width:900px){.locsplit{grid-template-columns:1fr}}

/* The setup QR code. White behind it whatever the page does, because a scanner needs dark on light,
   and a fixed size that a phone camera reads comfortably from a laptop screen. */
.totpqr{background:#fff;width:200px;max-width:100%;margin-top:8px;border:1px solid var(--line);border-radius:8px;padding:4px}
.totpqr svg{display:block;width:100%;height:auto}
