/* ==========================================================================
   Frame Team BQC — application styles
   Palette, type and auth screens taken from the v141 concept.
   ========================================================================== */

:root{--fd:'Inter',system-ui,-apple-system,sans-serif;--fb:'Inter',system-ui,-apple-system,sans-serif;--ink:#06180F;--forest:#093220;--forest-2:#0F4A2F;--forest-3:#1C6B45;--signal:#068943;--signal-lo:#E6F3EB;--signal-mid:#5FBE8B;--paper:#F4F6F3;--card:#FFFFFF;--line:#E2E8E3;--line-2:#EEF2EE;--text:#0A1F16;--muted:#5E6F65;--steel:#84968C;--amber:#A8710C;--amber-lo:#FBF2E1;--rust:#A33F27;--rust-lo:#FAEBE6;--indigo:#2E5590;--indigo-lo:#EAEFF8;--r-surface:20px;--r-card:14px;--r-ctl:11px;--r-pill:999px;--lift-1:0 1px 2px rgba(6,24,15,.05);--lift-2:0 12px 28px -14px rgba(6,24,15,.30), 0 2px 6px rgba(6,24,15,.05);--lift-3:0 -14px 40px -18px rgba(6,24,15,.28)}

*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;padding:0}
body{font-family:var(--fb);background:var(--paper);color:var(--text);min-height:100dvh;-webkit-font-smoothing:antialiased}

/* ==========================================================================
   Brand lockup
   ========================================================================== */

.lockup{display:inline-flex;flex-direction:column;align-items:center}
.lockup .mark{width:var(--ls);height:var(--ls);display:block}
.lockup .wm{display:block;width:calc(var(--ls)*1.5);margin-top:calc(var(--ls)*-.064);color:var(--forest)}
.lockup.light .wm{color:#fff}

.iso .face{transition:fill .55s ease}
.iso .u-on .lite{fill:#058943} .iso .u-on .dark{fill:#093320}
/* On dark the mark cannot use the brand's shadow green, because that IS the
   ground. Both tones lift instead. Raised 20 Sept: at the old pair the shaded
   faces sat at 1.4:1 against the lightest part of the sign-in gradient and the
   silhouette fell apart once the logo got bigger. This pair holds at 2.5:1
   there and still reads on the app bar. */
.iso.onDark .u-on .lite{fill:#45E09A} .iso.onDark .u-on .dark{fill:#12AC61}

/* the three units assemble on load */
.lockup .mark g{opacity:0;animation:build .6s ease forwards}
.lockup .mark g:nth-child(1){animation-delay:.08s}
.lockup .mark g:nth-child(2){animation-delay:.24s}
.lockup .mark g:nth-child(3){animation-delay:.40s}
@keyframes build{to{opacity:1}}
.lockup .wm{animation:fadeup .5s .44s both}
@keyframes fadeup{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

@media (prefers-reduced-motion: reduce){
  .lockup .mark g,.lockup .wm{animation:none;opacity:1;transform:none}
}

/* ==========================================================================
   Arriving, rather than appearing

   Two things that make a screen feel made rather than loaded, both from the
   design review of 23 Sept: every progress bar and ring draws itself in from
   nought over three quarters of a second, and a move between screens
   cross-fades instead of flashing white. Neither is information, so both go
   under reduced-motion. The fade is one declaration and browsers that do not
   know it ignore it.
   ========================================================================== */

@view-transition{navigation:auto}
::view-transition-old(root),::view-transition-new(root){animation-duration:.18s}

@keyframes barin{from{transform:scaleX(0)}}
.herobar i,.minibar i,.rowbar i,.sbar i,.rsm i,.repbar i{transform-origin:left center;animation:barin .75s cubic-bezier(.2,.75,.3,1) both}

@property --p{syntax:'<number>';inherits:false;initial-value:0}
@keyframes ringin{from{--p:0}}
.phring{animation:ringin .85s cubic-bezier(.2,.75,.3,1) both}

@media (prefers-reduced-motion: reduce){
  @view-transition{navigation:none}
  .herobar i,.minibar i,.rowbar i,.sbar i,.rsm i,.repbar i,.phring{animation:none}
}

/* ==========================================================================
   Auth screens (sign in, choose your PIN)

   White, at every width. Until 7 Oct 2026 the phone's sign-in was a dark green
   screen and these rules were its colours, with white ones laid over the top
   from 1024px; when the phone took the computer's design the white became the
   only one. Each screen still carries the brand block it opened with back then
   (.brandtop, .rule, .strap): the logo is in the green panel now, so they are
   not shown.

   How the page is laid out, and the panel with the frame going up in it, is
   "The sign-in", further down.
   ========================================================================== */

.auth-page{min-height:100dvh;display:flex;flex-direction:column;background:#fff}

.auth{position:relative;width:100%;flex:1;display:flex;flex-direction:column;background:#fff;color:var(--text)}
.auth-panel{flex:1;display:flex;flex-direction:column;position:relative;width:100%;max-width:430px;margin:0 auto;padding:22px 24px 28px}

.auth .brandtop,.auth .rule,.auth .strap{display:none}
.auth h1{font-family:var(--fd);font-size:30px;line-height:1.1;letter-spacing:-.028em;font-weight:600;
  margin:0 0 10px;color:var(--text)}
.auth .lede{font-size:14.5px;color:#5E6F65;line-height:1.55;margin:0 0 26px}
/* screens with no lede under the heading need the gap putting back */
.auth h1 + .roles,
.auth h1 + form{margin-top:16px}
.auth .spacer{flex:1;min-height:10px}
.auth .legal{font-size:12px;color:#5E6F65;line-height:1.85;margin-top:20px}
.auth .legal b{color:var(--text);font-weight:600}
/* the small print a screen adds reads with its form; the line that closes every screen is centred */
.auth-panel > .legal:last-child{text-align:center}

/* fields */
.authfield{margin-bottom:16px}
.authfield label{display:block;font-size:13px;color:var(--text);margin-bottom:9px;font-weight:600}
.authfield .box{display:flex;align-items:center;gap:11px;background:#F4F6F3;
  border:1px solid #84968C;border-radius:var(--r-ctl);padding:16px;
  transition:border-color .18s, background .18s, box-shadow .18s}
.authfield .box svg{width:18px;height:18px;stroke:#5E6F65;fill:none;stroke-width:1.8;flex:none;
  stroke-linecap:round;stroke-linejoin:round}
.authfield .box:focus-within{border-color:var(--signal);background:#fff;
  box-shadow:0 0 0 3px rgba(6,137,67,.18)}
.authfield input{
  flex:1;min-width:0;width:100%;
  background:none;border:none;outline:none;padding:0;
  font:500 15.5px var(--fb);color:var(--text);
}
.authfield input::placeholder{color:#74857B}
.authfield input:-webkit-autofill{
  -webkit-text-fill-color:var(--text);
  transition:background-color 5000s ease-in-out 0s;
}
.authfield input.pin{letter-spacing:.42em;font-size:17px}
.authfield.has-error .box{border-color:var(--rust);background:var(--rust-lo)}
.authfield .err{font-size:12.5px;color:#8A331E;margin-top:8px}
.authfield .hint{font-size:12.5px;color:#5E6F65;margin-top:8px}

/* role picker */
.roles{display:grid;gap:12px}
.role{position:relative;display:flex;gap:15px;align-items:center;width:100%;text-align:left;color:var(--text);cursor:pointer;
  padding:17px;border-radius:18px;border:1px solid #C9D3CC;
  background:#fff;
  transition:border-color .18s,transform .1s,background .18s;
  text-decoration:none;font-family:inherit}
.role:hover{border-color:var(--signal);background:#F4FAF6}
.role:active{transform:scale(.99)}
.role .ic{width:50px;height:50px;flex:none;border-radius:15px;display:grid;place-items:center;
  background:var(--signal-lo);border:1px solid #CDE7D9}
.role .ic svg{width:24px;height:24px;stroke:var(--signal);fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.role .txt{flex:1}
.role h5{font-family:var(--fd);font-size:16.5px;font-weight:600;margin:0 0 5px;letter-spacing:-.016em}
.role p{margin:0;font-size:13px;color:#5E6F65;line-height:1.45}
.role .go{margin-left:auto;flex:none;color:var(--signal);display:flex}
.role .go svg{width:18px;height:18px;display:block;stroke:currentColor;fill:none}

/* back button on the sign-in step */
.authback{position:absolute;top:18px;left:18px;width:38px;height:38px;border-radius:var(--r-ctl);z-index:2;
  border:1px solid #C9D3CC;background:#fff;color:var(--forest);
  display:grid;place-items:center;cursor:pointer;text-decoration:none;transition:background .16s}
.authback:hover{background:#F4F6F3}
.authback svg{width:17px;height:17px;stroke:currentColor;fill:none}

.roletag{display:inline-flex;align-items:center;gap:7px;align-self:flex-start;border:1px solid #CDE7D9;
  background:#E6F3EB;border-radius:var(--r-pill);padding:7px 14px;font:600 12.5px var(--fb);
  color:var(--forest);margin-bottom:20px}
.auth .btn.link{color:var(--forest)}
.auth .btn.link:hover{color:var(--signal)}

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn{width:100%;border:none;border-radius:var(--r-ctl);padding:16px;cursor:pointer;
  font:600 15px var(--fb);background:var(--signal);color:#fff;letter-spacing:normal;
  transition:background .16s,transform .09s}
.btn:hover{background:#057A3B}
.btn:active{transform:scale(.987)}
.btn:disabled{background:#DFE5E1;color:#93A69B;cursor:not-allowed}
.btn.ghost{background:transparent;color:var(--forest);border:1px solid var(--line);margin-top:9px}
.btn.ghost:hover{background:#F7F9F7}
.btn.sm{padding:12px;font-size:14px;width:auto}
.btn.link{background:none;color:#8FBFA6;border:none;font-weight:500;font-size:13.5px;padding:13px}
.btn.link:hover{background:none;color:#B4D9C5}

/* ==========================================================================
   Flash messages
   ========================================================================== */

.flash{border-radius:var(--r-ctl);padding:12px 14px;font-size:13.5px;line-height:1.5;margin-bottom:16px;border:1px solid}
.flash.success{background:var(--signal-lo);border-color:#CDE7D9;color:#0A5C31}
.flash.error{background:var(--rust-lo);border-color:#F0D6CC;color:#8A331E}
.flash.warning{background:var(--amber-lo);border-color:#F0E2C4;color:#7A5510}
.flash.info{background:var(--indigo-lo);border-color:#D6E1F3;color:#294B80}

/* on the sign-in screens a plain notice is a quiet grey, not the app's blue */
.auth .flash{background:#F4F6F3;border-color:#E2E8E3;color:var(--text)}
.auth .flash.success{background:var(--signal-lo);border-color:#CDE7D9;color:#0A5C31}
.auth .flash.error{background:var(--rust-lo);border-color:#F0D6CC;color:#8A331E}
.auth .flash.warning{background:var(--amber-lo);border-color:#F0E2C4;color:#7A5510}

/* ==========================================================================
   Application shell (signed in)
   ========================================================================== */

.app-header{background:var(--forest);color:#fff;padding:13px 18px;display:flex;align-items:center;justify-content:space-between;gap:16px}
.app-header .brand{display:flex;align-items:center;gap:10px;color:#fff;text-decoration:none}
.app-header .brand .mark{width:35px;height:35px;display:block}
.app-header .brand b{font-family:Quicksand,var(--fb);font-weight:600;font-size:15.5px;letter-spacing:.004em}
.app-header .brand b i{font-style:normal;font-weight:500}
.app-header .who{display:flex;align-items:center;gap:13px;font-size:13.5px;color:#96B8A6}
.app-header .who a{color:#fff;text-decoration:none;border-bottom:1px solid rgba(255,255,255,.32);padding-bottom:1px}
.app-header .who a:hover{border-bottom-color:#fff}

.wrap{max-width:900px;margin:0 auto;padding:24px 18px 64px}

.page-title{font-family:var(--fd);font-size:26px;letter-spacing:-.022em;font-weight:600;margin:0 0 5px}
.page-sub{color:var(--muted);font-size:14.5px;margin:0 0 22px;line-height:1.5}

.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);box-shadow:var(--lift-1);padding:20px;margin-bottom:16px}
.card h2{font-family:var(--fd);font-size:17px;letter-spacing:-.016em;font-weight:600;margin:0 0 4px}
.card .sub{color:var(--muted);font-size:13.5px;margin:0 0 14px;line-height:1.5}

.sectionlabel{font-size:12.5px;font-weight:600;color:var(--steel);margin:22px 0 10px}

.pill{display:inline-block;padding:4px 11px;border-radius:var(--r-tag);font-size:12px;font-weight:600;background:var(--line-2);color:var(--muted)}
.pill.ok{background:var(--signal-lo);color:#0A5C31}
.pill.warn{background:var(--amber-lo);color:#7A5510}
.pill.bad{background:var(--rust-lo);color:#8A331E}

.meta-list{list-style:none;margin:0;padding:0}
.meta-list li{display:flex;justify-content:space-between;gap:16px;padding:11px 0;border-bottom:1px solid var(--line-2);font-size:14px}
.meta-list li:last-child{border-bottom:0}
.meta-list .k{color:var(--muted)}
.meta-list .v{font-weight:600;text-align:right}

/* short viewports: tighten the stack so the picker fits without scrolling */
@media (max-height:780px){
  .auth h1{font-size:26px}
  .auth h1 + .roles,
  .auth h1 + form{margin-top:12px}
  .roles{gap:10px}
  .role{padding:14px;border-radius:16px}
  .role .ic{width:44px;height:44px;border-radius:13px}
  .role .ic svg{width:21px;height:21px}
  .role p{font-size:12.5px}
  .authfield{margin-bottom:13px}
  .auth .legal{margin-top:14px}
}

@media (max-height:640px){
  .auth h1{font-size:23px}
}

@media (max-width:480px){
  .app-header{padding:11px 14px}
  .app-header .who .name{display:none}
  .wrap{padding:18px 14px 48px}
  .auth-panel{padding:20px 20px 24px}
  .auth h1{font-size:27px}
}

/* ==========================================================================
   Passkeys (Face ID / fingerprint)
   ========================================================================== */

.pk-btn{display:flex;align-items:center;justify-content:center;gap:10px;
  background:#fff;border:1px solid #84968C;color:var(--forest)}
.pk-btn:hover{background:#F4F6F3}
.pk-btn svg{width:20px;height:20px;flex:none;color:var(--signal)}

.pk-or{display:flex;align-items:center;gap:12px;margin:20px 0 18px;
  font-size:12.5px;color:#5E6F65}
.pk-or::before,.pk-or::after{content:"";flex:1;height:1px;background:#E2E8E3}

.pk-card{border:1px solid #E2E8E3;border-radius:18px;padding:22px 18px 18px;
  background:#F4F6F3;
  text-align:center;margin-bottom:6px}
.pk-icon{width:62px;height:62px;margin:0 auto 18px;border-radius:19px;display:grid;place-items:center;
  background:var(--signal-lo);border:1px solid #CDE7D9;color:var(--signal)}
.pk-icon svg{width:30px;height:30px}

.pk-status{font-size:12.5px;color:#5E6F65;margin:12px 0 0;min-height:1em}
.pk-status.err{color:#8A331E}

.link-danger{color:var(--rust);text-decoration:none;font-weight:600;border-bottom:1px solid rgba(163,63,39,.35)}
.link-danger:hover{border-bottom-color:var(--rust)}
.card .pk-status{color:var(--muted);text-align:left}
.card .pk-status.err{color:var(--rust)}
.card .btn.sm{margin-top:4px}

/* ==========================================================================
   Signed-in app shell — app bar, screen, tabs
   Ported from the v141 concept and widened for the desktop.
   ========================================================================== */

body.app{display:flex;flex-direction:column;min-height:100dvh;background:var(--paper)}

.appbar{flex:none;position:sticky;top:0;z-index:20;background:var(--forest);color:#fff;padding:14px 16px 16px;display:flex;align-items:center;gap:12px}
/* The mark is 4:3, not square — the box is sized so its drawn width matches
   what the old square mark filled. */
.appbar .mark{width:34px;height:34px;flex:none}
.appbar .abtxt{flex:1;min-width:0}
.appbar h2{font-family:var(--fd);font-size:17px;font-weight:600;margin:0;letter-spacing:-.022em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.appbar .sub{font-size:12.5px;color:#8FBFA6;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.iconbtn{flex:none;width:36px;height:36px;border-radius:var(--r-ctl);border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.07);color:#fff;display:grid;place-items:center;cursor:pointer;text-decoration:none}
.iconbtn:hover{background:rgba(255,255,255,.14)}
.iconbtn svg{width:17px;height:17px}

body.app .scroll{flex:1}
/* `--gutter` is the page's own side padding, published so anything that has to
   bleed to the screen edge can pull back by exactly the right amount rather
   than by a number that was right once. See `.filters`. */
body.app .pad{--gutter:18px;max-width:860px;margin:0 auto;padding:22px var(--gutter) 108px}

/* ==========================================================================
   Screen furniture
   ========================================================================== */

h3.title{font-family:var(--fd);font-size:29px;line-height:1.06;letter-spacing:-.04em;font-weight:600;margin:0 0 7px}
body.app p.sub{margin:0 0 20px;font-size:14.5px;color:var(--muted);line-height:1.55}
body.app .sectionlabel{font-family:var(--fd);font-size:15px;font-weight:600;letter-spacing:-.02em;color:var(--text);text-transform:none;margin:30px 0 12px}
body.app .list{display:grid;gap:10px}
body.app .list .card{margin-bottom:0;padding:0;box-shadow:none}
body.app .card.pad-card{padding:20px}
body.app .card.pad-card .sub{margin-bottom:14px}

.search{display:flex;align-items:center;gap:10px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-ctl);padding:13px 14px;margin-bottom:18px}
.search svg{width:17px;height:17px;stroke:var(--steel);fill:none;stroke-width:2;flex:none}
.search input{border:none;background:none;font:500 14.5px var(--fb);color:var(--text);width:100%;outline:none}
.search input::placeholder{color:var(--steel);font-weight:400}
.search input:disabled{background:none;cursor:default}

/* ==========================================================================
   Hero panel — the headline number on the Staff screen
   ========================================================================== */

.hero{background:var(--forest);color:#fff;border-radius:var(--r-surface);padding:22px;margin-bottom:12px;position:relative;overflow:hidden}
.hero::after{content:"";position:absolute;right:-40px;top:-40px;width:190px;height:190px;border-radius:50%;background:none}
.hero .k{font-size:13px;color:#8FD3AE;font-weight:500;position:relative}
.hero b{display:block;font-family:var(--fd);font-size:44px;font-weight:600;letter-spacing:-.05em;margin:10px 0 6px;position:relative;line-height:1}
.hero em{font-style:normal;font-size:13px;color:#9EC6B0;position:relative;line-height:1.5;display:block}
.herobar{height:6px;border-radius:var(--r-pill);background:rgba(255,255,255,.14);margin-top:18px;overflow:hidden;position:relative}
.herobar i{display:block;height:100%;border-radius:var(--r-pill);background:var(--signal-mid);transition:width .55s cubic-bezier(.2,.75,.3,1)}
.herofoot{display:flex;justify-content:space-between;margin-top:10px;position:relative;font-size:12.5px;color:#9EC6B0}

/* ==========================================================================
   Stats
   ========================================================================== */

.stats{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:4px}
.stat{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:15px 13px}
.stat b{display:block;font-family:var(--fd);font-size:26px;line-height:1;color:var(--forest);letter-spacing:-.04em}
.stat span{display:block;font-size:11.5px;color:var(--steel);margin-top:7px;line-height:1.3}
.stat.hot{border-color:#D6E1F3;background:var(--indigo-lo)}
.stat.hot b{color:var(--indigo)}
.stat.hot span{color:#4A6796}

/* ==========================================================================
   Notice strip
   ========================================================================== */

.notice{display:flex;gap:12px;align-items:flex-start;margin-top:14px;background:var(--amber-lo);border:1px solid #F0E2C4;border-radius:var(--r-card);padding:14px 15px;color:#7A5510}
.notice svg{width:19px;height:19px;flex:none;margin-top:1px}
.notice b{display:block;font-size:13.5px;font-weight:600;letter-spacing:-.01em}
.notice span{display:block;font-size:12.5px;margin-top:3px;line-height:1.45;color:#8A6320}

/* ==========================================================================
   Development cards
   ========================================================================== */

/* Lee, 23 Sept 2026: *"make the development cards look way more premium… i
   just feel like these cards are quite basic."* A watermark of the logo was
   tried first and did not land. What did, chosen from two mock-ups: the
   client's own colour as a rail down the left edge and a caption under the
   site name; the open count inside a ring of the site's progress, so the two
   figures that matter are one object; a paper footer band flush to the
   card's edges; a gradient bar with the percentage in a badge; the logo tile
   on a soft shadow rather than a border. Padding and type sizes are exactly
   as they were — "Dont change the size of the cards." */
.card.site{padding:15px 15px 15px 14px !important;position:relative;overflow:hidden;border-color:#E6ECE7;border-left:4px solid var(--acc,var(--forest));background:#FBFCFB;box-shadow:0 1px 2px rgba(6,24,15,.04), var(--lift-2)}
.site .srow{display:flex;gap:14px;align-items:center}
.site .sbody{flex:1;min-width:0}
.site .nm{font-family:var(--fd);font-weight:600;font-size:16.5px;letter-spacing:-.024em;display:block}
.site .meta{font-size:12.5px;font-weight:600;color:var(--acc,var(--muted));margin-top:4px;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* The caption is one line that ellipsises — which needs every box between it
   and the grid to be allowed to shrink. `.sbody` already says `min-width:0`;
   the drag wrapper round each card did not, so on a phone a long caption
   pushed the whole card 50px past the screen. Lee: "make sure it fits on
   mobile as it doesnt at the minute." */
.dragrow,.list.sites > *{min-width:0}
.site .sbody{overflow:hidden}
/* The open count — a plain number. A ring of the progress round it was
   tried and taken off again the same afternoon: "remove the ring". */
.site .count{margin-left:auto;text-align:right;flex:none;min-width:46px}
.site .count b{display:block;font-family:var(--fd);font-size:22px;font-variant-numeric:tabular-nums;line-height:1;color:var(--forest);letter-spacing:-.03em}
.site .count span{font-size:11.5px;color:var(--steel);display:block;margin-top:4px}
.sthumb{width:84px;height:60px;flex:none;border-radius:10px;overflow:hidden;background:#DCE9F2;display:block;box-shadow:0 0 0 1px var(--line-2),0 4px 10px -6px rgba(6,24,15,.25)}
.sthumb .elev{display:block;width:100%;height:100%}

.pfoot{display:flex;align-items:center;gap:11px;margin-top:12px}
.minibar{flex:1;height:5px;border-radius:var(--r-pill);background:var(--line-2);overflow:hidden;display:block}
.minibar i{display:block;height:100%;border-radius:var(--r-pill);background:var(--signal);transition:width .55s cubic-bezier(.2,.75,.3,1)}
.minibar.amber i{background:var(--amber)}
.mv{font:600 12px var(--fb);color:var(--muted);flex:none;padding:2px 7px;border-radius:var(--r-tag);background:var(--line-2);text-align:right}
.mv.ok{color:var(--signal);background:var(--signal-lo)} .mv.amber{color:var(--amber);background:var(--amber-lo)}

/* The footer as a band flush to the card's edges: the same height as the
   row it replaces (negative margins cancel the padding exactly). */
.cardrow{display:flex;align-items:center;gap:10px;margin:12px -15px -15px -14px;padding:11px 15px 13px 14px;background:var(--paper);border-top:1px solid var(--line-2);font-size:12.5px;color:var(--muted)}
.cardrow span:first-child{flex:1}

/* ==========================================================================
   Action tiles
   ========================================================================== */

.tiles{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}
.tile{position:relative;text-align:left;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:16px 15px 17px;cursor:pointer;font:inherit;color:inherit;display:block;transition:border-color .16s,transform .09s}
.tile:hover:not(:disabled){border-color:var(--signal-mid);transform:translateY(-1px)}
.tile .ti{width:40px;height:40px;border-radius:12px;background:var(--signal-lo);color:var(--signal);display:grid;place-items:center;margin-bottom:13px}
.tile .ti svg{width:20px;height:20px}
.tile .tn{display:block;font-family:var(--fd);font-size:15px;font-weight:600;letter-spacing:-.02em}
.tile .tm{display:block;font-size:12px;color:var(--muted);margin-top:4px;line-height:1.4}
.tile .tag{position:absolute;top:13px;right:13px;font:600 12.5px var(--fb);background:var(--line-2);color:var(--steel);border-radius:var(--r-tag);padding:3px 8px}
.tile:disabled{cursor:default;opacity:.72}
.tile:disabled .ti{background:var(--line-2);color:var(--steel)}

/* ==========================================================================
   Plot rows
   ========================================================================== */

.card.plot{padding:13px !important}
.plot .prow{display:flex;gap:13px;align-items:center}
.plot .pbody{flex:1;min-width:0}
.pmark{width:46px;height:46px;flex:none;border-radius:13px;background:var(--signal-lo);color:var(--signal);display:grid;place-items:center;font:700 14px var(--fb);letter-spacing:-.02em}
.plot .pnum{font-family:var(--fd);font-size:16px;font-weight:600;letter-spacing:-.026em;display:block}
/* Called pnum and not no: .pmark carries a tone class, and the tone for a
   failed or rejected thing is .no — so a rejected plot's number square matched
   this rule too, took display:block, and stopped centring itself. */
.plot .ht{font-size:12.5px;color:var(--muted);margin-top:2px;display:block}
.plot .chips{display:flex;flex-wrap:wrap;gap:7px;margin-top:8px}

.chip{display:inline-flex;align-items:center;gap:6px;border-radius:7px;padding:4px 9px;font:600 11px var(--fb);letter-spacing:-.005em;white-space:nowrap}
.chip::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor;flex:none}
.chip.na{background:#EEF1EF;color:#6B7E73}
.chip.go{background:var(--amber-lo);color:var(--amber)}
.chip.wait{background:var(--indigo-lo);color:var(--indigo)}
.chip.ok{background:var(--signal-lo);color:var(--signal)}
.chip.no{background:var(--rust-lo);color:var(--rust)}
.hand{display:inline-flex;align-items:center;border-radius:6px;padding:4px 8px;font:600 11px var(--fb);background:#EDF1EE;color:#5A6D62;white-space:nowrap}
.hand.warn{background:var(--amber-lo);color:var(--amber)}

/* ==========================================================================
   You
   ========================================================================== */

.idcard{display:flex;align-items:center;gap:14px;padding:16px !important}
.idbody b{display:block;font-family:var(--fd);font-size:17px;font-weight:600;letter-spacing:-.022em}
.idbody span{display:block;font-size:13px;color:var(--muted);margin-top:3px}
body.app .btn.link{color:var(--muted)}
body.app .btn.link:hover{color:var(--rust)}

/* ==========================================================================
   Tabs
   ========================================================================== */

/* Docked to the bottom edge, dark like the app bar so the screen is bookended
   top and bottom. No float, no glow: the section you are in is marked by a
   short green rule above it and a brighter label, and nothing else. */
.tabbar{position:fixed;z-index:40;display:flex;left:0;right:0;bottom:0;padding:0 4px calc(10px + env(safe-area-inset-bottom));background:var(--forest);border-top:1px solid rgba(255,255,255,.08)}
.tabbar a{flex:1;display:flex;flex-direction:column;align-items:center;gap:5px;text-decoration:none;padding:11px 2px 7px;gap:5px;font:600 10px var(--fb);letter-spacing:.005em;color:#8FBFA6;position:relative;transition:color .18s}
.tabbar svg{width:21px;height:21px;fill:none;stroke:currentColor;stroke-width:1.85;stroke-linecap:round;stroke-linejoin:round;transition:transform .18s}
.tabbar a:hover{color:#C3E5D3}
.tabbar a:active svg{transform:scale(.92)}

/* A dot under the label rather than a rule along the top edge: it cannot end
   up straddling the bar's border, and it reads as quietly as it should. */
.tabbar a::after{content:"";display:block;width:4px;height:4px;border-radius:50%;background:transparent;transition:background .18s}
.tabbar a[aria-current="true"]{color:#fff}
.tabbar a[aria-current="true"]::after{background:var(--signal-mid)}
.tabbar a[aria-current="true"] svg{color:var(--signal-mid)}
.tabbar a.soon{opacity:.4;pointer-events:none}

@media (min-width:720px){
  .tabbar{padding-left:max(4px, calc((100vw - 860px) / 2));padding-right:max(4px, calc((100vw - 860px) / 2))}
}

@media (max-width:480px){
  body.app .pad{--gutter:14px;padding:18px var(--gutter) 102px}
  .hero{padding:19px}
  .hero b{font-size:38px}
  .stats{gap:8px}
  .stat{padding:13px 11px}
  .stat b{font-size:23px}
  .tiles{gap:8px}
  .sthumb{width:70px;height:52px}
  h3.title{font-size:26px}
}

/* ==========================================================================
   Filters
   ========================================================================== */

/* A full-bleed scroll strip: it pulls back by the page's gutter so the chips
   start at the text's left edge and the strip itself reaches the screen edge.

   The bleed was hardcoded at 18px, and the gutter is 14px below 480px and 26px
   above 960px — so on every phone the strip was 8px wider than the screen and
   the **whole page rubber-banded sideways**, on the developments screen, the
   safety log, the activity feed and everywhere else this appears. Nothing
   looked wrong; the page just moved. `--gutter` is published by `.pad` so the
   two can no longer disagree. */
.filters{display:flex;gap:7px;overflow-x:auto;margin:0 calc(var(--gutter, 18px) * -1) 12px;padding:0 var(--gutter, 18px) 4px;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.filters::-webkit-scrollbar{display:none}
.filters a{flex:none;border:1px solid var(--line);background:var(--card);border-radius:var(--r-ctl);padding:8px 14px;font:600 12.5px var(--fb);color:var(--muted);text-decoration:none;white-space:nowrap}
.filters a:hover{border-color:var(--signal-mid)}
.filters a[aria-pressed="true"]{background:var(--forest);border-color:var(--forest);color:#fff}
.filters.types{margin-bottom:18px}
/* From the rail up the row is not a phone strip that obviously scrolls, so the
   right edge fades to say there is more. A wrapper rather than the row itself:
   a mask on a scrolling element clips its own overflow. */
@media (min-width:960px){
  .filterwrap{position:relative}
  .filterwrap::after{content:"";position:absolute;top:0;right:0;bottom:4px;width:56px;pointer-events:none;background:var(--paper);border-radius:0 var(--r-pill) var(--r-pill) 0}
  .filterwrap .filters{margin-left:0;margin-right:0;padding-left:0;padding-right:56px}
}
.filters.types a{font-weight:500}

/* ==========================================================================
   Plot board — picking plots off a schedule
   ========================================================================== */

.boardbar{display:flex;align-items:center;gap:12px;margin-bottom:11px}
.bcount{font-size:13px;color:var(--muted)}
.bcount b{font-family:var(--fd);font-size:16px;color:var(--forest);letter-spacing:-.02em}
.bacts{margin-left:auto;display:flex;gap:14px}
.linky{background:none;border:none;padding:0;cursor:pointer;font:600 13px var(--fb);color:var(--signal)}
.linky:hover{text-decoration:underline}

.board{display:grid;grid-template-columns:repeat(auto-fill,minmax(62px,1fr));gap:7px}
.cell{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;aspect-ratio:1;border:1px solid var(--line);border-radius:11px;background:var(--card);cursor:pointer;transition:background .14s,border-color .14s;overflow:hidden}
.cell input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
.cell .n{font-family:var(--fd);font-size:14.5px;font-weight:600;letter-spacing:-.026em;color:var(--text)}
.cell .t{font-size:12.5px;font-weight:600;color:var(--steel)}
.cell:hover{border-color:var(--signal-mid)}
.cell.nohand{border-style:dashed;border-color:#E0CFA4}
.cell:has(input:checked){background:var(--signal);border-color:var(--signal)}
.cell:has(input:checked) .n{color:#fff}
.cell:has(input:checked) .t{color:rgba(255,255,255,.72)}
.cell:has(input:focus-visible){outline:2px solid var(--forest);outline-offset:2px}

.legend{display:flex;flex-wrap:wrap;gap:16px;margin-top:16px;font-size:12px;color:var(--muted)}
.legend span{display:inline-flex;align-items:center;gap:7px}
.legend .sw{width:12px;height:12px;border-radius:4px;border:1px solid var(--line);display:block}
.legend .sw.on{background:var(--signal);border-color:var(--signal)}
.legend .sw.off{background:var(--card)}
.legend .sw.warn{background:var(--card);border-style:dashed;border-color:#E0CFA4}

.stickysave{position:sticky;bottom:calc(78px + env(safe-area-inset-bottom));z-index:10;margin-top:20px;padding:12px 0 0;background:var(--paper)}

/* ==========================================================================
   Forms inside the app shell
   ========================================================================== */

body.app .input{margin-bottom:15px}
body.app .input:last-child{margin-bottom:0}
body.app .input label{display:block;font:600 12.5px var(--fb);color:var(--muted);margin-bottom:7px}
body.app .input input[type=text],
body.app .input input[type=date],
body.app .input input[type=number],
body.app .input select,
body.app .input textarea{width:100%;border:1px solid var(--line);border-radius:var(--r-ctl);padding:13px;font:500 15px var(--fb);color:var(--text);background:var(--paper);outline:none}
body.app .input select{appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--steel) 50%),
  linear-gradient(135deg,var(--steel) 50%,transparent 50%);background-position:calc(100% - 19px) 21px, calc(100% - 14px) 21px;background-size:5px 5px,5px 5px;background-repeat:no-repeat}
body.app .input textarea{resize:vertical;line-height:1.5}
body.app .input input:focus,
body.app .input select:focus,
body.app .input textarea:focus{border-color:var(--signal-mid);background:var(--card)}
body.app .input .error-message{color:var(--rust);font-size:12.5px;margin-top:6px;font-weight:500}
body.app .input.error input,
body.app .input.error select,
body.app .input.error textarea{border-color:var(--rust);background:var(--rust-lo)}
body.app .btn + .btn{margin-top:9px}
body.app form > .btn{margin-top:14px}

@media (max-width:480px){
  .board{grid-template-columns:repeat(auto-fill,minmax(54px,1fr));gap:6px}
  .cell{border-radius:9px}
  .cell .n{font-size:13.5px}
  .balance{padding:19px}
}

/* ==========================================================================
   Avatars — a face, or initials
   ========================================================================== */

.avatar{--av:44px;width:var(--av);height:var(--av);flex:none;border-radius:calc(var(--av) * .3);background:var(--forest);color:#fff;display:grid;place-items:center;overflow:hidden;font:700 calc(var(--av) * .33) var(--fb);letter-spacing:-.01em;line-height:1}
.avatar img{width:100%;height:100%;object-fit:cover;display:block}
.avatar.has-photo{background:var(--line-2)}

a.pphoto{display:block;flex:none;border-radius:calc(44px * .3);text-decoration:none;transition:opacity .14s,box-shadow .14s}
a.pphoto:hover{opacity:.88;box-shadow:0 0 0 3px rgba(6,137,67,.22)}


/* ==========================================================================
   People — a list you can read, not a control panel
   ========================================================================== */

.pagehead{display:flex;align-items:flex-start;gap:16px;margin-bottom:4px}
.pagehead > div{flex:1;min-width:0}
.pagehead .btn{flex:none;margin-top:4px}

/* `.dup` marks a page whose title is the app bar's title. On a phone the bar is
   80px above with the same word in it, and "People" printed twice in the first
   screenful was the design review's most repeated note; the sub line and the
   button stay. From the rail up the bar is chrome and the page keeps its own
   heading. */
@media (max-width:959px){
  .pagehead.dup .title{display:none}
  .pagehead.dup .sub{margin-top:2px}
  .pagehead.dup .btn{margin-top:0}
}

.card.plist{padding:0 !important;overflow:hidden}

a.prow{display:grid;grid-template-columns:40px minmax(0,1fr) auto 16px;align-items:center;column-gap:13px;padding:11px 14px;border-bottom:1px solid var(--line-2);text-decoration:none;transition:background .12s}
a.prow:last-child{border-bottom:none}
a.prow:hover{background:var(--signal-lo)}
a.prow:active{background:#DDEDE4}

.pwho{min-width:0}
a.prow .pname{color:var(--text)}
a.prow:hover .pname{color:var(--forest)}
.pname{display:block;font-family:var(--fd);font-size:14.5px;font-weight:600;letter-spacing:-.018em;line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.plogin{display:block;font:500 12px var(--fb);color:var(--steel);margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

a.prow .chip{justify-self:end}
.pchev{width:16px;height:16px;color:var(--steel);flex:none}

a.prow.is-off{background:#FAFBFA}
a.prow.is-off .avatar{background:var(--steel)}
a.prow.is-off .pname{color:var(--muted)}

.count-tag{display:inline-block;margin-left:8px;padding:2px 8px;border-radius:var(--r-tag);background:var(--line-2);color:var(--steel);font:600 11.5px var(--fb);vertical-align:middle}

/* Very narrow: the status word costs more than it earns, so it goes. */
@media (max-width:380px){
  a.prow{grid-template-columns:40px minmax(0,1fr) 16px}
  a.prow .chip{display:none}
  a.prow.is-off .pname::after{content:" · off";color:var(--steel);font-weight:500}
}

/* ==========================================================================
   One person
   ========================================================================== */

.personcard{text-align:center;padding:26px 20px 22px !important}
.personcard .pphoto{display:inline-block;margin-bottom:16px}
.pcname{font-family:var(--fd);font-size:22px;font-weight:600;letter-spacing:-.03em;margin:0 0 4px}
.pclogin{font:500 13px var(--fb);color:var(--muted);margin:0 0 12px;word-break:break-word}
.personcard .btn{margin-top:18px}

.plist.detail .drow{display:flex;align-items:baseline;gap:16px;padding:12px 16px;border-bottom:1px solid var(--line-2);font-size:13.5px}
.plist.detail .drow:last-child{border-bottom:none}
.plist.detail .dk{flex:none;width:112px;color:var(--muted)}
.plist.detail .dv{flex:1;font-weight:600;text-align:right}

/* ==========================================================================
   Photo picker
   ========================================================================== */

.photo-card{text-align:center}
.photo-card .avatar{margin:2px auto 18px}
.photo-form{margin-bottom:12px}
.filepick{display:block;border:1px dashed var(--line);border-radius:var(--r-ctl);padding:16px;margin-bottom:12px;cursor:pointer;background:var(--paper);transition:border-color .14s,background .14s}
.filepick:hover{border-color:var(--signal-mid);background:var(--card)}
.filepick span{font:600 13.5px var(--fb);color:var(--muted)}
.filepick input{position:absolute;width:1px;height:1px;opacity:0;overflow:hidden}
.filepick:focus-within{border-color:var(--signal);background:var(--card)}

/* ==========================================================================
   Buttons are buttons — no link underline anywhere in the app shell
   ========================================================================== */

/* Links rendered as buttons must keep the button's own colour — a blanket
   `a { color }` here turns a green button's label green on green, and paints
   every name in a tappable row. Style the classes, never the element. */
body.app a.btn,
body.app button.btn{text-decoration:none;display:block;text-align:center;color:#fff}
body.app a.btn:hover,
body.app button.btn:hover{color:#fff}
body.app .btn.sm{display:inline-block;width:auto}
body.app a.btn.ghost,
body.app button.btn.ghost{color:var(--forest)}
body.app a.btn.ghost.danger,
body.app button.btn.ghost.danger{color:var(--rust);border-color:#EBD3CB}
body.app .btn.ghost.danger:hover{background:var(--rust-lo);color:var(--rust)}
body.app a.btn.link{color:var(--muted)}
body.app a.btn.link:hover{color:var(--rust)}
body.app .linky{color:var(--signal);text-decoration:none}
body.app .linky:hover{text-decoration:underline}
body.app .idcard .idbody .linky{margin-top:7px;display:inline-block}

/* ==========================================================================
   Leave — requests
   ========================================================================== */

.req .reqhead{display:flex;align-items:center;gap:12px}
.reqwho{flex:1;min-width:0}
.reqwho b{display:block;font-family:var(--fd);font-size:15.5px;font-weight:600;letter-spacing:-.022em}
.reqwho span{display:block;font-size:12.5px;color:var(--muted);margin-top:2px}
.reqdays{flex:none;text-align:right}
.reqdays b{font-family:var(--fd);font-size:15px;font-weight:600;color:var(--forest);letter-spacing:-.02em}

.reqwhen{display:flex;align-items:center;gap:9px;margin-top:13px;padding:10px 12px;background:var(--paper);border-radius:var(--r-ctl);font:600 13.5px var(--fb);color:var(--text)}
.reqwhen svg{width:16px;height:16px;flex:none;color:var(--steel)}
.reqnote{margin:11px 0 0;font-size:13px;color:var(--muted);line-height:1.5;padding-left:12px;border-left:2px solid var(--line)}

.notice.tight{margin-top:12px;padding:11px 13px}
.notice.tight b{font-size:13px}
.notice.tight span{font-size:12px}

.decide{margin-top:14px}
.notefield{width:100%;border:1px solid var(--line);border-radius:var(--r-ctl);padding:11px 13px;font:500 13.5px var(--fb);color:var(--text);background:var(--paper);outline:none;margin-bottom:10px}
.notefield:focus{border-color:var(--signal-mid);background:var(--card)}
.decide .input{margin:0}

.quick{display:flex;gap:9px}
.qa{flex:1;border-radius:var(--r-ctl);padding:12px;cursor:pointer;font:600 13.5px var(--fb);border:1px solid var(--line);background:var(--card);color:var(--muted);transition:all .14s}
.qa.yes{border-color:#CDE7D9;color:var(--signal)}
.qa.yes:hover{background:var(--signal);border-color:var(--signal);color:#fff}
.qa.no:hover{background:var(--rust-lo);border-color:var(--rust);color:var(--rust)}

/* ---- An empty state, drawn ----------------------------------------------
   Paper rather than white and a dashed edge, so it reads as "nothing here
   yet" and not as a card that failed to fill. The glyph sits in a white disc
   on a faint horizon line — a small scene rather than an icon and a sentence.
   `templates/element/empty.php` renders it; twelve older screens carry the
   same markup by hand. */
.allclear{text-align:center;padding:30px 20px 26px !important;background:var(--paper);border:1px dashed var(--line);box-shadow:none;position:relative;overflow:hidden}
.allclear::before{content:"";position:absolute;left:12%;right:12%;top:74px;height:1px;background:var(--line)}
.allclear svg{position:relative;box-sizing:content-box;width:30px;height:30px;padding:29px;color:var(--steel);background:var(--card);border-radius:50%;box-shadow:0 0 0 7px rgba(255,255,255,.6),var(--lift-1);margin:0 auto 16px;display:block}
.allclear b{display:block;font-family:var(--fd);font-size:16.5px;font-weight:600;letter-spacing:-.022em;color:var(--text)}
.allclear span{display:block;font-size:13px;color:var(--muted);margin:5px auto 0;line-height:1.5;max-width:42ch}
.allclearact{margin-top:14px}
.allclearact .btn{width:auto;display:inline-block;margin:0 auto}
.allclear .btn{width:auto;display:inline-block;margin-top:14px}
.allclear p.sub{margin:5px auto 0;font-size:13px;line-height:1.5;max-width:42ch}

/* ==========================================================================
   Leave — the month
   ========================================================================== */

.cal{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:14px 12px 12px;margin-bottom:10px}
.calhead{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;padding:0 2px}
.calmonth{font-family:var(--fd);font-size:16.5px;font-weight:600;letter-spacing:-.024em}
.calnav{width:32px;height:32px;border-radius:10px;border:1px solid var(--line);color:var(--muted);display:grid;place-items:center;text-decoration:none;transition:all .14s}
.calnav:hover{border-color:var(--signal-mid);color:var(--signal);background:var(--signal-lo)}
.calnav svg{width:15px;height:15px}

.caldows{display:grid;grid-template-columns:repeat(7,1fr);gap:3px;margin-bottom:5px}
.caldows span{text-align:center;font:600 10.5px var(--fb);letter-spacing:.06em;color:var(--steel)}

.calgrid{display:grid;grid-template-columns:repeat(7,1fr);gap:3px}
.calday{aspect-ratio:1;border-radius:9px;background:var(--paper);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;position:relative;min-height:40px}
.cdn{font:600 13px var(--fb);color:var(--text);line-height:1}
/* A day outside the month is context, never a highlight — it wins over the
   weekend, bank holiday and off tints below. */
.calday.out,
.calday.out.wknd,
.calday.out.bank,
.calday.out.has,
.calday.out.thin{background:transparent;box-shadow:none}
.calday.out .cdn,
.calday.out.bank .cdn,
.calday.out.thin .cdn{color:#C8D2CC;font-weight:600}
.calday.out .cdbank,
.calday.out .cdpips{display:none}
.calday.wknd{background:#F0F3F0}
.calday.wknd .cdn{color:var(--steel)}
.calday.bank{background:var(--indigo-lo)}
.calday.bank .cdn{color:var(--indigo)}
.cdbank{font:700 8px var(--fb);letter-spacing:.06em;color:var(--indigo)}
.calday.has{background:var(--signal-lo)}
.calday.thin{background:var(--amber-lo);box-shadow:inset 0 0 0 1px #EFDFBE}
.calday.thin .cdn{color:var(--amber)}
.calday.today{box-shadow:inset 0 0 0 2px var(--forest)}
.calday.today .cdn{color:var(--forest);font-weight:700}

.cdpips{display:flex;align-items:center;gap:2px;height:7px}
.cdpips i{width:5px;height:5px;border-radius:50%;background:var(--signal);display:block}
.calday.thin .cdpips i{background:var(--amber)}
.cdpips b{font:700 8.5px var(--fb);color:var(--muted);margin-left:1px}

.calkey{display:flex;flex-wrap:wrap;gap:14px;margin-top:13px;padding-top:11px;border-top:1px solid var(--line-2);font-size:11.5px;color:var(--muted)}
.calkey span{display:inline-flex;align-items:center;gap:6px}
.calkey i{width:11px;height:11px;border-radius:4px;display:block}
.calkey .k-off{background:var(--signal-lo);box-shadow:inset 0 0 0 1px #CDE7D9}
.calkey .k-thin{background:var(--amber-lo);box-shadow:inset 0 0 0 1px #EFDFBE}
.calkey .k-bank{background:var(--indigo-lo);box-shadow:inset 0 0 0 1px #D6E1F3}

/* who is off, under the grid */
.offrow{display:flex;align-items:center;gap:12px;padding:12px 15px;border-bottom:1px solid var(--line-2)}
.offrow:last-child{border-bottom:none}
.offwho{flex:1;min-width:0}
.offwho b{display:block;font-family:var(--fd);font-size:14px;font-weight:600;letter-spacing:-.018em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.offwho span{display:block;font-size:12px;color:var(--steel);margin-top:1px}
.offwhen{flex:none;text-align:right}
.offwhen b{display:block;font:600 13px var(--fb);color:var(--text)}
.offwhen span{display:block;font-size:11.5px;color:var(--steel);margin-top:2px}
/* The chip keeps its own look inside the right-hand column; the Remove link
   sits under it. */
.offwhen .chip{display:inline-flex;font-size:11px;color:inherit;margin-top:0}
.offwhen .link-danger{display:inline-block;margin-top:6px;font-size:12px}

/* ==========================================================================
   Leave — your allowance
   ========================================================================== */

/* The crew's own panel. It held a ring counting down from a twenty-five day
   allowance until 22 Sept 2026, when the allowance was removed — nobody in this
   app ever had one. What is left is what the screen
   is actually for: the next day you are off, whether it has been answered, and
   the way to ask for another. `.ring`, `.ringmid` and `.ringkeys` went with it
   rather than being left in the file for something to reuse by accident. */

.balance{background:var(--forest);color:#fff;border-radius:var(--r-surface);padding:24px 22px;position:relative;overflow:hidden}
.balance::after{content:"";position:absolute;right:-50px;top:-50px;width:210px;height:210px;border-radius:50%;background:none}

.balancek{position:relative;display:block;font-size:12.5px;font-weight:600;color:#7FB39A}
.balancenext{position:relative;display:block;margin-top:7px;font-family:var(--fd);font-size:27px;font-weight:600;letter-spacing:-.03em;line-height:1.15}
/* `display:block`, not inline-block. Inline-block put it on the same line as
   the Book button — which is also inline-block above 760px — so the sentence
   ran straight into the green. */
.balancestate{position:relative;display:block;margin-top:8px;font-size:12.5px;color:#9EC6B0}
/* The one state worth a colour: still waiting on an answer. A row that has
   been approved says so in the list below and does not need saying twice. */
.balancestate.wait{color:#F2C65C}

/* ==========================================================================
   Leave — your requests
   ========================================================================== */

.leaverow{display:flex;align-items:flex-start;gap:12px;padding:14px 15px 14px 0;border-bottom:1px solid var(--line-2);position:relative}
.leaverow:last-child{border-bottom:none}
.lstrip{width:3px;align-self:stretch;border-radius:0 3px 3px 0;flex:none;background:var(--line)}
.lstrip.ok{background:var(--signal)} .lstrip.wait{background:var(--indigo)}
.lstrip.no{background:var(--rust)} .lstrip.na{background:var(--steel)}
.leaverow .when{flex:1;min-width:0}
.leaverow .when b{display:block;font-family:var(--fd);font-size:14.5px;font-weight:600;letter-spacing:-.02em}
.leaverow .when > span{display:block;font-size:12.5px;color:var(--muted);margin-top:3px;line-height:1.45}
.lback{display:block;margin-top:6px;font-size:12.5px;color:var(--muted);font-style:italic}
.lright{flex:none;display:flex;flex-direction:column;align-items:flex-end;gap:6px;font-size:12.5px}
.lright .days{font:600 12.5px var(--fb);color:var(--steel)}

/* ==========================================================================
   The bell
   ========================================================================== */

.bell{position:relative;flex:none}
.bell > summary{list-style:none;width:36px;height:36px;border-radius:var(--r-ctl);border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.07);color:#fff;display:grid;place-items:center;cursor:pointer;position:relative}
.bell > summary::-webkit-details-marker{display:none}
.bell > summary:hover{background:rgba(255,255,255,.14)}
.bell > summary svg{width:17px;height:17px}
.belldot{position:absolute;top:-5px;right:-5px;min-width:18px;height:18px;padding:0 5px;border-radius:var(--r-pill);background:var(--rust);color:#fff;font:700 10px var(--fb);display:grid;place-items:center;border:2px solid var(--forest)}

.bellpop{position:absolute;top:calc(100% + 10px);right:0;width:min(320px,calc(100vw - 28px));background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);box-shadow:var(--lift-2);overflow:hidden;z-index:60;text-align:left}
.bellhead{padding:13px 15px;border-bottom:1px solid var(--line-2)}
.bellhead b{font-family:var(--fd);font-size:14px;font-weight:600;letter-spacing:-.018em;color:var(--text)}

.bitem{display:flex;align-items:flex-start;gap:11px;padding:12px 15px;text-decoration:none;border-bottom:1px solid var(--line-2);transition:background .12s}
.bitem:hover{background:var(--signal-lo)}
.bmark{width:32px;height:32px;flex:none;border-radius:10px;background:var(--signal-lo);color:var(--signal);display:grid;place-items:center}
.bmark svg{width:16px;height:16px}
.btext{min-width:0}
.btext b{display:block;font:600 13px var(--fb);color:var(--text);line-height:1.35}
.btext span{display:block;font-size:12px;color:var(--muted);margin-top:2px}

.bellall{display:block;padding:12px 15px;text-align:center;font:600 13px var(--fb);color:var(--signal);text-decoration:none;background:var(--paper)}
.bellall:hover{background:var(--signal-lo)}
.bellnone{margin:0;padding:15px;font-size:12.5px;color:var(--muted);line-height:1.5}

@media (max-width:480px){
  .calday{min-height:36px;border-radius:8px}
  .cdn{font-size:12px}
  .balancenext{font-size:24px}
  .offwhen b{font-size:12px}
}

/* ==========================================================================
   Your face, top right
   ========================================================================== */

.barme{flex:none;display:block;border-radius:11px;line-height:0;text-decoration:none;box-shadow:0 0 0 1px rgba(255,255,255,.22);transition:box-shadow .16s}
.barme:hover{box-shadow:0 0 0 2px rgba(95,190,139,.85)}
.barme .avatar{border-radius:11px;background:rgba(255,255,255,.12);color:#fff}

/* the bell now shows who is asking, so the row leads with a face */
.bitem .avatar{flex:none}

/* ==========================================================================
   Health and safety
   ========================================================================== */

/* The board at the site gate. One number, the one everybody already reads. */
.hsboard{background:var(--forest);color:#fff;border-radius:var(--r-surface);padding:20px 22px;margin-bottom:12px;position:relative;overflow:hidden}
.hsboard::after{content:"";position:absolute;right:-46px;top:-46px;width:190px;height:190px;border-radius:50%;background:none}
.hsboard .k{font-size:13px;color:#8FD3AE;font-weight:500;position:relative}
.hsboard b{display:block;font-family:var(--fd);font-size:44px;font-weight:600;letter-spacing:-.05em;line-height:1;margin:7px 0 8px;position:relative}
.hsboard em{font-style:normal;font-size:13px;color:#9EC6B0;position:relative;line-height:1.5;display:block}

/* The square mark that stands in for a face on a safety row. It takes the
   same chip colours as everything else, so a near miss reads amber and an
   accident reads rust without anybody having to learn a new key. */
/* The crew's way into the safety log, on their home screen. A `.prow` on its
   own card rather than inside a list, so it needs the three-column shape — the
   list version carries a status chip in column three and this has nothing to
   put there. */
a.prow.hsraise{grid-template-columns:40px minmax(0,1fr) 16px;border-bottom:none}
/* And it wraps. A list row truncates because somebody is scanning a column of
   references; this is one call to action, and "Report something unsaf…" is a
   tidier way of not asking. Gotcha 54: when the responsive answer is to cut
   the text off, it is the layout that is wrong. */
a.prow.hsraise .pname,
a.prow.hsraise .plogin{white-space:normal;overflow:visible;text-overflow:clip}

/* A lift plan on the crew's site screen. Three columns, because the signature
   count moved into the sub line — see the comment where the row is built. */
a.prow.lprow{grid-template-columns:40px minmax(0,1fr) 16px}

.hsmark{width:40px;height:40px;border-radius:12px;display:flex;align-items:center;justify-content:center;flex:none}
.hsmark svg{width:20px;height:20px}
.hsmark.na{background:#EEF1EF;color:#6B7E73}
.hsmark.go{background:var(--amber-lo);color:var(--amber)}
.hsmark.wait{background:var(--indigo-lo);color:var(--indigo)}
.hsmark.ok{background:var(--signal-lo);color:var(--signal)}
.hsmark.no{background:var(--rust-lo);color:var(--rust)}
.hsmark.big{width:52px;height:52px;border-radius:15px}
.hsmark.big svg{width:26px;height:26px}

.hshead{display:flex;align-items:flex-start;gap:14px;margin-bottom:6px}
.hshead > div{flex:1;min-width:0}
.hshead h3.title{font-size:24px;margin-bottom:5px}
.hshead p.sub{margin:0}
.hshead .chip{flex:none;margin-top:4px}

/* Two columns of label-over-value. Four facts, no table. */
.hsfacts{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:var(--line-2);overflow:hidden;padding:0 !important}
.hsfacts .fact{background:var(--card);padding:14px 16px;min-width:0}
.hsfacts .fact span{display:block;font:600 12.5px var(--fb);color:var(--steel)}
.hsfacts .fact b{display:block;font-family:var(--fd);font-size:14.5px;font-weight:600;letter-spacing:-.018em;margin-top:5px;line-height:1.35}

/* Somebody's own words, kept as they typed them. */
.prose{margin:0;font-size:14.5px;line-height:1.62;color:var(--text)}

/* ---- Picking the kind of thing that happened ---------------------------- */

.typepick{border:none;margin:0 0 14px;padding:0;display:grid;gap:8px}
.typepick legend{padding:0;font-family:var(--fd);font-size:15px;font-weight:600;letter-spacing:-.02em;color:var(--text);margin:22px 0 10px}
.tp{position:relative;display:block;cursor:pointer}
.tp input{position:absolute;opacity:0;width:1px;height:1px}
.tpb{display:block;border:1px solid var(--line);border-radius:var(--r-card);background:var(--card);padding:13px 15px;transition:border-color .13s,background .13s}
.tp:hover .tpb{border-color:var(--signal-mid)}
.tpn{display:block;font-family:var(--fd);font-size:14.5px;font-weight:600;letter-spacing:-.018em;color:var(--text)}
.tph{display:block;font-size:12.5px;color:var(--muted);margin-top:3px;line-height:1.45}
.tp input:checked + .tpb{border-color:var(--signal);background:var(--signal-lo);box-shadow:inset 0 0 0 1px var(--signal)}
.tp input:checked + .tpb .tpn{color:#0A5C31}
.tp input:focus-visible + .tpb{outline:2px solid var(--forest);outline-offset:2px}

.fielderr{color:var(--rust);font-size:12.5px;margin:4px 0 0;font-weight:500}

/* ---- The RIDDOR tick ---------------------------------------------------- */

.checkline{display:flex;gap:12px;align-items:flex-start;cursor:pointer}
.checkline input{width:20px;height:20px;flex:none;margin:1px 0 0;accent-color:var(--signal)}
.checkline b{display:block;font-size:14px;font-weight:600;letter-spacing:-.01em;color:var(--text)}
.checkline em{display:block;font-style:normal;font-size:12.5px;color:var(--muted);margin-top:4px;line-height:1.45}

/* The amber notice is for things wanting attention. A RIDDOR report is not
   that — it is a fact about the record, so it reads rust. */
.notice.rid{background:var(--rust-lo);border-color:#F0D6CC;color:var(--rust)}
.notice.rid b{color:#8A331E}
.notice.rid span{color:#9A5340}

/* ---- Evidence ----------------------------------------------------------- */

.shots{display:grid;grid-template-columns:repeat(auto-fill,minmax(132px,1fr));gap:9px}
.shot{position:relative;margin:0;border-radius:var(--r-card);overflow:hidden;border:1px solid var(--line);background:var(--card);aspect-ratio:4/3}
.shot a{display:block;width:100%;height:100%}
.shot img{width:100%;height:100%;object-fit:cover;display:block}
.shotx{position:absolute;top:7px;right:7px;border-radius:var(--r-pill);padding:5px 11px;background:rgba(6,24,15,.66);color:#fff !important;font:600 11.5px var(--fb);text-decoration:none;backdrop-filter:blur(3px)}
.shotx:hover{background:var(--rust)}

.shotadd{margin-top:12px}
.shotadd .btn{margin-top:10px}

/* ---- Fixes to the shell these screens turned up ------------------------- */

/* The tiles are anchors, and nothing had ever told an anchor inside the app
   shell not to underline itself, so every tile label carried a browser
   underline. */
a.tile{text-decoration:none;color:inherit}
a.tile:hover{color:inherit}

/* The date-and-time control was the first of its kind in the app, so it fell
   outside the list of inputs the shell styles and rendered raw. */
body.app .input input[type=datetime-local],
body.app .input input[type=time],
body.app .input input[type=email],
body.app .input input[type=tel]{width:100%;border:1px solid var(--line);border-radius:var(--r-ctl);padding:13px;font:500 15px var(--fb);color:var(--text);background:var(--paper);outline:none}
body.app .input input[type=datetime-local]:focus,
body.app .input input[type=time]:focus,
body.app .input input[type=email]:focus,
body.app .input input[type=tel]:focus{border-color:var(--signal-mid);background:var(--card)}

/* "Looking into it" is not a refusal, so it should not hover like one. */
.qa.alt:hover{background:var(--indigo-lo);border-color:var(--indigo);color:var(--indigo)}

/* ---- The form as a whole ------------------------------------------------ */

body.app .hsform .card{margin-bottom:0}
body.app .hsform > .btn{margin-top:22px}
body.app .legal{font-size:12px;color:var(--steel);line-height:1.65;margin:0}

@media (max-width:420px){
  .hsfacts{grid-template-columns:1fr}
  .hsboard b{font-size:38px}
  .shots{grid-template-columns:repeat(2,1fr)}
}

/* An odd number of tiles used to leave a hole in the second column. The last
   one spans instead, which reads as a decision rather than a gap. */
.tiles > :last-child:nth-child(odd){grid-column:1 / -1}

/* ==========================================================================
   Activity — the bell, the sheet, and the feed
   ========================================================================== */

:root{--barh:68px}   /* measured and overwritten by sheet.js on open */

.bellbtn{position:relative}
.belldot{position:absolute;top:-5px;right:-5px}

/* ---- The sheet ---------------------------------------------------------- */

/* Printed at the end of <body>, so it answers to no stacking context but its
   own — which is the whole point. Anchored to the bottom of the app bar rather
   than to the button, so it sits square across the screen instead of hanging
   off one corner of it. */
.sheet{position:fixed;inset:0;z-index:60}
.sheet[hidden]{display:none}

.sheetscrim{position:absolute;left:0;right:0;bottom:0;top:var(--barh);background:rgba(6,24,15,.44);opacity:0;transition:opacity .2s ease}
.sheet.is-open .sheetscrim{opacity:1}

.sheetpanel{position:absolute;top:var(--barh);left:0;right:0;max-height:min(calc(100dvh - var(--barh) - 96px), 540px);display:flex;flex-direction:column;background:var(--card);border-radius:0 0 var(--r-surface) var(--r-surface);box-shadow:0 4px 12px rgba(6,24,15,.08), 0 12px 32px -12px rgba(6,24,15,.28), 0 2px 6px rgba(6,24,15,.06);transform:translateY(-14px);opacity:0;transition:transform .22s cubic-bezier(.22,.9,.3,1), opacity .18s ease}
.sheet.is-open .sheetpanel{transform:none;opacity:1}

.sheetgrab{width:38px;height:4px;border-radius:var(--r-pill);background:var(--line);margin:9px auto 3px;flex:none}

.sheethead{display:flex;align-items:center;gap:9px;padding:6px 14px 11px;flex:none;border-bottom:1px solid var(--line-2)}
.sheethead b{font-family:var(--fd);font-size:15.5px;font-weight:600;letter-spacing:-.02em;color:var(--text);margin-right:auto}
.sheetnew{border-radius:var(--r-tag);background:var(--signal-lo);color:var(--signal);font:600 11.5px var(--fb);padding:4px 9px}
.sheetclear{font:600 12.5px var(--fb);color:var(--muted);text-decoration:none;padding:4px 2px}
.sheetclear:hover{color:var(--rust)}
.sheetx{flex:none;width:30px;height:30px;border-radius:9px;border:none;cursor:pointer;background:var(--line-2);color:var(--muted);display:grid;place-items:center}
.sheetx:hover{background:var(--rust-lo);color:var(--rust)}
.sheetx svg{width:15px;height:15px}

.sheetbody{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch}
.sheetnone{margin:0;padding:22px 16px;font-size:13px;color:var(--muted);line-height:1.6}

.sheetall{display:block;flex:none;padding:14px;text-align:center;text-decoration:none;font:600 13.5px var(--fb);color:var(--signal);border-top:1px solid var(--line-2)}
.sheetall:hover{background:var(--signal-lo)}

body.sheet-open{overflow:hidden}

/* ==========================================================================
   The menu sheet — the desktop rail's list, on a phone
   ==========================================================================
   Three things make this feel like a panel rather than a page swap:

   1. **It actually travels.** The other sheets nudge 14px and fade, which at
      the top of the screen reads as "appeared". This one comes the whole way
      up from off-screen and goes the whole way back down, so the gesture and
      the motion agree. `sheet.js` waits on the transform specifically before
      it hides the thing, or the panel would be taken out of the layout part
      way through its own slide.
   2. **Asymmetric timing.** 420ms up on a decelerating curve, 260ms down on an
      accelerating one — opening is an arrival and wants to settle; closing is
      a dismissal and wants to get out of the way. Equal timings in both
      directions are the single most common tell of an animation nobody
      thought about.
   3. **The rows arrive after the panel.** A short stagger, 18ms apart, capped
      so the last tile is in within a fifth of a second. Enough to read as
      assembled rather than printed; not enough to wait for.
*/
.sheet.frombottom{overflow:hidden}
.sheet.frombottom .sheetscrim{top:0;bottom:0;transition:opacity .32s ease}

.sheet.frombottom .sheetpanel{top:auto;bottom:0;max-height:calc(100dvh - 56px);border-radius:24px 24px 0 0;padding-bottom:max(10px, env(safe-area-inset-bottom));background:var(--card);box-shadow:0 -1px 0 rgba(10,31,22,.06), 0 12px 32px -12px rgba(6,24,15,.28), 0 2px 6px rgba(6,24,15,.06);opacity:1;transform:translateY(100%);transition:transform .26s cubic-bezier(.4,.02,.9,.36)}
.sheet.frombottom.is-open .sheetpanel{transform:translateY(0);transition:transform .42s cubic-bezier(.22,.86,.28,1)}

/* A wider, softer grab bar than the other sheets — this one is the size of a
   screen and wants a handle that looks like it could be dragged. */
.sheet.frombottom .sheetgrab{width:44px;height:5px;margin:10px auto 2px;background:var(--line);border-radius:var(--r-pill)}

.sheet.frombottom .sheethead{padding:4px 16px 12px;border-bottom:1px solid var(--line-2)}
.sheet.frombottom .sheethead b{font-size:19px;letter-spacing:-.03em}
/* The Frame Team mark beside "Menu" — the icon alone, the wordmark is the
   title. Lee, 23 Sept 2026. */
.sheetmark{width:30px;height:30px;flex:none;margin-right:1px}

.sheet.frombottom .sheetbody{padding-bottom:6px}

/* ---- the sections ------------------------------------------------------- */

.mngroup{display:block;padding:12px 18px 7px;font:600 12.5px var(--fb);color:var(--steel)}
.mngroup:first-child{padding-top:10px}

/* Two across, because thirteen destinations down a single column is a scroll
   and Staff asked to see the lot at once. Two also keeps the labels on one line
   — "Where they are" and "Health & safety" will not sit under a 30px icon in a
   third of a phone. */
.mnrows{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:0 14px}

.mnlink{display:flex;align-items:center;gap:9px;min-width:0;padding:9px 10px;border-radius:15px;text-decoration:none;background:var(--paper);border:1px solid var(--line-2);color:var(--text);font:600 12.5px var(--fb);letter-spacing:-.012em;transition:transform .14s ease, background .14s ease, border-color .14s ease}
.mnlink:active{transform:scale(.97);background:var(--line-2)}

.mnic{flex:none;width:29px;height:29px;border-radius:10px;display:grid;place-items:center;background:var(--card);border:1px solid var(--line-2);color:var(--muted);transition:background .14s ease, color .14s ease, border-color .14s ease}
.mnic svg{width:16px;height:16px}

/* A group with one thing in it takes the whole row rather than leaving a hole
   next to it. `:has` is doing the counting; where it is not supported the tile
   simply stays half width, which is what it looked like before. */
.mnrows:has(> :only-child){grid-template-columns:1fr}

/* Small phones. "Where they are" and "Health & safety" are the two labels that
   decide this width, and an ellipsis in a menu is worse than a point smaller. */
@media (max-width:374px){
  .mnrows{padding:0 10px;gap:6px}
  .mnlink{padding:9px 8px;gap:8px;font-size:12px}
  .mnic{width:27px;height:27px}
  .mnic svg{width:15px;height:15px}
  .mngroup{padding-left:14px}
}
.mnlbl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Where you already are, said once. The panel is a list of places to go, so
   the one you are standing on is context rather than an offer — it keeps the
   tint and loses the chevron. */
.mnlink[aria-current="page"]{background:var(--signal-lo);border-color:transparent;color:var(--signal)}
.mnlink[aria-current="page"] .mnic{background:var(--signal);border-color:transparent;color:#fff}

/* ---- who you are -------------------------------------------------------- */

.mnme{flex:none;display:flex;align-items:center;gap:10px;margin:10px 14px 0;padding:10px 12px;border-radius:15px;background:var(--forest);color:#fff}
.mnme .avatar{flex:none}
.mnme > span{flex:1;min-width:0;display:flex;flex-direction:column;line-height:1.25}
.mnme b{font:600 13px var(--fb);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mnme em{font-style:normal;font-size:11px;color:#8FBFA6}
.mnout{flex:none;font:600 12px var(--fb);color:#FFD9CE;text-decoration:none;padding:6px 11px;border-radius:var(--r-tag);background:rgba(255,255,255,.1)}
.mnout:active{background:rgba(255,255,255,.18)}

/* ---- the rows arrive after the panel ------------------------------------ */

/* Section by section rather than tile by tile: with thirteen tiles a per-tile
   stagger is a queue you watch, and the eye reads the groups anyway. */
.sheet.frombottom .sheetbody > *,
.sheet.frombottom .mnme{opacity:0;transform:translateY(8px)}
.sheet.frombottom.is-open .sheetbody > *,
.sheet.frombottom.is-open .mnme{opacity:1;transform:none;transition:opacity .26s ease, transform .32s cubic-bezier(.22,.86,.28,1)}
.sheet.frombottom.is-open .sheetbody > :nth-child(-n+2){transition-delay:.10s}
.sheet.frombottom.is-open .sheetbody > :nth-child(n+3):nth-child(-n+4){transition-delay:.14s}
.sheet.frombottom.is-open .sheetbody > :nth-child(n+5):nth-child(-n+6){transition-delay:.18s}
.sheet.frombottom.is-open .sheetbody > :nth-child(n+7):nth-child(-n+8){transition-delay:.21s}
.sheet.frombottom.is-open .sheetbody > :nth-child(n+9){transition-delay:.23s}
.sheet.frombottom.is-open .mnme{transition-delay:.25s}

/* Nothing here is information — it is all timing — so it all goes. */
@media (prefers-reduced-motion: reduce){
  .sheet.frombottom .sheetpanel,
  .sheet.frombottom.is-open .sheetpanel{transition:none}
  .sheet.frombottom .sheetbody > *,
  .sheet.frombottom .mnme{opacity:1;transform:none;transition:none}
}

/* The Menu tab is a button wearing a link's clothes: no page is ever "the
   menu", so it never takes the current-page dot. */
.tabbar a.tabmenu[aria-expanded="true"]{color:#fff}
.tabbar a.tabmenu[aria-expanded="true"] svg{color:var(--signal-mid)}

/* The rail takes over at 960px and the tab bar goes with it, so from there up
   nothing can open this and it has no business being in the layout either. */
@media (min-width:960px){
  .sheet.frombottom{display:none}
}

/* Above phone width a sheet the full width of a monitor is silly. Centred, and
   still nowhere near the corner of the button. */
@media (min-width:700px){
  .sheetpanel{left:50%;right:auto;transform:translate(-50%,-14px);width:min(520px,calc(100vw - 32px));border-radius:0 0 var(--r-surface) var(--r-surface)}
  .sheet.is-open .sheetpanel{transform:translate(-50%,0)}
}

@media (prefers-reduced-motion:reduce){
  .sheetpanel,.sheetscrim{transition:none}
  .sheetpanel{transform:none}
  @media (min-width:700px){
    .sheetpanel,.sheet.is-open .sheetpanel{transform:translate(-50%,0)}
  }
  /* The desktop drawer: in place, no slide, rows already there. */
  @media (min-width:960px){
    .sheet:not(.frombottom) .sheetpanel,.sheet:not(.frombottom).is-open .sheetpanel{transform:none;transition:none}
    .sheet:not(.frombottom) .sheetbody > *{opacity:1;transform:none;transition:none}
  }
}

/* ---- One line of the feed ----------------------------------------------- */

.card.actlist{padding:0 !important;overflow:hidden}

.actrow{position:relative;display:flex;align-items:flex-start;gap:12px;padding:12px 16px;border-bottom:1px solid var(--line-2);text-decoration:none}
.actrow:last-child{border-bottom:none}
a.actrow:hover{background:var(--signal-lo)}
a.actrow:active{background:#DDEDE4}

/* A face, with the verb as a badge on the corner of it. */
/* One feed line and the control that removes it.

   The button is a sibling of the row, never a child: most rows are links, and a
   control inside a link is a fight between pressing it and following the link
   that the link wins on a phone every time. So the wrapper is the grid and the
   row is just its first column. */
.actwrap{display:grid;grid-template-columns:minmax(0,1fr) 34px;align-items:stretch}
.actwrap > .actrow{min-width:0}

/* `postLink` emits a hidden form beside the anchor. It is `display:none` so it
   is not a grid item, but said out loud because a third column appearing here
   would be baffling. */
.actwrap > form{display:none}

/* Wrapping the rows broke `.actrow:last-child{border-bottom:none}` — the row is
   no longer the last child of the list, it is the first child of a wrapper, so
   the rule stopped matching and the bottom row grew a rule under it. Gotcha 45:
   after putting elements in a new container, re-read every rule that positioned
   them. */
.actwrap:last-child > .actrow{border-bottom:none}

.actdel{display:flex;align-items:center;justify-content:center;border:0;background:none;padding:0;margin:0;color:var(--rust);cursor:pointer;border-bottom:1px solid var(--line-2);transition:color .12s, background .12s}
.actwrap:last-child .actdel{border-bottom:none}
/* Rust at rest, in a soft disc, so it reads as the thing that removes a line.
   It was hairline grey until hovered — Lee, 23 Sept 2026: *"the crosses are
   hard to see, can you make these Red so they stand out."* Solid on hover. */
.actdel svg{width:13px;height:13px;box-sizing:content-box;padding:6px;border-radius:50%;background:var(--rust-lo);transition:background .12s,color .12s,transform .1s}
.actdel:hover svg{background:var(--rust);color:#fff}
.actdel:active svg{transform:scale(.9)}
.actdel:focus-visible{outline:2px solid var(--rust);outline-offset:-2px}

.actmark{position:relative;width:38px;height:38px;flex:none}
.actmark .avatar{width:38px;height:38px}
.actblank{display:block;width:38px;height:38px;border-radius:12px}
.actblank.na{background:#EEF1EF}
.actblank.go{background:var(--amber-lo)}
.actblank.wait{background:var(--indigo-lo)}
.actblank.ok{background:var(--signal-lo)}
.actblank.no{background:var(--rust-lo)}

.actbadge{position:absolute;right:-4px;bottom:-4px;width:19px;height:19px;border-radius:50%;border:2px solid var(--card);display:grid;place-items:center}
.actbadge svg{width:10px;height:10px}
.actbadge.na{background:#E4E9E5;color:#5E7166}
.actbadge.go{background:var(--amber-lo);color:var(--amber)}
.actbadge.wait{background:var(--indigo-lo);color:var(--indigo)}
.actbadge.ok{background:var(--signal-lo);color:var(--signal)}
.actbadge.no{background:var(--rust-lo);color:var(--rust)}

.actbody{flex:1;min-width:0;display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:10px;align-items:baseline}
.actsum{font-family:var(--fd);font-size:14px;font-weight:600;letter-spacing:-.014em;line-height:1.32;color:var(--text)}
/* The time sits beside the headline rather than on a line of its own: three
   lines a row turned eight events into a screenful of scrolling. */
.actago{justify-self:end;white-space:nowrap;font:500 11.5px var(--fb);color:var(--steel)}
.actdet{grid-column:1 / -1;font-size:12.5px;color:var(--muted);margin-top:3px;line-height:1.42;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* On a phone the headline takes the whole width and the time drops to the
   detail line beside it, so a long summary is not squeezed into a 200px
   column beside 80px of "Yesterday, 11:03" — four lines became two, and a row
   is still never more than two. */
@media (max-width:959px){
  .actbody{grid-template-rows:auto auto}
  .actsum{grid-column:1 / -1;grid-row:1}
  .actdet{grid-column:1;grid-row:2}
  .actago{grid-column:2;grid-row:2;align-self:baseline;margin-top:3px}
}

/* Unread gets an edge, not a highlight. Twelve highlighted rows is a wall of
   colour; twelve edges is a column you can count down. */
.actrow.is-new{box-shadow:inset 3px 0 0 var(--signal)}

.seenline{display:flex;align-items:center;gap:12px;margin:20px 0 8px;font:600 12.5px var(--fb);color:var(--steel)}
.seenline::before,.seenline::after{content:"";flex:1;height:1px;background:var(--line)}
.seenline span{flex:none}

/* ==========================================================================
   The snapshot board — Staff jobs screen
   ========================================================================== */

/* One surface with hairline dividers rather than six floating boxes. Six boxes
   with six shadows read as six separate things; one board reads as one thing
   you can take in at a glance, which is what a snapshot is for.

   Called statboard and not board: .board was already the plot grid on the
   allocate screen, and this rule, arriving later in the stylesheet, quietly
   turned that dense grid of plot numbers into two enormous squares a row. */
.statboard{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:var(--line-2);padding:0 !important;overflow:hidden;margin-bottom:4px}

.bcell{display:block;background:var(--card);padding:15px 15px 16px;text-decoration:none;transition:background .14s}
.bcell:not(.hot):hover{background:var(--signal-lo)}
.bcell:active{background:#DDEDE4}

.bi{width:28px;height:28px;border-radius:9px;display:grid;place-items:center;margin-bottom:11px}
.bi svg{width:15px;height:15px}
.bi.na{background:#EEF1EF;color:#6B7E73}
.bi.go{background:var(--amber-lo);color:var(--amber)}
.bi.wait{background:var(--indigo-lo);color:var(--indigo)}
.bi.ok{background:var(--signal-lo);color:var(--signal)}
.bi.no{background:var(--rust-lo);color:var(--rust)}

.bcell b{display:block;font-family:var(--fd);font-size:27px;font-weight:600;letter-spacing:-.045em;line-height:1;color:var(--text)}
.bl{display:block;font-size:12px;color:var(--muted);margin-top:6px;line-height:1.35}

/* A figure that wants something doing takes its own colour. One shared "alert"
   colour across a board of six says only that something is up, not what. */
.bcell.hot.wait{background:var(--indigo-lo)}
.bcell.hot.wait b{color:var(--indigo)}
.bcell.hot.wait .bi{background:#fff}
.bcell.hot.no{background:var(--rust-lo)}
.bcell.hot.no b{color:var(--rust)}
.bcell.hot.no .bi{background:#fff}
.bcell.hot.go{background:var(--amber-lo)}
.bcell.hot.go b{color:var(--amber)}
.bcell.hot.go .bi{background:#fff}
.bcell.hot.ok{background:var(--signal-lo)}
.bcell.hot.ok b{color:var(--signal)}
.bcell.hot.ok .bi{background:#fff}
.bcell.hot:hover{filter:brightness(.975)}

@media (min-width:560px){
  .statboard{grid-template-columns:repeat(3,1fr)}
}

/* The tools are a drawer at the end of the screen, not another section of it,
   so they get a rule above them to say so. */
body.app .sectionlabel.manage{margin-top:30px;padding-top:24px;border-top:1px solid var(--line)}

/* ==========================================================================
   Reporting

   This is the screen somebody opens in front of a main contractor, so it is
   allowed to look like something. The rules it works to:

     - depth comes from light, not from boxes. A gradient that runs the way a
       light would fall, a shadow that sits under a card rather than around it,
       a hairline highlight along a top edge. Nothing glows for the sake of it.
     - the gradients are decoration on top of a flat colour, never instead of
       one. Every fill still carries its solid colour as an attribute, so a
       print with no stylesheet is still readable and still the right colour.
     - a chart's colour never repeats what its length already says. Bars are
       one tone; only the two rings colour-code anything, and both label every
       segment with its value.
   ========================================================================== */

.repscreen{position:relative}

/* ---- The headline ------------------------------------------------------- */

.hero.rep{padding:26px 24px 22px;background:#09301F;box-shadow:var(--lift-2)}
/* The stock hero's corner glow is already in the layers above; here the
   pseudo-element earns its keep as a sheen running across the panel. */
.hero.rep::after{right:auto;top:auto;left:-10%;bottom:-70%;width:120%;height:150%;border-radius:0;background:none}
.hero.rep .k{color:#93D9B2;font-weight:600;font-size:12.5px}
.hero.rep b{font-size:56px;margin:12px 0 7px;font-variant-numeric:tabular-nums;color:#fff}
/* The figure lit from above. Falls back to plain white wherever the clip is
   not supported, which is why the colour above is set first. */
@supports (-webkit-background-clip:text) or (background-clip:text){
  .hero.rep b{background:#FFFFFF;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent}
}
.hero.rep em{color:#A6CDB8;font-size:13.5px}
.hero.rep .herobar{height:10px;margin-top:20px;background:rgba(255,255,255,.09);box-shadow:inset 0 1px 2px rgba(0,0,0,.30)}
.hero.rep .herobar i{background:#4FB57D;box-shadow:none}
.hero.rep .herofoot{color:#A6CDB8;font-size:12.5px;margin-top:12px}

/* ---- The six figures ----------------------------------------------------
   They used to be one card carved into six by hairlines. Six cards reads as
   six readings; one carved card reads as a table with the lines rubbed out. */

.repstats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:12px 0 4px}

.rstat{position:relative;overflow:hidden;background:#FFFFFF;border:1px solid var(--line);border-radius:var(--r-card);padding:15px 15px 16px;box-shadow:var(--lift-1);transition:box-shadow .18s ease, transform .18s ease}
.rstat:hover{box-shadow:var(--lift-2);transform:translateY(-1px)}
/* The accent along the top, fading out to the right so it reads as a mark on
   the card rather than a border around it. */
.rstat::before{content:"";position:absolute;left:0;right:0;top:0;height:3px;background:currentColor}
/* There was a wash of the tile's own colour in the top corner here. It was
   drawn, looked at, and taken out again: a radial gradient that faint does not
   read as light falling on a card, it reads as a smudge on the screen. The
   accent line above does the job it was meant to do. */
.rstat.t-ok{color:var(--signal)}
.rstat.t-go{color:var(--amber)}
.rstat.t-wait{color:var(--indigo)}
.rstat.t-no{color:#C2533A}
.rstat.t-na{color:var(--steel)}

.rsk{display:block;position:relative;font:600 12.5px var(--fb);color:var(--steel)}
.rstat b{display:block;position:relative;font-family:var(--fd);font-size:30px;font-weight:600;letter-spacing:-.05em;line-height:1.05;margin-top:9px;color:var(--text);font-variant-numeric:tabular-nums}
.rss{display:block;position:relative;font-size:11.5px;color:var(--muted);margin-top:5px;line-height:1.4}
/* A bar under a figure that has a denominator. Four of the six do; the other
   two get nothing rather than a bar measuring nothing. */
.rsm{display:block;position:relative;height:5px;border-radius:var(--r-pill);background:var(--line-2);overflow:hidden;margin-top:11px;box-shadow:inset 0 1px 1px rgba(6,24,15,.06)}
.rsm i{display:block;height:100%;border-radius:var(--r-pill);background:currentColor;background-image:none;transition:width .5s cubic-bezier(.2,.75,.3,1)}

@media (min-width:600px){
  .repstats{grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}
  .rstat{padding:17px 17px 18px}
  .rstat b{font-size:32px}
}

/* ---- Section headings ---------------------------------------------------- */

body.app .repscreen .sectionlabel{display:flex;align-items:center;gap:10px;margin:32px 0 12px}
body.app .repscreen .sectionlabel::before{content:"";width:16px;height:3px;border-radius:2px;flex:none;background:var(--signal)}

/* ---- The cards themselves ------------------------------------------------ */

/* `:not(.hsfacts)` because that one is not a surface — it is a grid of cells
   with its own background showing through one-pixel gaps as the hairlines
   between them. Painting a gradient over it rubbed all four lines out. */
.repscreen .card:not(.hsfacts){background:#FFFFFF;box-shadow:0 1px 2px rgba(6,24,15,.045), var(--lift-2);transition:box-shadow .18s ease}
.repscreen .card:hover{box-shadow:0 1px 2px rgba(6,24,15,.05), var(--lift-2)}
.repscreen .hsfacts{box-shadow:0 1px 2px rgba(6,24,15,.045), var(--lift-2)}

/* ---- Getting it out of the app ------------------------------------------ */

.exports{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:16px 0 4px}
.exbtn{display:flex;align-items:center;justify-content:center;gap:10px;border:1px solid var(--line);border-radius:var(--r-ctl);background:#FFFFFF;padding:14px 12px;text-decoration:none;color:var(--forest);font:600 13.5px var(--fb);box-shadow:var(--lift-1);transition:border-color .16s,box-shadow .16s,transform .12s,background .16s}
.exbtn:hover{border-color:var(--signal-mid);background:#F6FCF8;box-shadow:var(--lift-2);transform:translateY(-1px)}
.exbtn:active{transform:translateY(0)}
.exbtn svg{width:17px;height:17px;flex:none;color:var(--signal)}

/* ---- Bars ---------------------------------------------------------------- */

.cbars{display:grid;gap:14px}
.cbar{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:12px;align-items:baseline}
.cbl{font:600 13px var(--fb);color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cbv{justify-self:end;font-family:var(--fd);font-size:14.5px;font-weight:600;letter-spacing:-.02em;color:var(--text);font-variant-numeric:tabular-nums}
.cbt{grid-column:1 / -1;display:block;height:10px;border-radius:var(--r-pill);background:#E7ECE8;box-shadow:inset 0 1px 2px rgba(6,24,15,.075);overflow:hidden;margin-top:8px}
.cbt i{display:block;height:100%;border-radius:var(--r-pill);transition:width .45s cubic-bezier(.2,.75,.3,1)}
.cbn{grid-column:1 / -1;font-size:11.5px;color:var(--muted);margin-top:6px;line-height:1.4}

/* The gradient goes on `background-image`, which leaves the solid `background`
   the helper wrote in the style attribute untouched underneath. Two layers:
   a highlight down the top third so the bar has a surface, then the tone
   running along its length so the far end is the brighter one. */
.cbt i, .rsm i, .cmeter i{background-repeat:no-repeat}
.cbt i.t-signal{background-image:#068943}
.cbt i.t-forest{background-image:#0C4029}
.cbt i.t-deep,.cbt i.t-step4{background-image:#0A5330}
.cbt i.t-amber{background-image:#A8710C}
.cbt i.t-flag,.cbt i.t-rust{background-image:#C2533A}
.cbt i.t-indigo{background-image:#2E5590}
.cbt i.t-steel{background-image:#84968C}

/* The whole row lifts on hover, so the pointer has something to land on and
   the native tooltip has somewhere to come from. */
.repscreen .cbar{border-radius:9px;padding:6px 8px;margin:0 -8px;transition:background .14s ease}
.repscreen .cbar:hover{background:#F6F9F7}

/* ---- Donut --------------------------------------------------------------- */

.cdonutwrap{display:flex;align-items:center;gap:24px;flex-wrap:wrap;position:relative}
/* A soft plate under the ring, so it sits on the card instead of floating. */
.cdonutwrap::before{content:"";position:absolute;left:8px;top:50%;width:184px;height:184px;transform:translateY(-50%);border-radius:50%;pointer-events:none;background:none}
.cdonut{width:184px;height:184px;flex:none;position:relative}
.cdonut .cdn{font-family:var(--fd);font-size:32px;font-weight:600;letter-spacing:-.045em;fill:var(--text);font-variant-numeric:tabular-nums}
.cdonut .cdu{font-size:12px;fill:var(--steel);font-family:var(--fb)}

/* Screen only: each segment takes the gradient the SVG defined for its tone.
   Nothing else knows these exist, so a print falls back to the flat stroke. */
.cdonut circle.t-signal{}
.cdonut circle.t-forest{}
.cdonut circle.t-deep{}
.cdonut circle.t-flag{}
.cdonut circle.t-steel{}
.cdonut circle.t-step1{}
.cdonut circle.t-step2{}
.cdonut circle.t-step3{}
.cdonut circle.t-step4{}
.cdonut circle.t-amber{}
.cdonut circle.t-indigo{}
.cdonut circle.t-rust{}

/* The two palest steps of the scale are under 3:1 against white. The value is
   written beside them in the key, and here they get an edge so the shape is
   still a shape on a bright screen in daylight. */
.ckey i.t-step1,.ckey i.t-step2{box-shadow:inset 0 0 0 1px rgba(6,24,15,.22)}

.ckey{flex:1;min-width:170px;display:grid;gap:4px}
.ckey span{display:flex;align-items:center;gap:10px;font-size:12.5px;color:var(--muted);padding:6px 9px;border-radius:8px;transition:background .14s ease}
.ckey span:hover{background:#F6F9F7}
.ckey i{width:11px;height:11px;border-radius:3.5px;flex:none;background-image:none}
.ckey b{margin-left:auto;font-family:var(--fd);font-size:14px;font-weight:600;letter-spacing:-.02em;color:var(--text);font-variant-numeric:tabular-nums}

/* ---- Columns over time --------------------------------------------------- */

.ccolwrap{width:100%}
.ccols{display:block;width:100%;height:186px}
.ccols rect.t-signal{fill:url(#cgc-signal)}
.ccols rect.t-forest{fill:url(#cgc-forest)}
.ccols rect.t-deep{fill:url(#cgc-deep)}
.ccols rect.t-amber{fill:url(#cgc-amber)}
.ccols rect.t-indigo{fill:url(#cgc-indigo)}
.ccols rect.t-flag{fill:url(#cgc-flag)}
.ccollabels,.ccolvalues{display:flex;width:100%}
.ccollabels span,.ccolvalues span{flex:1;text-align:center;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ccolvalues span{font:600 11px var(--fb);color:var(--text);font-variant-numeric:tabular-nums;padding-bottom:5px}
.ccollabels span{font:500 10.5px var(--fb);color:var(--steel);padding-top:7px}
.ccollabels span.now{color:var(--signal);font-weight:600}

/* ---- Meter --------------------------------------------------------------- */

.cmeter{display:block;height:10px;border-radius:var(--r-pill);background:#E7ECE8;box-shadow:inset 0 1px 2px rgba(6,24,15,.075);overflow:hidden;flex:1;min-width:90px}
.cmeter i{display:block;height:100%;border-radius:var(--r-pill)}
.cmeter i.t-signal{background-image:#068943}
.cmeter i.t-amber{background-image:#A8710C}

.evline{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:22px;padding-top:20px;border-top:1px solid var(--line-2)}
.evt{flex:1 1 100%;min-width:0}
.evt b{display:block;font-family:var(--fd);font-size:14px;font-weight:600;letter-spacing:-.018em}
.evt span{display:block;font-size:12.5px;color:var(--muted);margin-top:3px;line-height:1.4}
.evv{font-family:var(--fd);font-size:15px;font-weight:600;letter-spacing:-.02em;flex:none;font-variant-numeric:tabular-nums}

/* ---- Tables -------------------------------------------------------------- */

/* A table is the right shape for this data and the wrong shape for a 390px
   screen, so it scrolls sideways inside its card rather than squashing. */
.tablewrap{padding:0 !important;overflow-x:auto;-webkit-overflow-scrolling:touch}
.rtable{width:100%;border-collapse:collapse;font-size:13px;min-width:420px}
.rtable th{text-align:left;padding:12px 14px;font:600 12.5px var(--fb);color:var(--steel);border-bottom:1px solid var(--line);white-space:nowrap;background:#FBFCFB}
.rtable td{padding:12px 14px;border-bottom:1px solid var(--line-2);vertical-align:top;color:var(--text);line-height:1.4}
.rtable tbody tr{transition:background .14s ease}
.rtable tbody tr:hover{background:#F7FAF8}
.rtable tr:last-child td{border-bottom:none}
.rtable td.num,.rtable th.num{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}
.rtable td:first-child{font-family:var(--fd);font-weight:600;letter-spacing:-.016em}
.rtable td em{display:block;font-style:normal;font-size:11.5px;color:var(--muted);margin-top:3px}
.rtable .chip{font-size:11px}

/* A row that is information rather than a way through to something. */
.prow.static{display:grid;grid-template-columns:40px minmax(0,1fr) auto;align-items:center;column-gap:13px;padding:12px 14px;border-bottom:1px solid var(--line-2);transition:background .14s ease}
.prow.static:hover{background:#F7FAF8}
.prow.static:last-child{border-bottom:none}
.prow.static .pname{white-space:normal;line-height:1.35}

@media (max-width:420px){
  .hero.rep{padding:22px 19px 19px}
  .hero.rep b{font-size:46px}
  .cdonutwrap{gap:18px}
  .cdonut{width:152px;height:152px;margin:0 auto}
  .cdonutwrap::before{left:50%;top:92px;transform:translate(-50%,-50%);width:160px;height:160px}
  .ckey{min-width:0}
}

@media (min-width:640px){
  .exports{grid-template-columns:repeat(2,minmax(0,220px))}
}

/* Once there is room, a bar goes on the same line as its label and its value
   rather than under both.
   
   Stacked, on a monitor, the label sits at the far left and the reading at the
   far right with nine hundred pixels of bar between them — a reader has to
   cross the card to find out what the number belongs to, and on the "who is
   doing the work" chart, where everybody is on one check and every bar is
   therefore full width, the value ends up touching the end of its own bar.
   Side by side, the reading is next to the thing it reads. */
@media (min-width:700px){
  .repscreen .cbar{grid-template-columns:minmax(120px,1.05fr) minmax(0,2.6fr) auto;align-items:center;column-gap:16px}
  .repscreen .cbar .cbt{grid-column:2;margin-top:0}
  .repscreen .cbar .cbv{grid-column:3;min-width:58px}
  .repscreen .cbar .cbn{grid-column:1 / -1;margin-top:4px}
  .repscreen .cbars{gap:10px}

  /* Four readings across, rather than two over two with the width for four. */
  .repscreen .hsfacts{grid-template-columns:repeat(4,1fr)}
}

/* Printing the screen itself is not the way to get this on paper — there is a
   report view for that — but if somebody does, the gradients and the shadows
   are ink that says nothing. */
@media print{
  .repscreen .card,.rstat,.exbtn{box-shadow:none;background:#fff}
  .cdonut{filter:none}
  .cdonutwrap::before,.rstat::after{display:none}
  .cbt i,.cmeter i,.rsm i{background-image:none}
  .exports{display:none}
}

/* ==========================================================================
   Plots — the list, the plot, the check
   ========================================================================== */

/* A way back that is not the browser's, because the crew reach a plot from a
   development and a check from a plot, and a phone's back gesture is not
   something you can use one-handed up a scaffold. */
.backlink{display:inline-flex;align-items:center;gap:6px;font:600 13px var(--fb);color:var(--muted);text-decoration:none;margin:-4px 0 14px;padding:4px 0}
.backlink:hover{color:var(--forest)}

/* ---- The plot list ------------------------------------------------------- */

.card.plot.tap{display:flex;align-items:center;gap:13px;padding:13px 14px !important;text-decoration:none;color:inherit;transition:border-color .14s,transform .14s}
.card.plot.tap:hover{border-color:var(--signal-mid);transform:translateY(-1px)}
.card.plot.tap .pbody{flex:1;min-width:0}

/* The plot number is what a person is scanning for, so it is the one thing
   on the row you can read at arm's length. */
.pmark.na{background:#EEF1EF;color:#6B7E73}
.pmark.go{background:var(--amber-lo);color:var(--amber)}
.pmark.wait{background:var(--indigo-lo);color:var(--indigo)}
.pmark.ok{background:var(--signal-lo);color:var(--signal)}
.pmark.no{background:var(--rust-lo);color:var(--rust)}

.pbar{display:block;height:5px;border-radius:var(--r-pill);background:var(--line-2);overflow:hidden;margin-top:9px}
.pbar i{display:block;height:100%;border-radius:var(--r-pill);background:var(--steel)}
.pbar i.go{background:var(--amber)} .pbar i.wait{background:var(--indigo)}
.pbar i.ok{background:var(--signal)} .pbar i.no{background:var(--rust)}

.pend{flex:none;text-align:right;display:flex;flex-direction:column;align-items:flex-end;gap:6px}
.pfrac{font:600 11.5px var(--fb);color:var(--steel);font-variant-numeric:tabular-nums}
.pfrac em{font-style:normal;color:var(--rust)}

/* A count inside a filter chip — how many are behind each tab. */
.filters a i{font-style:normal;font-weight:600;opacity:.55;margin-left:5px;font-variant-numeric:tabular-nums}
.filters a[aria-pressed="true"] i{opacity:.75}

.searchform{margin:0}
.search .linky{flex:none;font:600 12.5px var(--fb)}

/* ---- The plot's own headline --------------------------------------------- */

.plothero{background:var(--forest);color:#fff;border-radius:var(--r-surface);padding:20px;margin-bottom:14px;position:relative;overflow:hidden}
.plothero::after{content:"";position:absolute;right:-50px;top:-60px;width:200px;height:200px;border-radius:50%;background:rgba(255,255,255,.05)}
.plothero.no{background:#4A1E13}
.plothero.wait{background:#1B3255}
.plothero.ok{background:#0A5C31}

.phtop{display:flex;align-items:center;gap:18px;position:relative}
.phtxt{flex:1;min-width:0}
.phtxt b{display:block;font-family:var(--fd);font-size:20px;font-weight:600;letter-spacing:-.032em;margin-top:9px}
.phtxt > span:last-child{display:block;font-size:12.5px;color:#9EC6B0;margin-top:4px;line-height:1.45}
.plothero.no .phtxt > span:last-child{color:#D9AE9F}
.plothero.wait .phtxt > span:last-child{color:#A8BDDE}

/* The ring is the progress: one glance, no reading. Drawn with a conic
   gradient rather than an SVG so it costs nothing and scales with the text. */
.phring{position:relative;width:74px;height:74px;flex:none;border-radius:50%;background:conic-gradient(var(--signal-mid) calc(var(--p) * 1%), rgba(255,255,255,.16) 0);display:grid;place-items:center}
.phring::before{content:"";position:absolute;inset:7px;border-radius:50%;background:inherit;background:var(--forest)}
.plothero.no .phring::before{background:#4A1E13}
.plothero.wait .phring::before{background:#1B3255}
.plothero.ok .phring::before{background:#0A5C31}
.phring b{position:relative;font-family:var(--fd);font-size:20px;font-weight:600;letter-spacing:-.04em;line-height:1}
.phring b i{font-style:normal;font-size:11px;font-weight:500;opacity:.7;margin-left:1px}

.chip.big{font-size:11.5px;padding:5px 10px;background:rgba(255,255,255,.14);color:#fff}
.chip.big::before{background:currentColor;opacity:.8}

.phfacts{display:flex;gap:1px;margin:18px -20px -20px;background:rgba(255,255,255,.09);position:relative}
.phfacts span{flex:1;background:inherit;padding:12px 10px 14px;font-size:11px;color:#9EC6B0;line-height:1.35;text-align:center;background:rgba(0,0,0,.14)}
.phfacts b{display:block;font-family:var(--fd);font-size:16px;font-weight:600;letter-spacing:-.03em;color:#fff;margin-bottom:3px}

.notice.good{background:var(--signal-lo);border-color:#CDE7D9;color:#0A5C31}
.notice.good span{color:#2E7A52}
.notice.bad{background:var(--rust-lo);border-color:#EBD3CB;color:#8A331E}
.notice.bad span{color:#96503A}

/* ---- Stages -------------------------------------------------------------- */

.stage{margin-top:22px}
.sthead{display:flex;align-items:center;gap:9px;margin-bottom:8px}
.sthead b{font-family:var(--fd);font-size:15px;font-weight:600;letter-spacing:-.022em;flex:1;min-width:0}
.sfrac{font:600 12px var(--fb);color:var(--steel);font-variant-numeric:tabular-nums;flex:none}
.sdot{width:9px;height:9px;border-radius:50%;flex:none;background:var(--line)}
.sdot.go{background:var(--amber)} .sdot.wait{background:var(--indigo)}
.sdot.ok{background:var(--signal)} .sdot.no{background:var(--rust)}

.sbar{height:4px;border-radius:var(--r-pill);background:var(--line-2);overflow:hidden;margin-bottom:10px}
.sbar i{display:block;height:100%;border-radius:var(--r-pill);background:var(--steel)}
.sbar i.go{background:var(--amber)} .sbar i.wait{background:var(--indigo)}
.sbar i.ok{background:var(--signal)} .sbar i.no{background:var(--rust)}

/* ---- The checks themselves ----------------------------------------------- */

body.app .card.checks{padding:0 !important;overflow:hidden}

.crow{display:flex;align-items:flex-start;gap:12px;padding:13px 14px;text-decoration:none;color:inherit;border-bottom:1px solid var(--line-2);transition:background .14s}
.crow:last-child{border-bottom:none}
.crow:hover{background:var(--paper)}
.crow:active{background:var(--line-2)}

.cmark{width:30px;height:30px;flex:none;border-radius:9px;display:grid;place-items:center;margin-top:1px}
.cmark svg{width:18px;height:18px}
.cmark.na{background:#EEF1EF;color:#6B7E73}
.cmark.ok{background:var(--signal-lo);color:var(--signal)}
.cmark.no{background:var(--rust-lo);color:var(--rust)}
.cmark.todo{background:transparent;color:#C6D2CA}

.cbody{flex:1;min-width:0}
.cd{display:block;font-size:14px;line-height:1.45;color:var(--text);font-weight:500}
.cmeta{display:flex;flex-wrap:wrap;align-items:center;gap:7px;margin-top:8px}
.mcount{display:inline-flex;align-items:center;gap:5px;font:600 11.5px var(--fb);color:var(--steel)}
.mcount svg{width:14px;height:14px}
.cnote{display:block;font-size:12.5px;color:var(--muted);line-height:1.45;margin-top:7px;padding-left:10px;border-left:2px solid var(--line)}
.crow .pchev{margin-top:7px}

/* ---- One check ----------------------------------------------------------- */

.checkhead{margin-bottom:22px}
.stagetag{display:inline-block;font:600 12.5px var(--fb);color:var(--steel);margin-bottom:9px}
.checkhead h1{font-family:var(--fd);font-size:22px;font-weight:600;letter-spacing:-.03em;line-height:1.32;margin:0 0 13px}
.checkhead .chips{display:flex;flex-wrap:wrap;gap:7px}

.shots{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;margin-bottom:12px}
.shot{margin:0;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);overflow:hidden}
.shot img,.shot video{display:block;width:100%;height:130px;object-fit:cover;background:var(--line-2)}
/* `.evshot`, not `.shot` — see the note in appshell9.css. A figure on the
   check screen carries a caption; the one on the safety screen does not. */
.evshot figcaption{display:flex;align-items:flex-start;gap:10px;padding:9px 11px 10px;font-size:12px;color:var(--text);line-height:1.4}
.evshot figcaption > span{flex:1;min-width:0}
.evshot figcaption em{display:block;font-style:normal;font-size:11px;color:var(--steel);margin-top:2px}
.linky.danger{color:var(--rust);flex:none;font-size:12px}
body.app .linky.danger:hover{color:var(--rust)}

.filepick.shotpick{display:flex;flex-direction:column;align-items:center;gap:7px;padding:20px 16px;text-align:center;background:var(--card)}
.filepick.shotpick svg{width:24px;height:24px;color:var(--signal)}
.filepick.shotpick span{font:600 14px var(--fb);color:var(--text)}
.filepick.shotpick em{font-style:normal;font-size:11.5px;color:var(--steel);line-height:1.45;max-width:36ch}

/* ---- The answer ---------------------------------------------------------- */

.answers{display:grid;grid-template-columns:repeat(3,1fr);gap:9px;margin-bottom:14px}
.ans{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;text-align:center;background:var(--card);border:1px solid var(--line);border-radius:var(--r-ctl);padding:15px 8px;cursor:pointer;transition:border-color .14s,background .14s}
.ans input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
.ans b{font-family:var(--fd);font-size:15.5px;font-weight:600;letter-spacing:-.024em;color:var(--text)}
.ans span{font-size:11px;color:var(--steel);line-height:1.3}
.ans:hover{border-color:var(--steel)}

/* The colour at rest: a mark in a soft disc and the word in the same tone. */
.ansmark{width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;margin-bottom:3px;transition:background .14s,color .14s}
.ansmark svg{width:16px;height:16px}
.ans.ok .ansmark{background:var(--signal-lo);color:var(--signal)}
.ans.no .ansmark{background:var(--rust-lo);color:var(--rust)}
.ans.na .ansmark{background:#EEF1EF;color:var(--muted)}
.ans.ok b{color:#0A5C31}
.ans.no b{color:#8A331E}
.ans.na b{color:var(--muted)}
.ans.ok{border-color:#CDE7D9}
.ans.no{border-color:#EBD3CB}
.ans.ok:hover{border-color:var(--signal-mid)}
.ans.no:hover{border-color:var(--rust)}
.ans:has(input:checked) .ansmark{background:rgba(255,255,255,.18);color:#fff}
.ans.ok:has(input:checked){background:var(--signal);border-color:var(--signal)}
.ans.no:has(input:checked){background:var(--rust);border-color:var(--rust)}
.ans.na:has(input:checked){background:var(--forest);border-color:var(--forest)}
.ans:has(input:checked) b,.ans:has(input:checked) span{color:#fff}
.ans:has(input:checked) span{opacity:.78}
.ans:has(input:focus-visible){outline:2px solid var(--forest);outline-offset:2px}

body.app .input label .req{color:var(--rust);font-weight:600}

.answeracts{display:grid;gap:9px;margin-top:16px}
body.app .answeracts .btn + .btn{margin-top:0}

.stepper{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:20px;padding-top:16px;border-top:1px solid var(--line)}
.stepper a{font:600 13px var(--fb);color:var(--signal);text-decoration:none}
.stepper a:hover{text-decoration:underline}
.stepper > span{flex:1}

/* ---- Deciding ------------------------------------------------------------ */

.decidecard{margin-top:24px}
.decidecard .quick{margin-top:4px}

/* ---- Comments ------------------------------------------------------------ */

body.app .list.thread{margin-top:10px}
.card.cmt{padding:13px 14px !important}
.chead{display:flex;align-items:center;gap:9px;margin-bottom:8px}
.chead b{font-family:var(--fd);font-size:13.5px;font-weight:600;letter-spacing:-.018em}
.chead span{font-size:11.5px;color:var(--steel);margin-left:auto}
.card.cmt p{margin:0;font-size:13.5px;line-height:1.55;color:var(--text)}

@media (max-width:400px){
  .phring{width:64px;height:64px}
  .phring b{font-size:17px}
  .phtxt b{font-size:18px}
  .answers{grid-template-columns:1fr 1fr}
  .answers .ans:last-child{grid-column:1 / -1;flex-direction:row;gap:8px}
  .answers .ans:last-child .ansmark{margin-bottom:0;width:24px;height:24px}
}

@media (min-width:560px){
  .answeracts{grid-template-columns:1fr 1fr}
}

/* ---- Cards that are now ways through ------------------------------------- */

/* The development card and the plot row were read-outs. Both are the only
   route to the screen behind them, so both are links now — and a link that
   looks exactly like the card it replaced needs to say so on hover. */
body.app a.card.site.tap{display:block;text-decoration:none;color:inherit;transition:border-color .14s,transform .14s,box-shadow .2s}
body.app a.card.site.tap:hover{border-color:var(--signal-mid);border-left-color:var(--acc,var(--forest));transform:translateY(-2px);box-shadow:0 1px 2px rgba(6,24,15,.04), var(--lift-2)}
.site.tap .srow{display:flex;gap:14px;align-items:center}
.site.tap .pfoot{display:flex}
.site.tap .cardrow{display:flex}
.cardrow .go{color:var(--signal);font-weight:600;flex:none;white-space:nowrap}

.card.plot.tap.row{align-items:center}
.card.plot.tap.row .pchev{flex:none}

/* ==========================================================================
   Running the site — schedules, imports, checklists
   ========================================================================== */

/* ---- The two ways in and out of a schedule -------------------------------- */

.tiles.two{grid-template-columns:1fr 1fr;margin-bottom:14px}
.tile.flat{padding:13px 14px}
.tile.flat .tn{font-size:14px}
.tile.flat .tm{margin-top:3px}

/* ---- A grid you can type into --------------------------------------------- */

/* Not a spreadsheet, but the same shape as one, because that is what a plot
   schedule is and everybody already knows how to read it. */
.rtable.grid{min-width:620px}
.rtable.grid td{padding:5px 7px;vertical-align:middle}
.rtable.grid th{padding:10px 7px}
.rtable.grid td:first-child{font-family:var(--fb);font-weight:400;letter-spacing:0}
.rtable.grid tr:hover td{background:var(--paper)}

.rtable.grid th.tick,.rtable.grid td.tick{width:76px;text-align:center;white-space:nowrap}
.rtable.grid td.tick input[type=checkbox]{width:19px;height:19px;accent-color:var(--signal);margin:0}
.rtable.grid td.tick .hand{font-size:10.5px}

.cellinput{width:100%;min-width:0;border:1px solid transparent;background:transparent;border-radius:8px;padding:7px 8px;font:500 13px var(--fb);color:var(--text);outline:none}
.cellinput:hover{border-color:var(--line)}
.cellinput:focus{border-color:var(--signal-mid);background:var(--card);box-shadow:0 0 0 3px var(--signal-lo)}
.cellinput.num{font-variant-numeric:tabular-nums;font-weight:600}
.cellinput.wide{resize:vertical;line-height:1.45;min-height:44px}
select.cellinput{appearance:none;font-size:12.5px;background-image:linear-gradient(45deg,transparent 49%,var(--steel) 51%),
                   linear-gradient(135deg,var(--steel) 49%,transparent 51%);background-size:5px 5px,5px 5px;background-position:calc(100% - 13px) 50%,calc(100% - 8px) 50%;background-repeat:no-repeat;padding-right:26px}

/* A row on its way out says so before the save, not after it. */
.rtable.grid tr.going td{background:var(--rust-lo);opacity:.62}
.rtable.grid tr.going .cellinput{text-decoration:line-through;color:var(--rust)}
.rtable.grid tr.dup td{background:#FCFAF4}
.rtable.grid tr.blank td{background:var(--line-2)}
.rtable.grid tr.blank:hover td{background:var(--line-2)}
.rtable.grid.items td:nth-child(2){min-width:260px}

/* ---- Choices that need a sentence, not a word ---------------------------- */

.opt{display:flex;gap:11px;align-items:flex-start;padding:12px 0;border-top:1px solid var(--line-2);cursor:pointer}
.opt:first-of-type{border-top:none}
.opt input{width:19px;height:19px;flex:none;margin:1px 0 0;accent-color:var(--signal)}
.opt span{flex:1;font-size:12.5px;color:var(--muted);line-height:1.5}
.opt b{display:block;font-size:13.5px;font-weight:600;color:var(--text);letter-spacing:-.012em;margin-bottom:2px}

/* ---- The two ways of getting a schedule in ------------------------------- */

.routes{display:grid;gap:10px;margin:12px 0 4px}
.route{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:14px 15px}
.route b{display:block;font-family:var(--fd);font-size:14px;font-weight:600;letter-spacing:-.02em;margin-bottom:5px}
.route span{display:block;font-size:12.5px;color:var(--muted);line-height:1.55}
.route em{font-style:normal;font-weight:600;color:var(--text)}
.route .linky{display:inline-block;margin-top:9px;font-size:12.5px}

/* The page of the drawing the numbers were read off, so a person correcting
   the list is looking at the thing rather than trusting the parser. */
.drawing{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:8px;overflow:auto;max-height:460px;-webkit-overflow-scrolling:touch}
.drawing img{display:block;width:100%;min-width:520px;border-radius:8px}

@media (min-width:640px){
  .routes{grid-template-columns:1fr 1fr}
}

@media (max-width:420px){
  .tiles.two{grid-template-columns:1fr}
}

/* A route this host cannot take. Shown rather than hidden, because "why can I
   not upload the drawing" is a question somebody will ask either way. */
.route.off{background:var(--paper);border-style:dashed}
.route.off b{color:var(--muted)}

/* ---- The same grid on a phone -------------------------------------------- */

/* A four-column editable table at 390px is a table you scroll sideways to use,
   and nobody edits 267 plots that way. Below the width where the columns stop
   fitting, each row becomes a small card with its headings on the left — the
   same form, the same field names, no horizontal scrolling. */
@media (max-width:700px){
  .rtable.grid{min-width:0;display:block}
  .rtable.grid thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
  .rtable.grid tbody,.rtable.grid tr{display:block}
  .rtable.grid tr{border-bottom:1px solid var(--line);padding:6px 4px 10px}
  .rtable.grid tr:last-child{border-bottom:none}
  .rtable.grid td{display:flex;align-items:center;gap:12px;border:none;padding:3px 8px;background:transparent !important}
  .rtable.grid td::before{content:attr(data-label);flex:none;width:92px;font:600 12.5px var(--fb);color:var(--steel)}
  .rtable.grid td:empty{display:none}
  .rtable.grid th.tick,.rtable.grid td.tick{width:auto;text-align:left}
  .rtable.grid td.tick input[type=checkbox]{margin:2px 0}
  .rtable.grid .cellinput{border-color:var(--line);background:var(--card)}
  .rtable.grid.items td:nth-child(2){min-width:0}
  .rtable.grid tr.going td::before{color:var(--rust)}
  .rtable.grid tr.blank{background:var(--line-2);border-radius:var(--r-ctl)}
}

/* ---- The save bar -------------------------------------------------------- */

/* It sits over the list it is saving, so it needs to look like it is floating
   above the page rather than cutting it in half. The fade is doing the work a
   hard edge was doing badly. */
.stickysave{position:sticky;z-index:10;bottom:calc(70px + env(safe-area-inset-bottom));margin:22px calc(var(--gutter, 18px) * -1) 0;padding:18px var(--gutter, 18px) calc(16px + env(safe-area-inset-bottom));background:var(--paper);border:none}
.stickysave .btn{box-shadow:var(--lift-2)}

/* Two stacked rows per plot is a lot of screen for 267 of them, so the phone
   layout is tighter than the reading rhythm elsewhere in the app. */
@media (max-width:700px){
  .rtable.grid td{padding:2px 8px}
  .rtable.grid .cellinput{padding:6px 8px}
  .rtable.grid .cellinput.wide{min-height:38px}
}

/* Column widths as classes rather than inline, so a wider screen can widen
   them — an inline style cannot be overridden by a media query. */
.rtable.grid.items .w-stage{width:186px}
.rtable.grid.items .w-ev{width:194px}

/* ==========================================================================
   The desktop
   ==========================================================================

   Everything above this line is a phone screen that happens to widen a bit.
   Below 1080px it stays exactly that. From 1080px up the app takes a
   different shape: a dark navigation rail down the left, a light bar across
   the top of the content, and room to put two things beside each other.

   The rail and the tab bar are both in the markup on every page. Which one
   you get is a question of the width of the window, not of the device, and
   neither knows the other exists.
   ========================================================================== */

.sidenav{display:none}

@media (min-width:960px){

  body.app{--navw:262px;display:grid;grid-template-columns:var(--navw) minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);grid-template-areas:"nav bar" "nav main";min-height:100dvh}
  body.app.navmin{--navw:76px}

  /* ---- the rail --------------------------------------------------------- */

  .sidenav{grid-area:nav;display:flex;flex-direction:column;position:sticky;top:0;align-self:start;height:100dvh;background:var(--forest);color:#fff;border-right:1px solid rgba(255,255,255,.07);overflow:hidden}

  .snbrand{display:flex;align-items:center;gap:10px;padding:17px 16px 15px;flex:none;border-bottom:1px solid rgba(255,255,255,.08)}
  .snbrand .mark{width:33px;height:33px;flex:none}
  .snwm{font-family:Quicksand,var(--fb);font-weight:600;font-size:16.5px;color:#fff;white-space:nowrap;letter-spacing:.004em;flex:1;min-width:0}
  .snwm i{font-style:normal;font-weight:500;color:#8FBFA6}

  .sncollapse{flex:none;width:26px;height:26px;border-radius:8px;border:none;cursor:pointer;background:transparent;color:#8FBFA6;display:grid;place-items:center;transition:background .14s,color .14s}
  .sncollapse:hover{background:rgba(255,255,255,.1);color:#fff}
  .sncollapse svg{width:15px;height:15px;transition:transform .2s ease}

  .snnav{flex:1;min-height:0;overflow-y:auto;padding:14px 12px 18px;display:flex;flex-direction:column;gap:2px}
  .snnav::-webkit-scrollbar{width:6px}
  .snnav::-webkit-scrollbar-thumb{background:rgba(255,255,255,.12);border-radius:3px}

  .sngroup{display:block;font:600 12.5px var(--fb);color:#5E8B74;padding:16px 10px 7px;white-space:nowrap}
  .snnav > .sngroup:first-child{padding-top:2px}

  .snlink{display:flex;align-items:center;gap:11px;padding:9px 10px;border-radius:10px;text-decoration:none;color:#B7D4C4;font:600 13.5px var(--fb);letter-spacing:-.012em;white-space:nowrap;transition:background .14s,color .14s}
  .snlink svg{width:18px;height:18px;flex:none;transition:color .14s}
  .snlink span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}
  .snlink:hover{background:rgba(255,255,255,.08);color:#fff}

  /* The current section is a filled row rather than a tinted word: at a glance
     across nine of them, the shape is what you find, not the colour. */
  .snlink[aria-current="page"]{background:rgba(255,255,255,.12);color:#fff}
  .snlink[aria-current="page"] svg{color:var(--signal-mid)}

  .snfoot{flex:none;padding:12px;border-top:1px solid rgba(255,255,255,.08)}
  .snyou{display:flex;align-items:center;gap:11px;padding:8px 9px;border-radius:11px;text-decoration:none;color:#fff;transition:background .14s}
  .snyou:hover{background:rgba(255,255,255,.08)}
  .snyou .avatar{--av:32px}
  .snme{flex:1;min-width:0}
  .snme b{display:block;font:600 13px var(--fb);letter-spacing:-.014em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .snme em{display:block;font-style:normal;font-size:11px;color:#8FBFA6;margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .snyou > svg{width:15px;height:15px;flex:none;color:#5E8B74}

  /* ---- narrowed --------------------------------------------------------- */

  body.navmin .snwm,
  body.navmin .sngroup,
  body.navmin .snlink span,
  body.navmin .snme,
  body.navmin .snyou > svg{display:none}
  body.navmin .snbrand{justify-content:center;padding:17px 8px 15px;gap:0}
  body.navmin .sncollapse{position:absolute;right:-999px}
  body.navmin .snbrand .mark{cursor:pointer}
  body.navmin .snnav{padding:14px 10px;gap:4px}
  body.navmin .sngroup + .snlink{margin-top:10px}
  body.navmin .snlink{justify-content:center;padding:11px 0}
  body.navmin .snyou{justify-content:center;padding:8px 0}
  body.navmin .sncollapse svg{transform:rotate(180deg)}

  /* The rail is narrowed with a button that is itself hidden when narrowed,
     so the mark doubles as the way back out. */
  body.navmin .snbrand::after{content:"";position:absolute;inset:0 0 auto 0;height:60px}

  /* ---- the bar ---------------------------------------------------------- */

  /* Dark rail, light bar. Two dark bands meeting at the corner reads as one
     large piece of chrome and swallows the page; this way the content starts
     at the top of the screen, which is what makes it look like a system
     rather than a phone that has been stretched. */
  .appbar{grid-area:bar;background:var(--card);color:var(--text);border-bottom:1px solid var(--line);padding:0 26px;min-height:64px;gap:16px}
  .appbar .barmark{display:none}
  .appbar h2{font-size:19px;letter-spacing:-.028em}
  .appbar .sub{color:var(--muted);font-size:13px;margin-top:1px}

  .appbar .iconbtn,
  .appbar .bell > summary{border-color:var(--line);background:var(--paper);color:var(--muted)}
  .appbar .iconbtn:hover,
  .appbar .bell > summary:hover{background:var(--signal-lo);color:var(--signal);border-color:var(--signal-mid)}
  .appbar .barme{display:none}

  /* ---- the page --------------------------------------------------------- */

  .tabbar{display:none}
  body.app .scroll{grid-area:main;min-width:0}
  body.app .pad{--gutter:26px;max-width:1200px;margin:0 auto;padding:26px var(--gutter) 64px}

  .tiles{grid-template-columns:repeat(3,1fr)}
  .tiles.two{grid-template-columns:repeat(2,minmax(0,1fr));max-width:620px}

  .shots{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}
  .shot img,.shot video{height:150px}

  .exports{grid-template-columns:repeat(2,minmax(0,240px))}

  /* A form is a column, not a canvas. Stretching four fields across 1200px
     makes a short form look like a spreadsheet. */
  .colform{max-width:680px}

  /* The sheet stops being a sheet across the screen and becomes a drawer
     down the right-hand edge — the whole height, sliding in from off-screen
     and back out, the page dimmed beneath it. It hung under the bell as a
     420px card before; Lee, 23 Sept 2026: *"it just floats in the middle of
     the page, can it slide in from the right instead with a premium feel."*
     Asymmetric timing, like the menu sheet: 380ms in on a settling curve,
     260ms out on a leaving one. The rows arrive after the panel, a beat
     apart, so the feed reads as being laid out rather than switched on. */
  .sheet:not(.frombottom) .sheetscrim{top:0}
  .sheet:not(.frombottom) .sheetpanel{top:0;bottom:0;left:auto;right:0;width:min(460px,100vw);height:100dvh;max-height:none;border-radius:0;box-shadow:0 12px 32px -12px rgba(6,24,15,.28), 0 2px 6px rgba(6,24,15,.06);transform:translateX(100%);opacity:1;transition:transform .26s cubic-bezier(.4,0,.8,.4)}
  .sheet:not(.frombottom).is-open .sheetpanel{transform:none;transition:transform .38s cubic-bezier(.16,.84,.24,1)}
  .sheet:not(.frombottom) .sheethead{padding:20px 20px 16px;border-bottom:1px solid var(--line-2)}
  .sheet:not(.frombottom) .sheethead b{font-size:20px;letter-spacing:-.03em}
  .sheet:not(.frombottom) .sheetbody{padding:8px 12px 24px}
  .sheet:not(.frombottom) .sheetbody > *{opacity:0;transform:translateX(14px)}
  .sheet:not(.frombottom).is-open .sheetbody > *{opacity:1;transform:none;transition:opacity .28s ease,transform .38s cubic-bezier(.16,.84,.24,1)}
  .sheet:not(.frombottom).is-open .sheetbody > :nth-child(1){transition-delay:.08s}
  .sheet:not(.frombottom).is-open .sheetbody > :nth-child(2){transition-delay:.12s}
  .sheet:not(.frombottom).is-open .sheetbody > :nth-child(3){transition-delay:.16s}
  .sheet:not(.frombottom).is-open .sheetbody > :nth-child(4){transition-delay:.2s}
  .sheet:not(.frombottom).is-open .sheetbody > :nth-child(5){transition-delay:.24s}
  .sheet:not(.frombottom).is-open .sheetbody > :nth-child(n+6){transition-delay:.27s}
  .sheetgrab{display:none}
  .sheethead{padding:13px 15px}

  /* The bleed is `--gutter`-driven above, so all this branch has to say is
     that the bar sits on the bottom edge once there is no tab bar under it. */
  .stickysave{bottom:0}
  .stickysave .btn{max-width:320px;margin-left:auto;margin-right:auto}
}

/* Room for more across. The figures were three to a row because a phone has
   room for three, not because six is wrong — but a card list only goes two
   abreast once each card still has room to be read. */
@media (min-width:1180px){
  .tiles{grid-template-columns:repeat(4,1fr)}
  body.app .list.plots,
  body.app .list.sites{grid-template-columns:repeat(2,minmax(0,1fr))}
}

@media (min-width:1360px){
  .statboard{grid-template-columns:repeat(6,1fr)}
}

@media (min-width:1500px){
  body.app .pad{max-width:1320px}
}

/* ==========================================================================
   Two columns where a screen has two jobs
   ==========================================================================

   Below the breakpoint these three wrappers are not there at all — the
   children flow exactly as they did, in the order they are written, which is
   the order a phone wants them. Above it, the same markup splits.
   ========================================================================== */

.plotgrid,.plotmain,.plotside,
.checkgrid,.checkmain,.checkside{display:contents}

@media (min-width:1180px){
  .plotgrid,.checkgrid{display:grid;gap:26px;align-items:start;grid-template-columns:minmax(0,1.6fr) minmax(300px,1fr)}
  .plotmain,.checkmain,.plotside,.checkside{display:block}

  /* The decision travels with you down a long checklist rather than waiting at
     the bottom of it. */
  /* The handover rail sits in the right-hand column above the decision, and
     the checklist runs down the left past both. Explicit rows, because a third
     child in a two-column grid lands wherever auto-placement puts it. */
  /* A fourth child since 7 Oct 2026: the lift plans that cover the plot, moved
     up from under the decision. Rail, lift plans, then the decision, down the
     right; the checklist runs past all three. */
  .plotgrid{grid-template-rows:auto auto 1fr}
  .plotgrid>.horail{grid-column:2;grid-row:1;margin:0}
  .plotgrid>.plotlift{grid-column:2;grid-row:2}
  .plotgrid>.plotmain{grid-column:1;grid-row:1 / span 3}
  .plotgrid>.plotside{grid-column:2;grid-row:3}
  body.app .plotlift > .sectionlabel{margin-top:0}
  .plotlift > :last-child{margin-bottom:0}

  .plotside,.checkside{position:sticky;top:88px}
  .plotside > .sectionlabel.manage:first-child,
  .checkside > .sectionlabel.manage:first-child{margin-top:0;padding-top:0;border-top:none}
  .plotside .decidecard:first-child,
  .checkside > *:first-child{margin-top:0}
  .plotmain .stage:first-child{margin-top:0}

  .checkhead h1{font-size:26px}
}

/* ==========================================================================
   Filling the width, rather than just having it
   ========================================================================== */

@media (min-width:1180px){

  /* A dark panel with a number in the top-left corner and 700px of nothing
     beside it does not read as considered. The bar and the two figures under
     it move up alongside the headline instead. Guarded on :has, because a hero
     without a bar has nothing to put over there. */
  .hero:has(.herobar){display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,34%);column-gap:46px;align-items:end;padding:26px 28px}
  .hero:has(.herobar) .k{grid-column:1;grid-row:1}
  .hero:has(.herobar) b{grid-column:1;grid-row:2;font-size:52px}
  .hero:has(.herobar) em{grid-column:1;grid-row:3;margin-top:4px}
  /* The bar and its two figures hug the boundary between their rows, so they
     read as one group rather than a bar floating in green with its labels
     60px below it — the design review's note on all three heroes. */
  .hero:has(.herobar) .herobar{grid-column:2;grid-row:1 / 4;align-self:center;margin:0 0 30px}
  .hero:has(.herobar) .herofoot{grid-column:2;grid-row:1 / 4;align-self:center;margin:18px 0 0}
  .hero.rep:has(.herobar) b{font-size:62px}

  /* Same on the plot: the three facts come up beside the ring instead of
     sitting in a band underneath it. */
  .plothero{display:grid;grid-template-columns:minmax(0,1fr) minmax(300px,38%);column-gap:34px;align-items:center;padding:24px 26px}
  .plothero .phtop{grid-column:1}
  .plothero .phfacts{grid-column:2;margin:0;border-radius:var(--r-card);overflow:hidden}
  .plothero .phfacts span{padding:14px 12px 15px}
  .plothero .phring{width:84px;height:84px}
  .plothero .phring b{font-size:23px}
  .plothero .phtxt b{font-size:23px}
}

@media (min-width:1180px){
  /* The answer column is narrow on purpose; two buttons abreast in it means
     "Save and next check" wraps and the pair stop matching. */
  .checkgrid{grid-template-columns:minmax(0,1.15fr) minmax(360px,1fr)}
  .checkside .answeracts{grid-template-columns:1fr}
  .checkside .answers{grid-template-columns:repeat(3,1fr)}
  .checkmain .filepick.shotpick{padding:34px 20px}

  /* A card that is only a heading and a text box does not need 700px. */
  .plotside .card.pad-card,
  .checkside .card.pad-card{padding:18px}
}

@media (min-width:1180px){
  .rtable.grid.items .w-stage{width:238px}
  .rtable.grid.items .w-ev{width:214px}
}

/* ==========================================================================
   Lift plans
   ========================================================================== */

/* ---- picking the crane --------------------------------------------------- */

/* The crane decides the arithmetic, so it is asked first and asked big. */
.cranepick{display:grid;gap:10px;margin-bottom:4px}
.crane{position:relative;display:block;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:16px 17px;cursor:pointer;transition:border-color .14s,background .14s}
.crane input{position:absolute;opacity:0;width:100%;height:100%;top:0;left:0;margin:0;cursor:pointer}
.crane:hover{border-color:var(--signal-mid)}
.crane:has(input:checked){border-color:var(--signal);background:var(--signal-lo);box-shadow:0 0 0 1px var(--signal)}
.crane:has(input:focus-visible){outline:2px solid var(--forest);outline-offset:2px}
.cranet{display:block;font-family:var(--fd);font-size:24px;font-weight:600;letter-spacing:-.04em;color:var(--forest);line-height:1}
.cranen{display:block;font:600 13.5px var(--fb);color:var(--text);margin-top:5px}
.cranem{display:block;font-size:12px;color:var(--muted);margin-top:5px;line-height:1.45}
.crane.unset{background:var(--paper);border-style:dashed}
.crane.unset .cranet{color:var(--amber)}
.crane.unset .cranem{color:var(--amber)}

/* ---- the yes / no pairs -------------------------------------------------- */

body.app .card.hazards{padding:0 !important;overflow:hidden}
.hazrow{display:flex;align-items:center;gap:14px;padding:11px 14px;border-bottom:1px solid var(--line-2)}
.hazrow:last-child{border-bottom:none}
.hazq{flex:1;min-width:0;font-size:13px;line-height:1.4;color:var(--text)}

.yn{flex:none;display:flex;gap:6px}
.yb{position:relative;display:grid;place-items:center;min-width:52px;padding:7px 10px;border:1px solid var(--line);border-radius:9px;background:var(--card);cursor:pointer;font:600 12px var(--fb);color:var(--muted);transition:background .14s,border-color .14s,color .14s}
.yb input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
.yb:hover{border-color:var(--steel)}
.yb.yes:has(input:checked){background:var(--signal);border-color:var(--signal);color:#fff}
.yb.no:has(input:checked){background:var(--rust);border-color:var(--rust);color:#fff}
.yb:has(input:focus-visible){outline:2px solid var(--forest);outline-offset:2px}

/* ---- the sum, shown back ------------------------------------------------- */

/* The figures are worked out rather than typed, so the screen has to show the
   working — otherwise it is asking somebody to sign for arithmetic they cannot
   see. */
.sumbox{margin-top:16px;background:var(--signal-lo);border:1px solid #CDE7D9;border-radius:var(--r-card);padding:14px 15px}
.sumbox > b{display:block;font-family:var(--fd);font-size:13.5px;font-weight:600;letter-spacing:-.016em;color:#0A5C31;margin-bottom:9px}
.sumbox dl{display:grid;grid-template-columns:auto 1fr;gap:5px 14px;margin:0}
.sumbox dt{font-size:12px;color:#2E7A52;white-space:nowrap}
.sumbox dd{margin:0;font:600 12.5px var(--fb);color:var(--text);font-variant-numeric:tabular-nums}
.sumbox > span{display:block;font-size:11.5px;color:#2E7A52;margin-top:10px;line-height:1.45}

/* ---- signing ------------------------------------------------------------- */

.signhead{margin-bottom:18px}
.signhead h1{font-family:var(--fd);font-size:26px;font-weight:600;letter-spacing:-.032em;line-height:1.2;margin:0 0 6px}
body.app .signhead p.sub{margin-bottom:0}
.signwhat .meta-list{margin-bottom:14px}

.sigcards{display:grid;grid-template-columns:1fr 1fr;gap:12px}
@media (max-width:420px){.sigcards{grid-template-columns:1fr;gap:0}}

/* A box big enough to sign in with a finger, on a phone, in the cold. */
.sigpad{position:relative;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:10px 10px 8px}
.sigpad canvas{display:block;width:100%;height:190px;border-radius:10px;background:#fff;border:1px dashed var(--line);touch-action:none;cursor:crosshair}
.sigpad.signed canvas{border-style:solid;border-color:var(--signal-mid)}
.sigpad.wants canvas{border-color:var(--rust);background:var(--rust-lo)}
.sighint{position:absolute;left:0;right:0;top:84px;text-align:center;pointer-events:none;font:500 13px var(--fb);color:var(--steel)}
.sigpad.wants .sighint{color:var(--rust);font-weight:600}
.sigacts{display:flex;justify-content:flex-end;padding:8px 2px 0}

/* Not JavaScript-dependent for the words, only for the drawing. */
.sigpad:not(.ready) canvas{display:none}
.sigpad:not(.ready) .sighint{display:none}

body.app p.sub.siglegal{font-size:12.5px;margin:16px 0 18px;color:var(--muted)}

/* ---- who has signed ------------------------------------------------------ */

body.app .card.siglist{padding:0 !important;overflow:hidden}

.sigrow{display:flex;align-items:center;gap:13px;padding:13px 14px;border-bottom:1px solid var(--line-2);text-decoration:none;color:inherit}
.sigrow:last-child{border-bottom:none}
a.sigrow:hover{background:var(--paper)}
.sigbody{flex:1;min-width:0}
.sigrole{display:block;font-family:var(--fd);font-size:14.5px;font-weight:600;letter-spacing:-.02em}
.sigwho{display:block;font-size:12px;color:var(--muted);margin-top:3px;line-height:1.4}
.signame{display:block;font-size:13px;color:var(--text);margin-top:3px;font-weight:500}
.sigmeta{display:block;font-size:11.5px;color:var(--steel);margin-top:3px;line-height:1.4}
/* Where a mark came from: drawn on the plot, or applied from file. Quiet — it
   is provenance, not a warning. */
.sigsrc{display:inline-flex;align-items:center;gap:5px;margin-top:5px;padding:2px 8px;border-radius:var(--r-tag);background:var(--line-2);color:var(--muted);font-size:11px;font-weight:600;letter-spacing:.01em}
.sigsrc::before{content:'';width:5px;height:5px;border-radius:50%;background:var(--steel)}
.sigbody .hand{margin-top:6px}

.sigcta{flex:none;border-radius:var(--r-ctl);background:var(--forest);color:#fff;padding:8px 16px;font:600 12.5px var(--fb)}
a.sigrow:hover .sigcta{background:var(--signal)}

/* The mark itself, small. It is the record, not the decoration. */
.sigmark{flex:none;width:96px;height:38px;display:grid;place-items:center;background:#fff;border:1px solid var(--line-2);border-radius:8px;overflow:hidden}
.sigmark img{max-width:100%;max-height:100%;display:block}

.sigrow.done{background:var(--card)}
.sigrow .linky.danger{flex:none}

@media (max-width:520px){
  .sigrow{flex-wrap:wrap}
  .sigmark{order:3;width:100%;height:44px}
  .sigrow .linky.danger{order:4;margin-left:auto}
}

/* ---- the loads table ----------------------------------------------------- */

.rtable.grid.loads{min-width:560px}
.rtable.grid.loads td:first-child{min-width:280px}
@media (max-width:700px){.rtable.grid.loads{min-width:0}}

/* ---- a badge that carries words ------------------------------------------ */

/* `.pmark` is a 46px square built for a plot number. A crane's label is
   whatever Staff call the machine — "55T - Half Rigged" is seventeen
   characters — so on the cranes screen the badge grows sideways and wraps
   rather than letting the text hang out of its corners. It keeps the square
   shape for a short label, which is nearly all of them. */
.pmark.text{width:70px;padding:0 7px;font-size:13px;line-height:1.15;text-align:center;white-space:normal;overflow-wrap:anywhere}
.pmark.text.long{font-size:11px;letter-spacing:0}

/* ---- deleting a plan ----------------------------------------------------- */

/* Red, but not loud: it sits beside two ordinary tiles and should read as the
   one you have to mean rather than the one you reach for. */
.tile.flat.danger{border-color:#EBD3CB;background:var(--rust-lo)}
.tile.flat.danger .tn{color:var(--rust)}
.tile.flat.danger .tm{color:#8A5646}
.tile.flat.danger:hover:not(:disabled){border-color:var(--rust)}

/* ==========================================================================
   CPCS cards
   ========================================================================== */

/* A notice that is not a caution but a problem. The amber one says "look at
   this"; this one says "do not send that to the client". */
.notice.bad{background:var(--rust-lo);border-color:#EBD3CB;color:#8A331E}
.notice.bad span{color:#95503C}

/* A line under a field explaining how it will be read. Used where the app does
   something with the value that the person cannot see — the expiry date, where
   "Aug 2031" is taken as the end of August. */
body.app .input .hint{display:block;font-size:12px;color:var(--steel);margin-top:6px;line-height:1.45}

/* The two sides of the card, side by side, because that is how they appear on
   the plan and seeing them apart is how you end up with two fronts. */
.cardsides{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:12px}
@media (max-width:520px){.cardsides{grid-template-columns:1fr}}

.cardside{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:13px 13px 11px}
.cardside > b{display:block;font-family:var(--fd);font-size:13.5px;font-weight:600;letter-spacing:-.02em;margin-bottom:9px}

/* A CPCS card is 85.6 x 54mm, so the box it sits in is that shape whether or
   not there is a photo in it yet — the empty state does not jump when filled. */
.cardshot{aspect-ratio:1.585;border-radius:10px;overflow:hidden;background:var(--paper);display:grid;place-items:center;margin-bottom:10px}
.cardshot img{width:100%;height:100%;object-fit:contain;display:block}
.cardshot.none{border:1px dashed var(--line)}
.cardshot.none span{font-size:12.5px;color:var(--steel)}

.cardside .filepick{padding:11px}
.cardside .filepick span{font-size:12.5px}

/* On a lift plan: what will print, small, next to the crane drawing block. */
.cardpair{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px}
.cardpair .cardshot{margin-bottom:0}

/* A phrase inside a paragraph that has to be noticed without becoming a banner
   — an expired card named in the middle of a sentence about what you can do. */
.warntext{color:var(--rust);font-weight:600}

/* ==========================================================================
   Where the team are
   ========================================================================== */

.wheremap{position:relative;height:380px;border-radius:var(--r-card);overflow:hidden;border:1px solid var(--line);background:var(--paper);margin-bottom:18px;z-index:0}
@media (max-width:520px){.wheremap{height:300px}}

/* What is behind the map, and what shows if the tiles never arrive. Not an
   error — a site cabin with one bar is the normal case, and the list below the
   map is the real answer anyway. */
.wheremapoff{position:absolute;inset:0;display:grid;place-items:center;padding:24px;text-align:center;font-size:12.5px;color:var(--steel);line-height:1.5}
.wheremap.ready .wheremapoff{display:none}

/* Leaflet draws its own furniture; this is only to stop it fighting the app. */
.wheremap .leaflet-container{font:inherit;background:var(--paper)}
.wheremap .leaflet-popup-content{font:13px/1.45 var(--fb);margin:11px 13px}
.wheremap .leaflet-popup-content b{font-family:var(--fd);font-size:14px;letter-spacing:-.02em}
.wheremap .leaflet-control-attribution{font-size:10px}

/* A person on the map: initials, not a teardrop. Twenty identical pins tell
   you nothing, and a name is what Staff are actually looking for. */
.whpin{display:grid;place-items:center;width:34px;height:34px;border-radius:50%;background:var(--steel);color:#fff;border:2px solid #fff;box-shadow:0 1px 5px rgba(0,0,0,.3)}
.whpin b{font:700 11.5px var(--fb);letter-spacing:-.02em}
.whpin.fresh{background:var(--signal)}

/* A site on the map: a plaque carrying its worst gust, not a pin.

   Lee, 21 Sept 2026: "it would be good to show the location of each site on the
   map too with a wind speed icon on each." A pin says "something is here"; the
   thing Staff need off this map is the number, so the number is on the face.

   Coloured against that site's OWN limits, which is the whole point of having
   per-site limits — 9 m/s is over the stop figure on one site and inside it on
   another. `.none` is its own state and is deliberately not green: a site whose
   forecast has never landed must never read as "within the limit". */
/* The marker box itself, centred on the position. Leaflet flows a divIcon's
   content from the top-left of a fixed box, so without this the plaque sat
   thirteen pixels west of the site it was marking. `pointer-events` is off on
   the box and back on for the plaque, so a 96px invisible rectangle per site
   does not swallow clicks meant for the map. */
.whmark{display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none}
.whmark .whsite{pointer-events:auto;cursor:pointer}

.whsite{display:inline-flex;align-items:center;gap:4px;padding:3px 8px 3px 6px;border-radius:var(--r-tag);background:var(--steel);color:#fff;border:2px solid #fff;box-shadow:0 1px 5px rgba(0,0,0,.3);white-space:nowrap}
.whsite svg{width:13px;height:13px;flex:none;opacity:.9}
.whsite b{font:700 11.5px/1 var(--fb);letter-spacing:-.02em}

/* Zoomed out, a site is a dot. Ten badges do not fit on a phone at the zoom
   that fits ten sites, and a pile of overlapping badges answers nothing; a dot
   still says where the site is and what colour it is, which is the whole of
   the overview question. `wheremap.js` adds `.figures` from zoom 10. */
.whsite{width:15px;height:15px;padding:0;justify-content:center}
.whsite svg,
.whsite b{display:none}
.wheremap.figures .whsite{width:auto;height:auto;padding:3px 8px 3px 6px}
.wheremap.figures .whsite svg{display:block}
.wheremap.figures .whsite b{display:block}
.whsite.ok{background:var(--signal)}
.whsite.amber{background:var(--amber)}
.whsite.red{background:var(--rust)}
.whsite.none{background:var(--steel)}

/* A stale reading is drawn as a claim with a question over it rather than as a
   fact — same rule as a person's dot carrying its age. */
.whsite.stale{opacity:.62}

/* "to 8.2" — a figure the site is heading for, not one it is at. Drawn a touch
   lighter than a reading so the two do not look like the same kind of claim. */
.whsite.due b{font-weight:600}

/* The name under the plaque. No background: a second filled box per site turns
   ten sites into a wall, and the outline is what keeps it readable over both
   the pale streets and the dark green of a park.

   Hidden until the map is zoomed in far enough for the sites to be separate —
   `wheremap.js` adds `.named` from zoom 11. Ten site names drawn at the zoom
   that fits Teesside and Ripon in one frame is one unreadable smear, and the
   name is a tap away in the popup at every zoom. */
.whsitename{display:none;margin-top:2px;text-align:center;font:700 10.5px/1.2 var(--fb);letter-spacing:-.01em;color:var(--forest);text-shadow:0 0 3px #fff, 0 0 3px #fff, 0 0 3px #fff;white-space:nowrap;pointer-events:none}
.wheremap.named .whsitename{display:block}

/* The key under the map, and the sentence that keeps it honest. */
.maplegend{display:flex;flex-wrap:wrap;align-items:center;gap:6px 14px;margin:-8px 0 18px;font:600 12px var(--fb);color:var(--steel)}
.mapkey{display:inline-flex;align-items:center;gap:6px}
.mapkey i{width:11px;height:11px;border-radius:50%;border:0;box-shadow:none;padding:0;display:block}
.mapkey i.ok{background:var(--signal)}
.mapkey i.amber{background:var(--amber)}
.mapkey i.red{background:var(--rust)}
.mapnote{flex:1 1 100%;font:400 11.5px/1.5 var(--fb);color:var(--muted)}
.mapmissing{margin:-6px 0 18px;font-size:12.5px}

.wheremap .leaflet-popup-content .whlimit{color:var(--muted);font-size:12px}
.wheremap .leaflet-popup-content a{color:var(--signal);font-weight:600}

/* The age, which is the fact that matters. Green only when it is recent enough
   to act on — everything else is deliberately quiet, because a grey "2 hours
   ago" should not read like a live position. */
.whage{flex:none;font:600 12px var(--fb);color:var(--steel);white-space:nowrap;background:var(--paper);border-radius:var(--r-tag);padding:5px 11px}
.whage.fresh{background:var(--signal-lo);color:var(--signal)}
.whage.off{background:var(--line-2);color:var(--muted)}

@media (max-width:420px){
  .whage{font-size:11.5px;padding:4px 9px}
}

/* ==========================================================================
   Where somebody has been — the activity feed

   Lee, 21 Sept 2026: "please add Location history for each person logging in
   (Similar to a google maps location activity feed)."

   Built around the gaps rather than around the entries. A web app gets a
   position only while its page is open, so most of every day is silence, and a
   feed that closed the silence up would read as a record of where somebody was
   all afternoon. The gap gets a row of its own.
   ========================================================================== */

/* The days, as a strip you push along. A dropdown would hide the shape of the
   record — which days are busy, which have one stray fix — and that shape is
   the first thing anybody wants. */
.dayscroll{display:flex;gap:8px;overflow-x:auto;padding:2px 0 12px;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.dayscroll::-webkit-scrollbar{display:none}
.daychip{flex:none;display:flex;flex-direction:column;align-items:center;gap:2px;min-width:74px;padding:9px 12px;border-radius:var(--r-ctl);background:var(--card);border:1px solid var(--line);text-decoration:none;transition:border-color .12s, background .12s}
.daychip b{font:600 12.5px var(--fb);color:var(--text);letter-spacing:-.01em}
.daychip span{font:600 11px var(--fb);color:var(--steel)}
.daychip:hover{border-color:var(--signal)}
.daychip.on{background:var(--forest);border-color:var(--forest)}
.daychip.on b{color:#fff}
.daychip.on span{color:var(--signal-mid)}

.wheremap.histmap{height:320px}
@media (max-width:520px){.wheremap.histmap{height:260px}}

/* A fix on the map: its number, not a teardrop. Numbered oldest first, which
   is how a person reads a day. */
.histpin{display:grid;place-items:center;width:26px;height:26px;border-radius:50%;background:var(--signal);color:#fff;border:2px solid #fff;box-shadow:0 1px 5px rgba(0,0,0,.3)}
.histpin b{font:700 11px var(--fb);letter-spacing:-.02em}

/* The feed itself. A rail down the left with the entries hung off it, so the
   gaps read as distance rather than as missing rows. */
.histfeed{position:relative;margin:4px 0 6px;padding-left:4px}

.histrow{display:grid;grid-template-columns:13px minmax(0,1fr);gap:12px;align-items:start;padding:11px 0}
.histdot{width:13px;height:13px;border-radius:50%;margin-top:4px;background:var(--signal);box-shadow:0 0 0 3px var(--signal-lo)}
/* Not near a site is its own state, and it is not green. */
.histdot.off{background:var(--steel);box-shadow:0 0 0 3px var(--line-2)}

.histbody b{display:block;font-family:var(--fd);font-size:14.5px;font-weight:600;letter-spacing:-.018em;line-height:1.3;color:var(--text)}
.histwhen{display:block;margin-top:2px;font:600 12.5px var(--fb);color:var(--steel)}
.histhow{display:block;margin-top:2px;font:400 11.5px var(--fb);color:var(--muted)}

/* The silence. Dashed, quiet, and impossible to mistake for an entry — it is
   the part of the day the app knows nothing about, and it is most of it. */
.histgap{display:flex;align-items:center;gap:10px;margin:2px 0 2px 6px;padding:8px 0 8px 15px;border-left:2px dashed var(--line)}
.histgap span{font:400 11.5px var(--fb);color:var(--muted);font-style:italic}

/* ---- the notice ---------------------------------------------------------- */

/* A dialog rather than a banner. This is the thing that makes recording
   somebody's position not covert, so it has to be answered rather than
   scrolled past. */
.wherenotice{position:fixed;inset:0;z-index:200;display:grid;place-items:end center;background:rgba(9,22,16,.55);padding:16px;overflow-y:auto}
.wherenotice[hidden]{display:none}
@media (min-width:560px){.wherenotice{place-items:center}}
html.noscroll{overflow:hidden}

.whpanel{width:100%;max-width:460px;background:var(--card);border-radius:20px;padding:22px 20px 18px;box-shadow:var(--lift-2)}
.whpanel .whicon{width:34px;height:34px;color:var(--signal);display:block;margin-bottom:12px}
.whpanel h2{font-family:var(--fd);font-size:21px;font-weight:600;letter-spacing:-.03em;line-height:1.2;margin:0 0 10px}
.whpanel p{font-size:13.5px;line-height:1.55;color:var(--muted);margin:0 0 14px}

.whlist{list-style:none;margin:0 0 18px;padding:0;display:grid;gap:10px}
.whlist li{position:relative;padding-left:20px;font-size:12.5px;line-height:1.5;color:var(--muted)}
.whlist li::before{content:"";position:absolute;left:2px;top:7px;width:6px;height:6px;border-radius:50%;background:var(--signal)}
.whlist b{color:var(--text);font-weight:600}

body.app .whpanel .btn{width:100%;margin-bottom:6px}

/* ---- the switch on your own account -------------------------------------- */

/* Stacked by default, side by side only when there is room.

   It started as a flex row, and on a phone the sentence came out one word per
   line with the button beside it. A `.btn` in this app is `width:100%`, and a
   flex item asking for the whole container will take it: the text has
   `min-width:0` so it can shrink to nothing, and the button's own content is
   its floor, so the text lost. Mobile first is the fix — the row is the
   exception, not the default. */
.wheremine{display:block}
.wheremine .whstate b{display:block;font-family:var(--fd);font-size:14.5px;font-weight:600;letter-spacing:-.02em}
.wheremine .whstate span{display:block;font-size:12.5px;color:var(--muted);margin-top:3px;line-height:1.45}
body.app .wheremine .btn{margin-top:14px}

@media (min-width:560px){
  .wheremine{display:flex;align-items:center;gap:16px}
  .wheremine .whstate{flex:1 1 auto;min-width:0}
  /* Width and margin both said again: a .btn is 100% wide with a top margin,
     and next to a paragraph it should be neither. */
  body.app .wheremine .btn{flex:0 0 auto;width:auto;margin-top:0}
}

/* ==========================================================================
   Client logos
   ========================================================================== */

/* A logo in the development card's thumbnail slot.

   White and padded, and `contain` rather than `cover`: a logo cropped to fill a
   box is a logo with its name cut off. The white is deliberate even though the
   PNG keeps its transparency — a brand's artwork is drawn to sit on white, and
   a green-tinted card behind a dark wordmark is how you make somebody's logo
   look wrong. */
/* Less padding than a photo gets, and the box is taller than it was (see the
   .sthumb override below). A logo is scaled to fit, so padding comes straight
   off the size of the mark — and a portrait logo like Donaldson's JD device is
   limited by the box's height, which was the shortest thing about it. */
.sthumb.logo{background:#fff;display:flex;align-items:center;justify-content:center;padding:4px}
/* FLEX, not grid, and the image sized by max-width/max-height.

   This is what "the Donaldsons logo doesn't fit" actually was. The box was
   `display:grid; place-items:center`, and a percentage height on a grid item
   resolves against the grid ROW — which was auto-sized, so it simply grew to
   whatever the image wanted. The constraint never bit: a portrait logo
   rendered at 123% of the box height and `overflow:hidden` sliced the top and
   bottom off it. A wide logo happened to fit, so two of the three looked fine
   and the bug read as "that one image is wrong".

   A flex container with a definite height is a definite containing block, so
   `max-height` resolves against it. The img box then measures the picture
   itself, which is also what makes it checkable: compare the img rect to the
   box rect and an overflow is a number, not a judgement. */
.sthumb.logo img{width:auto;height:auto;max-width:100%;max-height:100%;display:block}

/* The same thing on the client list and on its own screen. */
.clogo{width:84px;height:56px;flex:none;border-radius:10px;overflow:hidden;border:1px solid var(--line-2);background:#fff;padding:5px;display:flex;align-items:center;justify-content:center}
.clogo img{width:auto;height:auto;max-width:100%;max-height:100%;display:block}
.clogo.none{background:var(--paper);border-style:dashed}
.clogo.none span{font-size:11px;color:var(--steel);text-align:center;line-height:1.3}

.clogo.big{width:100%;height:130px;padding:18px;margin-bottom:16px}
.clogo.big.none span{font-size:13px}

@media (max-width:520px){
  .clogo{width:70px;height:50px;padding:6px}
}

/* The thumbnail is a touch taller than it was, which the card absorbs — the row
   height is set by two lines of title and a subtitle, not by this. It buys a
   portrait logo about a quarter more height, which is the difference between
   the Donaldsons mark looking placed and looking lost. */
body.app .sthumb{height:66px}
@media (max-width:520px){ body.app .sthumb{height:58px} }

/* ==========================================================================
   Wind
   ========================================================================== */

.windcard{border-radius:var(--r-card);border:1px solid var(--line);background:var(--card);padding:15px 16px 13px;margin-bottom:14px}
.windcard.none{background:var(--paper);border-style:dashed}
.windcard.ok{border-color:#CDE7D9;background:var(--signal-lo)}
.windcard.amber{border-color:#F0E2C4;background:var(--amber-lo)}
.windcard.red{border-color:#EBD3CB;background:var(--rust-lo)}

.windhead{display:flex;align-items:baseline;gap:10px;margin-bottom:12px}
.windhead b{flex:1;font-family:var(--fd);font-size:15px;font-weight:600;letter-spacing:-.024em}
.windcard.ok .windhead b{color:#0A5C31}
.windcard.amber .windhead b{color:#7A5510}
.windcard.red .windhead b{color:#8A331E}

.windage{flex:none;font-size:11.5px;color:var(--steel)}
/* An old reading says so. The number is still worth showing — it is the
   pretending it is current that would be wrong. */
.windage.stale{color:var(--rust);font-weight:600}

.windnow{display:flex;align-items:center;gap:14px}
.windbig{flex:none;display:flex;align-items:baseline;gap:3px}
.windbig b{font-family:var(--fd);font-size:34px;font-weight:600;letter-spacing:-.04em;line-height:1}
.windbig i{font-style:normal;font-size:13px;color:var(--muted)}
.windsub{flex:1;min-width:0;font-size:12.5px;line-height:1.5;color:var(--muted)}

/* What it is going to do, which is the half that changes a decision. */
.windpeak{margin-top:13px;padding:10px 12px;border-radius:11px;background:rgba(255,255,255,.72)}
.windpeak b{display:block;font:600 13px var(--fb)}
.windpeak span{display:block;font-size:12.5px;color:var(--muted);margin-top:2px;line-height:1.45}
.windpeak.red b{color:var(--rust)}
.windpeak.amber b{color:var(--amber)}
.windpeak.ok b{color:var(--signal)}

/* The shape of the day. Bars rather than a line: the question is "which hours
   are bad", not "what is the trend". */
.windbars{position:relative;display:flex;align-items:flex-end;gap:2px;height:76px;margin-top:14px;padding-bottom:14px}
.windbar{position:relative;flex:1;min-width:0;height:var(--h);border-radius:2px 2px 0 0;background:var(--signal);opacity:.55}
.windbar.amber{background:var(--amber);opacity:.85}
.windbar.red{background:var(--rust);opacity:1}
.windbar i{position:absolute;bottom:-14px;left:0;font-style:normal;font-size:9.5px;color:var(--steel);white-space:nowrap}

/* The two limits, drawn across the bars, so "over the line" is literal. */
.windline{position:absolute;left:0;right:0;bottom:calc(14px + var(--y));height:0;border-top:1px dashed;pointer-events:none}
.windline.amber{border-color:var(--amber)}
.windline.red{border-color:var(--rust)}

.windkey{display:flex;gap:14px;margin-top:4px;font-size:11px;color:var(--steel)}
.windkey i{display:inline-block;width:14px;height:0;border-top:1px dashed;vertical-align:middle;margin-right:4px}
.windkey i.amber{border-color:var(--amber)}
.windkey i.red{border-color:var(--rust)}

/* ---- the next five days -------------------------------------------------
   A different question from the bars above it, so it looks like a different
   thing: five named columns rather than a continuous trace, and figures
   rounded to whole metres because a day four out does not deserve a decimal
   point. The same two dashed limits run across it, so "over the line" means
   the same thing here as it does on the hourly chart. */
.wind5{margin-top:18px;padding-top:15px;border-top:1px solid var(--line-2)}

.w5head{display:flex;align-items:baseline;gap:8px;margin-bottom:11px}
.w5head b{font:600 12.5px var(--fb);color:var(--text);letter-spacing:-.01em}
.w5head span{font-size:11px;color:var(--steel)}

.w5days{position:relative;display:grid;grid-template-columns:repeat(5,1fr);gap:6px}

.w5day{display:flex;flex-direction:column;align-items:center;gap:5px;min-width:0;border-radius:12px;padding:8px 2px 7px;background:var(--paper);border:1px solid var(--line-2)}
.w5name{font:600 11px var(--fb);color:var(--muted);white-space:nowrap}

/* The column is a fixed-height well the bar grows inside, so five bars share
   one baseline and the dashed limits can be drawn straight across them. */
.w5col{position:relative;width:100%;height:46px;display:flex;align-items:flex-end;justify-content:center}
/* Proportional, not a fixed 16px: the same splinter that looks right in a 60px
   column on a phone is lost in a 250px one on a monitor. */
.w5col i{display:block;width:clamp(15px, 32%, 46px);height:var(--h);min-height:3px;border-radius:3px 3px 0 0;background:var(--signal);opacity:.5}
.w5ms{font:600 14px var(--fd);letter-spacing:-.02em;color:var(--text);line-height:1}
.w5at{font-size:9.5px;color:var(--steel);line-height:1}

.w5day.amber .w5col i{background:var(--amber);opacity:.85}
.w5day.amber .w5ms{color:var(--amber)}
.w5day.red .w5col i{background:var(--rust);opacity:1}
.w5day.red .w5ms{color:var(--rust)}

/* Today is where you are standing, not a warning — a ring, not a colour. */
.w5day.is-today{background:var(--card);border-color:var(--line);box-shadow:inset 0 0 0 1px var(--line)}
.w5day.is-today .w5name{color:var(--text)}

/* The limits, inside each well rather than across the row — a percentage here
   resolves against the well's own 46px and cannot drift when a label or a bit
   of padding changes. */
.w5col::before,
.w5col::after{content:"";position:absolute;left:0;right:0;height:0;border-top:1px dashed;pointer-events:none}
.w5col::before{bottom:calc(var(--ay, 0) * 1%);border-color:var(--amber);opacity:.6}
.w5col::after{bottom:calc(var(--ry, 0) * 1%);border-color:var(--rust);opacity:.6}

.w5note{margin:9px 0 0;font-size:11px;line-height:1.5;color:var(--steel)}

/* More room for the columns once there is room to have it. */
@media (min-width:700px){
  .w5days{gap:10px}
  .w5day{padding:10px 4px 9px}
  .w5col{height:58px}
  .w5ms{font-size:16px}
  .w5name{font-size:12px}
}

.windnote{margin:12px 0 0;font-size:11.5px;line-height:1.5;color:var(--steel)}

/* The chip on a development card. */
.windchip{display:inline-flex;align-items:center;gap:5px;border-radius:var(--r-tag);padding:3px 9px;font:600 11px var(--fb);white-space:nowrap}
.windchip::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor}
.windchip.ok{background:var(--signal-lo);color:var(--signal)}
.windchip.amber{background:var(--amber-lo);color:var(--amber)}
.windchip.red{background:var(--rust-lo);color:var(--rust)}

/* Two fields on one line — a coordinate pair, a pair of limits. Stacks on a
   narrow phone, where two boxes side by side are two boxes too small. */
.tworow{display:grid;grid-template-columns:1fr 1fr;gap:12px}
@media (max-width:420px){.tworow{grid-template-columns:1fr;gap:0}}

/* A wind warning is a notice you can tap — it goes to the site it is about. */
a.notice.windwarn{text-decoration:none;color:#7A5510}
a.notice.windwarn:hover{border-color:var(--amber)}
a.notice.windwarn.bad{background:var(--rust-lo);border-color:#EBD3CB;color:#8A331E}
a.notice.windwarn.bad span{color:#95503C}
a.notice.windwarn.bad:hover{border-color:var(--rust)}

/* ---------------------------------------------------------------------------
   A list folded away behind its own count.

   Frame Team have 92 house types. Ninety of them have no work on them on day
   one, and drawing a 0% bar for each was seventy per cent of the reporting
   screen saying nothing. They are still worth being able to see — a house type
   missing from the schedule is a real thing to notice — so they are a count
   that opens, rather than a wall that cannot be shut.

   <details> rather than a script: it works with the page's JS unloaded, it is
   keyboard operable for nothing, and the browser tells a screen reader what it
   is without being asked.
--------------------------------------------------------------------------- */
.foldlist{margin:16px 0 0;border-top:1px solid var(--line);padding-top:12px}
.foldlist>summary{cursor:pointer;font:600 13px var(--fb);color:var(--muted);list-style:none;display:flex;align-items:center;gap:7px;padding:3px 0}
.foldlist>summary::-webkit-details-marker{display:none}
.foldlist>summary::before{content:"";width:7px;height:7px;flex:none;border-right:1.8px solid currentColor;border-bottom:1.8px solid currentColor;transform:rotate(-45deg);transition:transform .15s ease;margin-left:2px}
.foldlist[open]>summary::before{transform:rotate(45deg)}
.foldlist>summary:hover{color:var(--text)}
.foldlist>ul{list-style:none;margin:10px 0 0;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:2px 18px}
.foldlist>ul li{display:flex;justify-content:space-between;gap:10px;padding:5px 0;border-bottom:1px solid var(--line-2);font:500 13px var(--fb);color:var(--text)}
.foldlist>ul li em{font:500 12px var(--fb);font-style:normal;color:var(--muted);white-space:nowrap}

/* ==========================================================================
   Ticking plots off a list

   Frame Team have houses the crew checked and Staff signed off before this app existed.
   Making Staff answer 22 questions a plot on a phone for work that is finished
   is how you end up with somebody who does not use the app, so they tick them and
   says so once.

   Two rules shaped this:

   - **The tick target is the whole left edge of the row, not the box.** This
     is used standing on a site with gloves on. A 16px checkbox at arm's length
     on a phone is a miss, and a miss here means the wrong house.
   - **A selected row is obvious from the top of the screen**, not from the
     state of a small square at its edge. Somebody about to sign off forty
     houses needs to see which forty without reading forty rows.
   ========================================================================== */

.plotpick{display:flex;align-items:stretch;gap:0;border-radius:var(--r-card);transition:background .12s ease}
.plotpick>.card.plot{flex:1;min-width:0}

/* The label is the hit area: full height of the row, thumb-width, and it
   carries the box rather than sitting next to it. */
.ptick{flex:none;display:flex;align-items:center;justify-content:center;width:46px;cursor:pointer;border-radius:var(--r-card) 0 0 var(--r-card);-webkit-tap-highlight-color:transparent}
.ptick input{position:absolute;opacity:0;width:1px;height:1px;margin:0;pointer-events:none}
.ptick>span{width:22px;height:22px;border-radius:7px;border:2px solid var(--line);background:var(--card);display:block;position:relative;transition:all .12s ease}
.ptick>span::after{content:"";position:absolute;left:6px;top:2px;width:5px;height:10px;border:solid #fff;border-width:0 2.4px 2.4px 0;transform:rotate(42deg) scale(.4);opacity:0;transition:all .12s ease}
.ptick:hover>span{border-color:var(--signal-mid)}
.ptick input:checked+span{background:var(--signal);border-color:var(--signal)}
.ptick input:checked+span::after{opacity:1;transform:rotate(42deg) scale(1)}

/* Keyboard users get the ring the hidden input cannot show for itself. */
.ptick input:focus-visible+span{outline:2px solid var(--signal);outline-offset:2px}

.plotpick.on{background:var(--signal-lo)}
.plotpick.on>.card.plot{border-color:var(--signal-mid)}

/* ---- The bar ------------------------------------------------------------- */

/* Same furniture as the plot schedule's save bar, and for the same reason: it
   floats over the list it is acting on rather than cutting the page in half.
   It is in the markup from the start and hidden, so the form still works with
   the script blocked.

   Lee, 14 Sept: "the text at top is see through and clashes with the screen."
   It was. The bar was copied from `.stickysave`, whose whole content is one
   button — a button brings its own solid ground, so the fade across the top of
   that bar never lands on anything. This bar's first two lines are bare text:
   "6 selected / Select all 267 / Clear" and the note's label sat in the
   transparent band, with a plot card's number, house type and status chip
   reading straight through them.

   **A fade belongs above the panel, not across its first line.** The ground is
   solid now and the feather is a strip of its own, sitting entirely outside
   the bar — the list still dissolves into it rather than meeting a hard seam,
   and nothing is ever drawn on top of the words. */
.pickbar{position:sticky;z-index:11;bottom:calc(70px + env(safe-area-inset-bottom));margin:18px -18px 0;padding:16px 18px calc(16px + env(safe-area-inset-bottom));background:var(--paper)}
.pickbar::before{content:"";position:absolute;left:0;right:0;bottom:100%;height:26px;background:var(--paper);pointer-events:none}
.pickbar .btn{box-shadow:var(--lift-2);width:100%}

.pickcount{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;margin-bottom:12px}
.pickcount b{font:600 15px var(--fd);letter-spacing:-.02em}
.pickcount .linky{font-size:13px;background:none;border:none;padding:0;cursor:pointer}

.picknote{display:block;margin-bottom:12px}
.picknote>span{display:block;font:600 12.5px var(--fb);color:var(--muted);margin-bottom:6px}
.picknote input{width:100%;border:1px solid var(--line);border-radius:var(--r-ctl);padding:11px 13px;font:400 14.5px var(--fb);background:var(--card);color:var(--text)}
.picknote input:focus{outline:none;border-color:var(--signal);box-shadow:0 0 0 3px var(--signal-lo)}

.picksub{margin:10px 0 0;font-size:12.5px;line-height:1.5;color:var(--muted)}

/* The tab bar is gone from 960px up, so the clearance it was being held off the
   bottom by is clearance from nothing — and a full-bleed strip with a band of
   scrolling plot list showing underneath it does not read as a strip. It sits
   on the bottom edge here, the same as the plot schedule's save bar does. */
@media (min-width:960px){
  .pickbar{bottom:0}
}

/* Two abreast on a desktop, and the bar should not stretch across both.

   Here it stops being a strip across the foot of the screen and becomes a panel
   floating over one column, so it is given the edges of one: a rounded corner
   that is actually visible now the ground is solid, a hairline, and a lift. The
   feather comes off — a soft top edge on a card with three hard ones reads as a
   rendering fault rather than as depth. */
@media (min-width:1180px){
  .pickbar{max-width:560px;margin-left:0;margin-right:0;bottom:20px;padding:18px 20px;border-radius:var(--r-surface);border:1px solid var(--line);box-shadow:var(--lift-2)}
  .pickbar::before{content:none}
}

/* ---- A sign-off taken on trust ------------------------------------------- */

/* Indigo rather than green, and deliberately not the colour of a sign-off done
   in the app. The plot IS approved and the notice should not read as a
   problem — but it is a different kind of claim from "22 checks were answered
   on this plot", and two different claims wearing the same green is how the
   difference stops being noticed by about the third plot. */
.notice.prior{background:var(--indigo-lo);border-color:#D2D6EE;color:#3A3F7A}
.notice.prior span{color:#4A4F86}
.notice.prior b{color:#2E3370}

/* A notice that folds. `<details>` wearing the notice's own clothes: the
   summary is the two-line head with the chevron, the body is the paragraph
   that used to be on the screen all the time. */
.notice.notefold{display:block;padding:0}
.notice.notefold>summary{list-style:none;cursor:pointer;display:flex;gap:12px;align-items:flex-start;padding:14px 15px}
.notice.notefold>summary::-webkit-details-marker{display:none}
.notice.notefold>summary>div{flex:1;min-width:0}
.notice.notefold>summary .windfoldmark{margin-top:7px;color:currentColor;opacity:.7}
.notice.notefold[open]>summary .windfoldmark{transform:rotate(45deg)}
.notice.notefold>p{margin:0;padding:0 15px 14px 46px;font-size:12.5px;line-height:1.5;color:#4A4F86}

/* On a single check, the same thing in six words. Somebody scrolling straight
   to one check should not have to have read the header to know where its
   answer came from. */
.hand.prior{background:var(--indigo-lo);color:#3A3F7A}

/* ---- The card you delete from -------------------------------------------- */

/* The last card on a person's page, and the only place in the app that removes
   something outright. It is set apart so it reads as a different kind of
   action from the ones above it — and so that when the account has history and
   the button is replaced by the reason why, the card still looks like the
   place the button would have been rather than like a stray paragraph. */
.danger-card{border-color:#EBD3CB}
.danger-card .sub b{color:var(--rust)}

/* ---- Lifting tickets on a person ----------------------------------------- */

/* Who can be put down as Appointed Person, Lift Supervisor or Slinger /
   Signaller on a lift plan. It reuses .opt, which is the same control the lift
   plan form uses for its hazard ticks — the same kind of question should not
   look like a different kind of control two screens apart. */
.tickets{margin:0 0 14px}

/* ==========================================================================
   Teams

   The screen that decides what every crew member can see.

   The first version was a heading and a two-column table of counts, and it read
   like a database row — which is exactly what Lee said about it. What fixed it
   was not more padding: it was giving each team a face. A stack of the people
   in it sits where a photograph sits on everything else in this app, because a
   team IS people, and a card about one with nobody on it is a label.
   ========================================================================== */

.list.teams{display:grid;gap:14px}

.card.team{padding:0;overflow:hidden;box-shadow:var(--lift-1);border-radius:var(--r-surface);transition:box-shadow .15s ease,border-color .15s ease}
.card.team:hover{box-shadow:var(--lift-2)}
.card.team.off{opacity:.66}
.card.team.off:hover{box-shadow:var(--lift-1)}

/* ---- the top: faces, name, edit ------------------------------------------ */

.teamtop{display:flex;align-items:center;gap:16px;padding:20px 22px 16px}

/* Overlapping, like a shared document. Negative margin rather than absolute
   positioning so the pile is exactly as wide as the faces in it and the name
   beside it never has to be guessed at. */
.facepile{display:flex;align-items:center;flex:none;min-width:116px}
.facepile .avatar{box-shadow:0 0 0 2.5px var(--card);border-radius:50%}
.facepile .avatar + .avatar{margin-left:-12px}

/* The initials have to sit in the part of the circle you can still see.
   Every face after the first has its left twelfth of an inch hidden under the
   one in front, and a monogram centred in the whole circle puts half of its
   first letter under there — which is how "CH" came out as a C with a bite
   taken out of it. Padding the hidden strip re-centres the letters in the
   visible crescent. A photo needs none of this: a face with its left edge
   covered still reads as that face. */
.facepile .avatar + .avatar:not(.has-photo){padding-left:12px}

.facepile .avatar:nth-child(1){z-index:5}
.facepile .avatar:nth-child(2){z-index:4}
.facepile .avatar:nth-child(3){z-index:3}
.facepile .avatar:nth-child(4){z-index:2}
.facepile .avatar:nth-child(5){z-index:1}
.facepile.empty .avatar{background:var(--line-2);color:var(--muted);border-radius:50%}
.facepile.empty .avatar svg{width:20px;height:20px}

.teamname{flex:1;min-width:0}
.teamname b{display:block;font:600 17px var(--fd);letter-spacing:-.025em;color:var(--text);line-height:1.25}
.teamname .chip{margin-left:8px;vertical-align:3px}
.teammeta{display:block;margin-top:3px;font:500 12.5px var(--fb);color:var(--muted)}

/* ---- the sites ----------------------------------------------------------- */

/* Chips, and every one a link. Going from a team to the job they are on is the
   obvious next move from this screen; a comma-separated list of names is a
   dead end. */
.teamsites{display:flex;flex-wrap:wrap;gap:7px;padding:0 22px 4px}

.sitechip{display:inline-flex;align-items:center;gap:7px;padding:7px 13px 7px 11px;border-radius:var(--r-tag);background:var(--signal-lo);color:#0A5C31;font:600 12.5px var(--fb);letter-spacing:-.01em;text-decoration:none;transition:background .12s ease}
.sitechip::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.55;flex:none}
.sitechip:hover{background:#CDE7D9;color:#074D29}

.teamnone{margin:0;font-size:13px;line-height:1.5;color:var(--muted)}
.teamnone b{color:var(--rust)}

.teamnote{margin:10px 22px 0;font-size:12.5px;line-height:1.5;color:var(--muted)}

/* ---- the foot ------------------------------------------------------------ */

/* Tinted rather than ruled. A hairline across a card this size reads as a seam;
   a change of ground reads as a different kind of content, which is what it is —
   who is in it, and the one destructive thing you can do to it. */
.teamfoot{display:flex;align-items:center;justify-content:space-between;gap:14px;margin-top:16px;padding:13px 22px;background:var(--paper);border-top:1px solid var(--line-2)}
.teamwho{flex:1;min-width:0;font:500 12.5px var(--fb);color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.teamfoot .linky{flex:none;font-size:12.5px}

@media (max-width:520px){
  .teamtop{gap:13px;padding:17px 17px 14px}
  /* The reserved width that lines the team names up on a wide screen is an
     eighth of a phone, and the name is what people are reading. Give it back. */
  .facepile{min-width:0}
  .teamsites{padding:0 17px 4px}
  .teamnote{margin:10px 17px 0}
  .teamfoot{padding:12px 17px;flex-direction:column;align-items:flex-start;gap:9px}
  .teamwho{white-space:normal}
}

/* A tick list of people or of sites. Two columns where there is room, because
   twenty-three names in one column is a scroll that hides the Save button. */
.ticklist{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:0 20px}
.tickrow{display:flex;gap:11px;align-items:flex-start;padding:10px 0;border-top:1px solid var(--line-2);cursor:pointer}
.tickrow input{width:19px;height:19px;flex:none;margin:1px 0 0;accent-color:var(--signal)}
.tickrow span{flex:1;min-width:0}
.tickrow b{display:block;font:600 13.5px var(--fb);color:var(--text);letter-spacing:-.012em}
.tickrow em{display:block;font:400 12px var(--fb);font-style:normal;color:var(--muted);margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tickrow:hover b{color:var(--signal)}

/* No new small-button class here on purpose: `.btn.sm` already exists and
   already carries the width:auto that a button beside a heading needs. A
   second one would drift from it. */

/* ==========================================================================
   Pills on a development card

   The wind chip was on its own; a lift plan chip joins it. They share a row so
   a card carrying both does not grow two lines, and the row is not rendered at
   all when there is nothing to put in it — an empty strip across a card on a
   calm day with no crane reads as something that failed to load.
   ========================================================================== */

.sitepills{display:flex;flex-wrap:wrap;gap:6px;margin-top:11px}
.sitepills .windchip{margin:0}

/* Says the STATE, not just that a plan exists. "There is a lift plan" is close
   to useless; whether anybody has signed it is the whole question, because a
   crane turning up to an unsigned plan is what the module exists to prevent. */
.planchip{display:inline-flex;align-items:center;gap:6px;border-radius:var(--r-tag);padding:3px 10px 3px 8px;font:600 11px var(--fb);white-space:nowrap}
.planchip svg{width:13px;height:13px;flex:none}
.planchip.ok{background:var(--signal-lo);color:#0A5C31}
.planchip.wait{background:var(--indigo-lo);color:#3A3F7A}
/* Fire stopping on the site cards: amber while in hand, green once all signed off. */
.planchip.firechip.wait{background:var(--amber-lo);color:#7A5510}
.planchip.firechip.none{background:#F1F4F2;color:var(--muted)}


/* ==========================================================================
   Evidence on a check — `.evshot`, and why it is not `.shot`

   Lee, 14 Sept: "When the team add pictures to checks there doesn't seem to be
   a way to delete the picture if its wrong."

   There was one. It had been there from the start: a Remove link in the
   figure's caption, wired to a POST that takes the file off disk as well as
   out of the database. It was in the markup, it was in the computed styles,
   and nobody could see it — because **two different screens had both called
   their photo tile `.shot`**.

   The safety screen's version is a bare 4:3 tile with a Remove button floated
   in the corner:  `.shot{aspect-ratio:4/3;overflow:hidden}`.
   The check screen's version is a card with a caption under the picture.
   Stacked in one flat stylesheet, the first one's `aspect-ratio` was never
   overridden — so the figure was locked to 132px tall on a phone while its
   contents came to 229px, and `overflow:hidden` quietly amputated the caption
   and the only Remove link in the app for check evidence.

   This is gotcha 11 for the third time: `.board`, then `.pmark no`, now
   `.shot`. A single `app.css` assembled from parts has no scoping, so a block
   that belongs to one screen gets a name nothing else could want.
   ========================================================================== */

.evshot{margin:0;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);overflow:hidden;aspect-ratio:auto;position:static}
.evshot img,.evshot video{display:block;width:100%;height:130px;object-fit:cover;background:var(--line-2)}
@media (min-width:700px){
  .evshot img,.evshot video{height:150px}
}

/* A destructive link on a phone, pressed by somebody wearing a glove. The text
   is 12px because it sits under a thumbnail; the target it is inside is not. */
.evshot figcaption .linky.danger{margin:-8px -6px -8px 0;padding:8px 6px}

/* Two cards fit across a phone, which leaves about 170px — and "Photo" beside
   "Remove" on one row squeezed the uploader's name onto two lines with the
   file size stranded on the second. Stacked, the meta gets the full width of
   the card and Remove gets a row of its own that is worth aiming at. */
@media (max-width:520px){
  .evshot figcaption{flex-direction:column;align-items:stretch;gap:0}
  .evshot figcaption .linky.danger{margin:9px -11px -10px;padding:9px 11px;border-top:1px solid var(--line-2);text-align:left}
}

/* ---- `.linky.danger` had never once rendered red -------------------------

   `body.app .linky{color:var(--signal)}` is three-and-a-bit specificity points
   and `.linky.danger{color:var(--rust)}` is two, so the green won everywhere:
   taking a signature off a lift plan, taking the crane drawing off, standing
   a team down, and removing a photo from a check all read as ordinary actions.
   Four screens, and it was only found by looking at a fifth. */
body.app .linky.danger{color:var(--rust)}
body.app .linky.danger:hover{color:var(--rust);text-decoration:underline}

/* ---- The safety screen's own Remove, which was a black rectangle ---------

   Found while checking that renaming `.evshot` had not disturbed the screen it
   shares a stylesheet with — and it had not, because that screen was already
   broken in the same way.

   `.shot a{width:100%;height:100%}` exists so the link wrapping the photo fills
   the tile. `.shotx` is also an `a` inside `.shot`, and it sets no size of its
   own, so the small dark pill in the corner inflated to 232 x 174 and sat over
   the whole picture. Nobody had ever put a photo on a near-miss on live, so
   nobody had seen it; the day somebody did, it would have read as exactly what
   Lee reported about check photos — "there is no way to delete this". */
.shot a.shotx{width:auto;height:auto}

/* ==========================================================================
   Wind warnings, folded

   Ten sites with postcodes meant ten identical warnings down the dashboard —
   around 780px of the same sentence between the figures and the jobs, which on
   a phone is more than a screen of scrolling past warnings to reach anything
   you can act on. Three or more now fold behind one line.

   Built on `<details>`, like `.foldlist`: it works with the page's JS unloaded,
   it is keyboard operable for nothing, and the browser tells a screen reader
   what it is without being asked.
   ========================================================================== */

.windfold{margin-top:14px}

/* The summary IS the banner when closed, so it carries the banner's own
   colours rather than inventing a third notice style. */
.windfold>summary{list-style:none;cursor:pointer;display:flex;gap:12px;align-items:flex-start;background:var(--amber-lo);border:1px solid #F0E2C4;border-radius:var(--r-card);padding:14px 15px;color:#7A5510;transition:border-color .12s ease}
.windfold>summary::-webkit-details-marker{display:none}
.windfold>summary:hover{border-color:var(--amber)}
.windfold>summary svg{width:19px;height:19px;flex:none;margin-top:1px}
.windfold>summary>div{flex:1;min-width:0}
.windfold>summary b{display:block;font-size:13.5px;font-weight:600;letter-spacing:-.01em}
.windfold>summary span{display:block;font-size:12.5px;margin-top:3px;line-height:1.45;color:#8A6320}

/* Same chevron as `.foldlist`, drawn from two borders rather than a glyph so it
   sits on the optical centre at any size. */
.windfoldmark{width:8px;height:8px;flex:none;margin:6px 3px 0 0;border-right:1.9px solid currentColor;border-bottom:1.9px solid currentColor;transform:rotate(-45deg);transition:transform .15s ease}
.windfold[open]>summary .windfoldmark{transform:rotate(45deg)}

/* At least one site is over the stop figure right now. */
.windfold.bad>summary{background:var(--rust-lo);border-color:#EBD3CB;color:#8A331E}
.windfold.bad>summary span{color:#95503C}
.windfold.bad>summary:hover{border-color:var(--rust)}

/* The banners inside keep their own margins; the first one would otherwise sit
   flush against the summary it just came out of. */
.windfoldlist{margin-top:2px}

/* ---- the same fold around one site's wind panel ---------------------------
   Site screen and lift plan. The summary is the amber `.windfold` line by
   default; `.warn` says so explicitly, `.bad` (above) is rust, `.fine` is green
   and `.quiet` is the plain card for "not checked". The panel inside keeps its
   own colours — the line and the card are the same ladder, so they agree. */
.windsite{margin:0 0 14px}
.windsite.fine>summary{background:var(--signal-lo);border-color:#CDE7D9;color:#0A5C31}
.windsite.fine>summary span{color:#2F6E4B}
.windsite.fine>summary:hover{border-color:var(--signal)}
.windsite.quiet>summary{background:var(--paper);border-color:var(--line);border-style:dashed;color:var(--muted)}
.windsite.quiet>summary span{color:var(--steel)}
.windsite.quiet>summary:hover{border-color:var(--steel)}
.windsite>.windcard{margin:6px 0 0}
.windsite[open]>summary{border-bottom-left-radius:var(--r-card);border-bottom-right-radius:var(--r-card)}

/* ==========================================================================
   The week across every site

   Ten sites down the side, five days across the top. It borrows the warning
   fold's summary shape — same chevron, same two-line head — but not its
   colours: this one is planning, and a planning grid that arrives in warning
   pink every Monday is a warning nobody believes by Wednesday.
   ========================================================================== */
.windweek{margin-top:12px}
.windweek>summary{list-style:none;cursor:pointer;display:flex;gap:12px;align-items:flex-start;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:13px 15px;color:var(--text);transition:border-color .12s ease}
.windweek>summary::-webkit-details-marker{display:none}
.windweek>summary:hover{border-color:var(--steel)}
.windweek>summary svg{width:19px;height:19px;flex:none;margin-top:1px;color:var(--muted)}
.windweek>summary>div{flex:1;min-width:0}
.windweek>summary b{display:block;font-size:13.5px;font-weight:600;letter-spacing:-.015em}
.windweek>summary span{display:block;font-size:12.5px;margin-top:3px;line-height:1.45;color:var(--muted)}
.windweek>summary .windfoldmark{color:var(--steel)}

.wkwrap{margin-top:8px;background:var(--card);border:1px solid var(--line-2);border-radius:var(--r-card);padding:12px 12px 11px}

/* Nothing to see: a zero-height line for the observer to notice going past. */
.wkstick{display:block;height:0}

/* The name column takes what is left; the day columns are fixed, so ten rows
   line up and the eye can run down a Tuesday. */
.wkgrid{display:grid;grid-template-columns:minmax(0,1fr) repeat(var(--cols,5), 46px);gap:3px;align-items:stretch}

.wkcorner{font:600 12.5px var(--fb);color:var(--steel);display:flex;align-items:flex-end;padding:0 2px 5px}

.wkday{display:flex;flex-direction:column;align-items:center;gap:1px;padding:0 0 5px;border-bottom:1px solid var(--line-2)}
.wkday b{font:600 11px var(--fb);color:var(--muted);white-space:nowrap}
/* How many sites are inside the limit that day — the grid's own summary, sat
   over the column it describes. */
.wkday i{font:600 10px var(--fb);font-style:normal;color:var(--steel)}
.wkday.is-best b{color:var(--signal)}
.wkday.is-best i{color:var(--signal)}

/* `display:block`, not flex. The name is a bare text node, and in a flex
   container that becomes an anonymous flex item — which `text-overflow` does
   not apply to, so the ellipsis never appeared and the names ran straight out
   under the cells instead. */
.wksite{display:block;align-self:center;min-width:0;padding:0 8px 0 2px;font:600 12px var(--fb);letter-spacing:-.01em;color:var(--text);text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wksite:hover{color:var(--signal)}

.wkcell{display:flex;align-items:center;justify-content:center;min-height:28px;border-radius:8px;font:600 11.5px var(--fb);letter-spacing:-.02em;background:var(--signal-lo);color:var(--signal)}
.wkcell.amber{background:var(--amber-lo);color:var(--amber)}
.wkcell.red{background:var(--rust-lo);color:var(--rust)}

.wknote{margin:11px 0 0;font-size:11px;line-height:1.5;color:var(--steel)}

/* ---- the phone ----------------------------------------------------------
   Lee, 21 Sept 2026: *"currently the text is cut off so we may need to redesign
   the layout on mobile."* He was right, and narrowing the columns was never
   going to fix it: ten site names and five day columns do not share 358px.
   "Whitworth Chase, Spennymoor - Vistry" was overflowing by 115px, and
   truncating it to "Whitworth Chase, Sp…" is only a tidier way of not saying
   which site it is.

   So the matrix breaks into blocks — **the name on its own full-width line,
   its five days underneath** — which is the one arrangement where nothing is
   abbreviated. The day columns stay the same five equal columns for every
   block, so a Tuesday still reads straight down the screen; only the corner
   cell goes, because the names no longer sit in a column that needs a heading.
   A grid you have to swipe sideways would have been the other answer, and
   nobody reads the right-hand end of one of those. */
@media (max-width:560px){
  .wkwrap{padding:10px 12px 11px}
  .wkgrid{grid-template-columns:repeat(var(--cols,5), 1fr);gap:5px;row-gap:0}
  .wkcorner{display:none}

  .wksite{grid-column:1 / -1;white-space:normal;overflow:visible;text-overflow:clip;padding:9px 0 4px;font-size:12.5px;line-height:1.3;border-top:1px solid var(--line-2)}
  /* The first name sits straight under the day heads, which already have a
     rule under them. */
  .wkgrid > .wksite:first-of-type{border-top:0;padding-top:7px}

  .wkcell{min-height:29px;font-size:12px;border-radius:9px}
  .wkday b{font-size:11.5px}

  /* The day heads follow you down.
     Ten blocks is taller than a phone — about 640px against 637px of room on a
     large one and rather less on a small one — so the header would scroll away
     somewhere around Coatham Meadows and the columns would stop meaning
     anything. `--barh` is the app bar's measured height, set by
     `webroot/js/sheet.js` on load; without the script the fallback is 0 and the
     heads simply do not stick, which is exactly where this started. */
  .wkday{position:sticky;top:var(--barh, 0px);z-index:1;background:var(--card);padding:6px 0 7px}
  /* The rule under the heads is on the cells' side of the sticky edge, so it
     travels with them instead of being left behind at the top of the list. */
  .wkgrid > .wksite:first-of-type{padding-top:9px}

  /* ---- and when they have stuck ----------------------------------------
     Lee, 21 Sept 2026: *"can the day headers turn the same colour green when
     they hit the main header without gaps between the days of the week."*
     Stuck, they are part of the app bar and should look it — one green bar,
     not five white tiles with daylight between them.

     The gutters are closed with a **box-shadow**, not negative margins. A
     shadow paints into the gap without changing the box, so the day name stays
     centred over its column; margins would have had to be paid back in padding
     and the ends would have drifted. Zero blur, zero spread, offset sideways:
     a solid rectangle of the same colour, which is all a gap needs.

     `webroot/js/stuck.js` puts the class on. Without the script the heads still
     stick and still say what each column is — they just stay white. */
  .wkday{transition:background .16s ease, color .16s ease, box-shadow .16s ease}

  .wkgrid.is-stuck .wkday{background:var(--forest);border-bottom-color:transparent;box-shadow:-3px 0 0 var(--forest), 3px 0 0 var(--forest)}
  /* The outer two reach the card's inner edge — the same 12px `.wkwrap` pads
     by — so the bar runs the full width of the panel rather than stopping
     short of it. */
  .wkgrid.is-stuck .wkday.is-first{box-shadow:-12px 0 0 var(--forest), 3px 0 0 var(--forest)}
  .wkgrid.is-stuck .wkday.is-last{box-shadow:-3px 0 0 var(--forest), 12px 0 0 var(--forest)}

  .wkgrid.is-stuck .wkday b{color:#fff}
  .wkgrid.is-stuck .wkday i{color:#8FBFA6}
  /* The best day keeps its green, lifted to the one that reads on forest. */
  .wkgrid.is-stuck .wkday.is-best b,
  .wkgrid.is-stuck .wkday.is-best i{color:var(--signal-mid)}
}

@media (min-width:760px){
  .wkgrid{grid-template-columns:minmax(0,1fr) repeat(var(--cols,5), 78px);gap:5px}
  .wkcell{min-height:32px;font-size:12.5px}
  .wksite{font-size:13px}
  .wkday b{font-size:12px}
}

/* ==========================================================================
   Dragging a list into the order somebody wants it

   Lee, 21 Sept 2026: "make them drag and drop so paul can organise them how he
   likes."

   The handle is a sibling of the card, never a child of it — the card is a
   link, and a grab handle inside a link is a fight between dragging and
   following it that the link wins on a phone every time. `.can-drag` is added
   by `webroot/js/reorder.js`, so with no script there is no handle, no gutter
   and no hint: the list is exactly the list it was.
   ========================================================================== */
.dragrow{position:relative}
.draghandle,
.draghint{display:none}

.dragwrap.can-drag .dragrow{padding-left:30px}

/* The hint sits outside the list it describes — above it on the home screen,
   inside the page heading on the Developments screen — so it cannot hang off
   `.dragwrap`. `reorder.js` marks the body instead, and `revert` lets the same
   class work on a <span> mid-sentence and on a <p> of its own. */
body.can-drag .draghint{display:revert}

.dragwrap.can-drag .draghandle{display:flex;align-items:center;justify-content:center;position:absolute;left:0;top:0;bottom:0;width:30px;border:0;background:none;padding:0;color:var(--steel);cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none}
.dragwrap.can-drag .draghandle:hover{color:var(--muted)}
.dragwrap.can-drag .draghandle svg{width:17px;height:17px}

/* While a card is in the air. The rows either side slide; the lifted one does
   not, because it is following a finger and has to arrive where the finger is
   rather than somewhere a transition decided. */
.dragrow{transition:transform .16s cubic-bezier(.22,.86,.28,1)}
.dragrow.is-lifted{transition:none;z-index:5}
.dragrow.is-lifted .card{box-shadow:0 2px 6px rgba(6,24,15,.08), var(--lift-2);transform:scale(1.012)}
body.is-dragging{cursor:grabbing;-webkit-user-select:none;user-select:none}
body.is-dragging .draghandle{cursor:grabbing}
/* A card is a link, and a drag that ends over one should not open it. */
body.is-dragging .card{pointer-events:none}

/* Saved, or not. Nothing says "saving" — the cards are already where the person
   put them, so a spinner would be theatre. Only a failure is worth a word. */
.dragwrap.is-unsaved::after{content:"That order could not be saved — try again";display:block;margin-top:10px;padding:9px 12px;border-radius:var(--r-ctl);background:var(--rust-lo);color:var(--rust);font:600 12.5px var(--fb)}

@media (prefers-reduced-motion: reduce){
  .dragrow{transition:none}
}

/* ==========================================================================
   Find — the search under the app bar

   Lee: "should we add the search now, can it be an instant search so results
   show as we type" / "Could it sit under the nav bar".

   A field you can see is a field you use. The app bar at 390px was already full
   — mark, title, bell, avatar — and a fourth button would have taken another
   48px off a title that truncates today, so the search gets its own row and the
   header becomes the two of them stuck together.
   ========================================================================== */

/* The pair, so they travel as one. `.appbar` was sticky by itself before this
   and owned the desktop grid's "bar" area; both moved up here. */
.topbar{position:sticky;top:0;z-index:20;flex:none}
.appbar{position:static}

/* The search lives **inside** the app bar now, so the bar wraps and the field
   takes a line of its own beneath the title and the bell. `order` is what puts
   it there: in the markup it sits before the bell, which is where it belongs on
   a desktop, and without the order it would wrap to line two and push the bell
   down to line three.

   The negative margins pull it back out to the bar's edges, so it looks exactly
   as it did when it was a separate strip — the full-width tint and the hairline
   under it are the bar's own, not a box inside it. */
/* `--barpad` mirrors the bar's own side padding — 14px 16px 16px, set in the
   base stylesheet — because the search bar has to cancel it exactly.
   **It is declared and not applied**: restating the padding here would win over
   the desktop rule that widens it to 26px, which is a media query and so no
   more specific. Changing the bar's padding means changing this line too;
   the desktop never uses it, because there the field sits inline and cancels
   nothing. */
.appbar{--barpad:16px;flex-wrap:wrap}

/* **The width is `100% + both paddings`, not `100%`.** A flex basis of 100%
   resolves against the *content* box, so the negative margins slid the bar
   16px left without making it any wider — it ran 0→358 on a 390px screen and
   sat with 16px of space on the left and 48px on the right. Lee saw it as off
   centre, which is exactly what it was. */
.findbar{order:5;flex:0 0 calc(100% + (var(--barpad) * 2));margin:16px calc(var(--barpad) * -1) calc(var(--barpad) * -1);padding:0 var(--barpad) 12px;background:var(--forest);border-bottom:1px solid rgba(255,255,255,.08)}
.findbar form{position:relative;margin:0}

/* ---- The bar tightening as the page moves (bar.js) --------------------------
   `html[data-condensed]` while the page is moving down past its top. The
   subtitle folds, the find row tucks up under the title, the padding pulls in
   and the mark shrinks — about 130px becomes about 56px. Everything moves on
   the same curve so the bar reads as one thing breathing in, and the page
   content follows because the bar is in the flow. Phone only: the desktop
   rule below sets the field inline and has no row to tuck. */
@media (max-width:959px){
  /* No scroll anchoring: the page moves with the bar, rather than the
     browser moving scrollY to hold a paragraph still and bar.js reading that
     as a scroll. See the note in bar.js. */
  body.app{overflow-anchor:none}
  .appbar{transition:padding .26s cubic-bezier(.2,.75,.3,1)}
  .appbar .mark{transition:width .26s cubic-bezier(.2,.75,.3,1),height .26s cubic-bezier(.2,.75,.3,1)}
  .appbar h2{transition:font-size .26s cubic-bezier(.2,.75,.3,1)}
  .appbar .sub{max-height:20px;transition:max-height .26s cubic-bezier(.2,.75,.3,1),opacity .18s ease,margin .26s cubic-bezier(.2,.75,.3,1)}
  /* `overflow:hidden` only while the row is tucked or on the move. Left on
     all the time it clipped the results panel, which hangs off this row —
     Lee, 23 Sept 2026: *"when i type something into the search bars the
     contents are hidden behind the page"*. bar.js sets `data-moving` for
     the length of the transition. */
  .findbar{max-height:80px;transition:max-height .26s cubic-bezier(.2,.75,.3,1),margin .26s cubic-bezier(.2,.75,.3,1),
      padding .26s cubic-bezier(.2,.75,.3,1),opacity .16s ease}
  html[data-condensed] .findbar,html[data-moving] .findbar{overflow:hidden}
  html[data-condensed] .appbar{padding-top:8px;padding-bottom:9px}
  html[data-condensed] .appbar .mark{width:28px;height:28px}
  html[data-condensed] .appbar h2{font-size:16px}
  html[data-condensed] .appbar .sub{max-height:0;opacity:0;margin-top:0}
  html[data-condensed] .findbar{max-height:0;opacity:0;margin-top:0;margin-bottom:calc(var(--barpad) * -1);padding-bottom:0;border-bottom-color:transparent}
  html[data-still] .appbar,html[data-still] .appbar *,html[data-still] .findbar{transition:none}
}

.findfield{display:flex;align-items:center;gap:9px;background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.15);border-radius:var(--r-ctl);padding:0 12px;transition:background .12s ease,border-color .12s ease}
.findfield:focus-within{background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.34)}
.findfield > svg{width:16px;height:16px;flex:none;color:#8FBFA6}
.findfield input{flex:1;min-width:0;border:0;background:none;outline:none;padding:10px 0;font:500 15px var(--fb);color:#fff;-webkit-appearance:none}
.findfield input::placeholder{color:#7FB39A}
.findfield input::-webkit-search-cancel-button{display:none}

.findclear{flex:none;width:24px;height:24px;margin-right:-4px;border:0;padding:0;background:none;color:#8FBFA6;display:grid;place-items:center;cursor:pointer;border-radius:50%}
/* Gotcha 12, walked into again: a `display` of our own beats the browser's
   `[hidden]{display:none}`, so the cross sat there over an empty field until
   this line. Anything in this stylesheet that sets display and is rendered
   with the attribute needs its own [hidden]. */
.findclear[hidden]{display:none}
.findclear:hover{background:rgba(255,255,255,.14);color:#fff}
.findclear svg{width:14px;height:14px}

/* ---- the panel the results drop into ------------------------------------ */

.findpanel{position:absolute;left:0;right:0;top:calc(100% + 8px);z-index:30;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);box-shadow:var(--lift-2);overflow:hidden}
.findpanelin{max-height:min(62dvh,460px);overflow-y:auto;padding:12px 14px 14px;-webkit-overflow-scrolling:touch}

/* ---- the results themselves, shared by the panel and the screen --------- */

.findlabel{font-family:var(--fd);font-size:12.5px;font-weight:600;color:var(--muted);margin:14px 0 7px;display:flex;align-items:center;gap:8px}
.findlabel:first-child{margin-top:2px}

/* `minmax(0,1fr)`, not the implicit `auto`. A grid item's automatic minimum
   size is its content, so one long development name and a status chip made the
   row wider than the panel holding it and the chip was clipped off the right
   edge. The explicit floor is what lets the name ellipsis instead. */
.findlist{display:grid;grid-template-columns:minmax(0,1fr);gap:4px}

.findrow{display:flex;align-items:center;gap:11px;text-decoration:none;padding:9px 10px;margin:0 -10px;border-radius:var(--r-ctl);color:var(--text);transition:background .1s ease}
.findrow:hover,.findrow:focus-visible,.findrow.on{background:var(--signal-lo);outline:none}
.findrow b{display:block;font:600 14px var(--fb);letter-spacing:-.012em;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.findrow em{display:block;font:400 12px var(--fb);font-style:normal;color:var(--muted);margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.findrow .chip{flex:none}

/* The plot number, sized like the badge on a plot row so the two screens read
   as the same system. */
.findno{flex:none;min-width:44px;padding:0 8px;height:34px;border-radius:9px;background:var(--paper);border:1px solid var(--line-2);display:grid;place-items:center;font:700 14px var(--fb);letter-spacing:-.02em;color:var(--text)}
.findbody{flex:1;min-width:0}

/* A development row is the same shape as a plot row minus the number badge:
   the name and what it is stacked in one block, the way out on the right. The
   first cut made the name and the subtitle two flex children of the row itself
   and they spread across it like a table. */
.findgo{flex:none;font:600 13px var(--fb);color:var(--signal)}

.findhint{margin:10px 0 2px;font-size:12.5px;line-height:1.5;color:var(--muted)}

/* ---- the search screen -------------------------------------------------- */

.findform{margin-bottom:6px}
.search.findbig input{font-size:17px}
/* The browser's own cross is the only blue thing in this app. The field gets
   cleared the way every other field in it does. */
.search.findbig input::-webkit-search-cancel-button{display:none}
.findresults{margin-top:6px}

/* ---- desktop ------------------------------------------------------------ */

@media (min-width:960px){
  /* The wrapper takes the grid's "bar" area, which `.appbar` used to hold on
     its own. No new row and no new area: the header is one thing that happens
     to have two parts, and the pair stick together as they do on a phone. */
  .topbar{grid-area:bar;position:sticky;top:0;z-index:20}
  .appbar{grid-area:auto}

  /* On a desktop it goes back into the flow of the bar: between the screen
     title and the bell, on the one line, in the room that was already there.
     A whole row of chrome holding a single field is a row the page can have
     back. */
  .appbar{flex-wrap:nowrap}
  /* Takes the room that is spare rather than a fixed slice of the viewport.
     A 26vw field measured 255px at 960 and clipped its own placeholder — and
     the title beside it truncates already, so there is no reason to ration the
     search by screen width instead of by what is actually left over. */
  .findbar{order:0;flex:1 1 auto;max-width:360px;min-width:0;margin:0;padding:0;background:transparent;border-bottom:0}
  .findbar form{max-width:none}
  .findfield{background:var(--paper);border-color:var(--line)}
  .findfield:focus-within{background:var(--card);border-color:var(--signal-mid);box-shadow:0 0 0 3px var(--signal-lo)}
  .findfield > svg{color:var(--muted)}
  .findfield input{color:var(--text)}
  .findfield input::placeholder{color:var(--muted)}
  .findclear{color:var(--muted)}
  .findclear:hover{background:var(--line-2);color:var(--text)}
}

/* ==========================================================================
   The bell rings when something is unread

   Lee: "Can the notification bell wobble if there is an unread notification".

   Two swings and then it stops. A bell that never stops moving is the same
   fault as ten identical wind banners down a dashboard — motion that is always
   there stops being a signal and becomes something to ignore, and on a screen
   somebody has open in a site office all day it goes from useful to maddening
   in about a minute. It rings again on the next screen load, because the app
   bar is on every screen and the count is still unread; the moment the feed is
   opened the count is zero, `has-new` is gone, and so is the movement.
   ========================================================================== */

@keyframes bellwobble{
  0%{transform:rotate(0)}
  8%{transform:rotate(-13deg)}
  18%{transform:rotate(11deg)}
  28%{transform:rotate(-8deg)}
  38%{transform:rotate(6deg)}
  48%{transform:rotate(-3deg)}
  /* Still for the rest of the cycle, so two iterations read as two separate
     rings rather than one long wobble. */
  56%,100%{transform:rotate(0)}
}

/* The pivot is the bell's crown, not the middle of its box — a bell hung from
   its centre swings like a pendulum bob and reads as a wobbling picture of a
   bell rather than a bell being rung.

   Only the icon turns. `.belldot` is its sibling, so the count stays square in
   the corner while the bell swings underneath it, which is what makes the
   movement read as the bell rather than as the whole button coming loose. */
.bellbtn.has-new svg{transform-origin:50% 16%;animation:bellwobble 1.4s ease-in-out .45s 2 both}

/* Not a nicety. Some people are made ill by movement they did not ask for, and
   the app already honours this on the lockup and the sheets. The dot alone
   says everything the wobble does. */
@media (prefers-reduced-motion:reduce){
  .bellbtn.has-new svg{animation:none}
}

/* ==========================================================================
   The hero's right-hand side, and the board under it

   Lee, 15 Sept: *"Can we make these squares look way more premium please, they
   look a bit basic. Also the green one above i feel could hold other
   information on the right hand side as it has blank space."*
   ========================================================================== */

/* ---- the hero, now two columns ------------------------------------------ */

/* The bar and the foot still span the full width underneath, so the panel is
   the same panel — it has grown a second column, not been rebuilt. */
.herotop{display:flex;align-items:flex-start;gap:18px;position:relative}
.heromain{flex:1;min-width:0}

/* Divided by a hairline rather than a box: the figure on the right is a
   different subject from the one on the left, and a second bordered card
   inside a card is two cards. */
.heroside{flex:none;text-align:right;text-decoration:none;color:inherit;padding-left:18px;border-left:1px solid rgba(255,255,255,.14);align-self:stretch;display:flex;flex-direction:column;align-items:flex-end;border-radius:0 var(--r-ctl) var(--r-ctl) 0;transition:border-color .14s ease}
.heroside:hover{border-left-color:rgba(255,255,255,.34)}
.heroside .k{color:#8FD3AE}

.herosafe{display:block;font-family:var(--fd);font-size:34px;font-weight:600;letter-spacing:-.045em;line-height:1;margin:10px 0 6px;color:#fff;font-variant-numeric:tabular-nums}
.heroside em{font-style:normal;font-size:12px;color:#9EC6B0;line-height:1.4;display:block}

/* No accident on record is not a zero — it is the absence of one, and a big
   "0" would read as the opposite of what it means. */
.herosafe.clear{display:grid;place-items:center;width:38px;height:38px;margin:10px 0 8px auto;border-radius:12px;background:rgba(95,190,139,.22);color:#8FD3AE}
.herosafe.clear svg{width:21px;height:21px}

@media (max-width:420px){
  .herotop{gap:14px}
  .heroside{padding-left:14px}
  .herosafe{font-size:28px}
  .heroside em{font-size:11.5px}
}

/* ---- the board ---------------------------------------------------------- */

/* What made it look basic was not the colours, it was that every cell had the
   same weight whatever it said. Four noughts in the same 27px black as 1,556
   is six figures all shouting, which reads as a spreadsheet rather than as a
   board somebody designed. Three changes, in order of how much they did:
   a nought goes quiet, the icon gets out of the number's way, and the card
   itself is given a material. */

.card.statboard{border:1px solid var(--line);border-radius:var(--r-surface);box-shadow:var(--lift-1);background:var(--line-2);gap:1px;margin-bottom:6px;transition:box-shadow .16s ease}
.card.statboard:hover{box-shadow:var(--lift-2)}

.bcell{position:relative;background:#FFFFFF;padding:17px 16px 18px;transition:background .14s ease,transform .1s ease}
.bcell:not(.hot):hover{background:#F7FBF8}
.bcell:active{transform:scale(.985)}

/* Top right, small, and out of the way. It used to sit above the number with
   eleven pixels under it, which pushed the figure down the cell and left a
   band of nothing across the middle of all six. */
.bcell .bi{position:absolute;top:13px;right:13px;margin:0;width:26px;height:26px;border-radius:8px}
.bcell .bi svg{width:14px;height:14px}

.bcell b{font-size:30px;letter-spacing:-.05em;font-variant-numeric:tabular-nums}
.bl{font-size:12px;margin-top:7px}

/* A nought is the good answer four times out of six on this board. It should
   read as "nothing to do here", not as a headline. */
.bcell.zero b{color:var(--steel)}
.bcell.zero .bi{opacity:.55}
.bcell.zero .bl{color:var(--steel)}

/* A figure that wants something doing keeps its own colour — one shared alert
   colour across six says only that something is up, not what. The accent along
   the top is new: it catches the eye from the side of a phone in a way a pale
   tint on its own does not. */
.bcell.hot::before{content:"";position:absolute;left:0;right:0;top:0;height:2.5px;background:currentColor;opacity:.85}
.bcell.hot.wait{color:var(--indigo)}
.bcell.hot.no{color:var(--rust)}
.bcell.hot.go{color:var(--amber)}
.bcell.hot.ok{color:var(--signal)}
.bcell.hot .bi{box-shadow:0 1px 2px rgba(6,24,15,.08)}

@media (min-width:700px){
  .bcell b{font-size:32px}
  .bcell{padding:19px 18px 20px}
}

/* --------------------------------------------------------------------------
   The hero, given the width it actually has
   --------------------------------------------------------------------------
   appshell8 moved the bar and the two figures up beside the headline at
   1180px, back when the hero's children were flat: `.k`, `b`, `em`,
   `.herobar`, `.herofoot`, all direct children of `.hero`, all placeable in a
   two-column grid.

   The safety column broke that. `.k`, `b` and `em` now sit inside
   `.heromain`, inside `.herotop`, so the old `grid-column` declarations on
   them land on elements that are not grid items and do nothing at all. What
   was left was the headline stranded in column one and the bar floating on
   its own down the right — which is what the render showed.

   `display:contents` on `.herotop` is the repair: it puts `.heromain` and
   `.heroside` back in the hero's own grid without touching the markup, so the
   phone keeps its flex row and the desktop gets three real columns —
   headline, progress, safety — each divided from the next.

   Guarded on `:has(.herotop)` so the reporting hero, which has no safety
   column, keeps the layout appshell8 wrote for it. */

@media (min-width:1180px){

  .hero:has(.herotop){display:grid;grid-template-columns:minmax(0,1fr) minmax(250px,30%) auto;grid-template-rows:auto auto;column-gap:40px;align-items:end;padding:26px 28px}

  .hero:has(.herotop) .herotop{display:contents}

  .hero:has(.herotop) .heromain{grid-column:1;grid-row:1 / 3}
  .hero:has(.herotop) .heromain b{font-size:52px}

  /* The bar sits on the baseline of the headline, the two figures under it. */
  /* Both in the same cell, centred, and pushed apart by their margins — so the
     pair sits on the panel's centre line as one group. Two rows with the bar
     at the end of one and the labels at the start of the next put the pair
     wherever the rows happened to split, which was nowhere in particular. */
  .hero:has(.herotop) .herobar{grid-column:2;grid-row:1 / 3;align-self:center;margin:0 0 30px}
  .hero:has(.herotop) .herofoot{grid-column:2;grid-row:1 / 3;align-self:center;margin:18px 0 0}

  /* Full height of the card, so the hairline divides rather than dangles. */
  .hero:has(.herotop) .heroside{grid-column:3;grid-row:1 / 3;align-self:stretch;padding-left:26px;justify-content:center}
  .hero:has(.herotop) .herosafe{margin:9px 0 7px}
  .hero:has(.herotop) .herosafe.clear{margin:9px 0 9px auto}
}

/* --------------------------------------------------------------------------
   More than one CPCS card
   --------------------------------------------------------------------------
   A person holds one card or several — one lists A40, A61 and A62 on the
   back of one piece of plastic; most of the team were issued a Lift Supervisor
   card and a Slinger/Signaller card separately.

   The screen is one block per card and one more to add. The block that adds is
   deliberately quieter than the blocks that are real: on the common screen —
   one card, one add block — the eye should land on the card that exists. */

.cardblock{margin-bottom:14px}
.cardblock + .cardblock{margin-top:0}

.cardblockhead{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin:0 0 12px;padding-bottom:11px;border-bottom:1px solid var(--line-2)}
.cardblockhead h4{margin:0;font-family:var(--fd);font-size:16px;font-weight:600;letter-spacing:-.015em;color:var(--text)}
.cardblockhead .chip{flex:none}

.addcard{background:var(--paper);border-style:dashed}
.addcard .cardblockhead{display:block;border-bottom:0;padding-bottom:0;margin-bottom:2px}
.addcard .sub.tight{margin:0 0 12px}

/* What the card evidences — not what the person may do. Three tickboxes that
   have to be readable with a glove on, so they are rows rather than a line. */
.ticketset{border:0;margin:14px 0 0;padding:0}
.ticketset legend{padding:0;font:600 13px var(--fb);color:var(--muted);margin-bottom:8px}
.ticketbox{display:flex;align-items:center;gap:11px;padding:11px 12px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-ctl);margin-bottom:7px;cursor:pointer}
.ticketbox:has(input:checked){border-color:var(--signal);background:var(--signal-lo)}
.ticketbox input{width:20px;height:20px;flex:none;accent-color:var(--signal);margin:0}
.ticketbox span{font:500 14px var(--fb);color:var(--text);display:flex;align-items:baseline;gap:8px;min-width:0}
.ticketbox em{font-style:normal;font-size:11px;font-weight:600;letter-spacing:.06em;color:var(--steel)}
/* `.hint` is styled as `body.app .input .hint`, and this one is not inside an
   `.input` — so it rendered at full body size, twice the size of every other
   hint on the screen. Same rule, stated where it applies. */
.ticketset .hint{display:block;font-size:12px;color:var(--steel);margin-top:2px;line-height:1.45}

/* Remove links sit under the save button, apart from it. */
.cardactions{display:flex;flex-wrap:wrap;gap:16px;margin-top:14px;padding-top:13px;border-top:1px solid var(--line-2)}





/* ==========================================================================
   Somebody's page
   ==========================================================================
   It was eight white cards, each with a paragraph and a full-width outlined
   button. Eight identical rectangles, so nothing on the page said which of them
   mattered — "Reset their PIN" and "Delete this account" were the same object.

   Three things fix that, and none of them is decoration:

   - a **dark header**, so the page opens on the person rather than on a form
   - **stamps**, so what they are qualified for is readable in one look
   - **rows with a small button on the right**, so an action is a line rather
     than a slab, and the destructive ones can be told apart from the rest
   ========================================================================== */

.phero{background:var(--forest);color:#fff;border-radius:var(--r-surface);padding:20px;margin-bottom:12px;position:relative;overflow:hidden;box-shadow:var(--lift-2)}
.phero::after{content:"";position:absolute;right:-50px;top:-60px;width:210px;height:210px;border-radius:50%;background:none}

/* An account that cannot sign in should look like one. Not red — being turned
   off is a normal state, not a fault — just drained of the green. */
.phero.off{background:#14261E}
.phero.off::after{background:none}

.pheromain{display:flex;align-items:center;gap:15px;position:relative}

.pherophoto{position:relative;flex:none;display:block;text-decoration:none;border-radius:50%;line-height:0}
.pherophoto .avatar{box-shadow:0 0 0 3px rgba(255,255,255,.14)}
.pheroedit{position:absolute;right:-2px;bottom:-2px;width:27px;height:27px;border-radius:50%;background:var(--signal);color:#fff;display:grid;place-items:center;box-shadow:0 0 0 3px var(--forest);transition:transform .14s ease}
.phero.off .pheroedit{box-shadow:0 0 0 3px #14261E}
.pheroedit svg{width:13px;height:13px}
.pherophoto:hover .pheroedit{transform:scale(1.09)}

.pherowho{min-width:0;flex:1}
.pherowho h1{margin:0;font-family:var(--fd);font-size:26px;font-weight:600;letter-spacing:-.04em;line-height:1.1;color:#fff;overflow-wrap:anywhere}
.pherowho p{margin:4px 0 0;font-size:13px;color:#9EC6B0;overflow-wrap:anywhere}

.pherotags{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}
.ptag{font:600 11px var(--fb);letter-spacing:.02em;padding:4px 9px;border-radius:var(--r-tag);background:rgba(255,255,255,.12);color:#CFE6D9}
.ptag.ok{background:rgba(95,190,139,.22);color:#8FD3AE}
.ptag.wait{background:rgba(232,179,58,.20);color:#F0CE86}
.ptag.no{background:rgba(214,110,80,.22);color:#F0B3A0}

.pheronone{margin:16px 0 0;font-size:12.5px;color:#9EC6B0;line-height:1.5;position:relative}

/* ---- the stamps ----------------------------------------------------------
   One per lifting ticket held. The ticket code is the thing read at arm's
   length, so it is set large and tight with the words beside it, rather than a
   pill with three words crammed in. */

.stamps{display:grid;gap:8px;margin-top:16px;position:relative}
@media (min-width:520px){.stamps{grid-template-columns:repeat(auto-fit,minmax(196px,1fr))}}

.stamp{display:flex;align-items:center;gap:11px;padding:10px 13px;border-radius:var(--r-ctl);text-decoration:none;background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.13);transition:background .14s ease,border-color .14s ease}
.stamp:hover{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.26)}
.stamp b{font-family:var(--fd);font-size:19px;font-weight:600;letter-spacing:-.04em;line-height:1;flex:none;width:34px;text-align:center;color:#fff}
.stamp span{display:flex;flex-direction:column;gap:2px;min-width:0}
.stamp em{font-style:normal;font:600 12.5px var(--fb);color:#fff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.stamp i{font-style:normal;font-size:11px;color:#9EC6B0;line-height:1.35}

/* The state is what makes it a stamp rather than a label. */
.stamp.ok{background:rgba(95,190,139,.16);border-color:rgba(95,190,139,.34)}
.stamp.ok b{color:#8FD3AE}
.stamp.ok:hover{background:rgba(95,190,139,.24)}
.stamp.warn{background:rgba(232,179,58,.14);border-color:rgba(232,179,58,.36)}
.stamp.warn b,.stamp.warn i{color:#F0CE86}
.stamp.warn:hover{background:rgba(232,179,58,.22)}
.stamp.bad{background:rgba(214,110,80,.16);border-color:rgba(214,110,80,.40)}
.stamp.bad b,.stamp.bad i{color:#F0B3A0}
.stamp.bad:hover{background:rgba(214,110,80,.24)}

/* ---- the cards on file --------------------------------------------------- */

.cardlist{list-style:none;margin:0 0 14px;padding:0;display:grid;gap:8px}
.cardlist li{display:flex;align-items:center;gap:11px;padding:10px 12px;background:var(--paper);border-radius:var(--r-ctl)}
.cardface{flex:none;width:46px;height:30px;border-radius:5px;overflow:hidden;background:var(--line-2);display:block;box-shadow:inset 0 0 0 1px var(--line)}
.cardface img{width:100%;height:100%;object-fit:cover;display:block}
.cardface.none{background:#E7EDE8}
.cardwho{min-width:0;flex:1;display:flex;flex-direction:column;gap:2px}
.cardwho b{font:600 13.5px var(--fb);color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cardwho em{font-style:normal;font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums}
.cardlist .chip{flex:none}

/* ---- an action as a row, not a slab -------------------------------------- */

.card.arows{padding:0;overflow:hidden}
.arow{display:flex;align-items:center;gap:14px;padding:14px 16px}
.arow + .arow{border-top:1px solid var(--line-2)}
.arow > div:first-child{flex:1;min-width:0}
/* `> div > b` — the row's own title, not every `b` in it.
   Written as a bare `.arow b` it claimed emphasis inside the description too,
   so a bolded phrase mid-sentence came out as a second 14px heading on its own
   line and broke the sentence in three. Found by looking at the rendered row,
   not at this rule, which reads perfectly well. */
.arow > div:first-child > b{display:block;font:600 14px var(--fb);color:var(--text)}
.arow span b{font-weight:600;color:var(--text)}
/* …and a warning inside one keeps its own colour, which the line above would
   otherwise take off it on specificity alone. */
.arow span b.warntext{color:var(--rust)}
.arow span{display:block;font-size:12px;color:var(--muted);line-height:1.45;margin-top:3px}
/* Deliberately small. The point of a row is that the button is a button rather
   than a slab — a full-width rectangle here is the thing that made the old page
   look like a list of identical decisions. */
.arow .btn{flex:none;margin:0;width:auto;padding:10px 15px;font-size:13.5px}
/* postLink renders a form around the link; it must not eat the row. */
.arow form{flex:none;margin:0;width:auto}

/* Only on a genuinely narrow screen, and even then the button is sized to its
   own words rather than stretched across the row. 390px has room for both. */
@media (max-width:340px){
  .arow{flex-direction:column;align-items:flex-start;gap:10px}
}

/* A section heading that means "be careful", set apart from the ones above.
   `body.app .sectionlabel` sets the colour, so a bare `.sectionlabel.danger`
   loses to it on specificity and renders in the same near-black as every other
   heading — gotcha 43, which is exactly this, and was found the same way: by
   looking at the rendered colour rather than at the rule. */
body.app .sectionlabel.danger{color:var(--rust);margin-top:30px;padding-top:24px;border-top:1px solid var(--line)}

/* The details list: a row that is a link rather than a fact. */
.plist.detail .drow.edit{justify-content:flex-end;padding-top:11px}
.plist.detail .drow.edit .linky{font-size:13.5px}

/* A notice sitting at the top of a card it belongs to, rather than floating
   between cards. */
.notice.tight{margin:0 0 14px}
.sub.tight{margin:0 0 12px}

/* --------------------------------------------------------------------------
   A site name is not a label, and it must not be cut
   --------------------------------------------------------------------------
   Lee, 15 Sept 2026: *"in search bar can it make the client visible as its cut
   off."* Frame Team's developments are named "Centurion Park - Donaldsons",
   "Fountains Walk, Ripon - TWNY", "Summerville Meadows - Vistry" — the client
   is the **tail** of the name, so a nowrap-and-ellipsis on a narrow row cuts
   off precisely the part that says whose site it is. "Centurion Park - Donal…"
   answers the wrong half of the question.

   So the name wraps, and the cap is **three** lines rather than two. Two looked
   like enough until "Maritime Ave, Hartlepool - Donaldsons" was rendered in the
   dropdown and lost its client on the third line — which is the whole fault,
   back again, in a narrower place. Three is a backstop, not a shape: almost
   every name takes one or two. */

.findrow b{white-space:normal;overflow-wrap:anywhere;line-height:1.3;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}

/* The row can no longer assume both lines are one line tall. */
.findrow{align-items:flex-start}
.findno,.findrow .chip,.findgo{margin-top:1px}

/* On a desktop the field is 360px, so the panel hanging off it would be 360px
   of results — narrower than the phone. It keeps the field's right edge and
   grows to the left instead, which is where the room is. */
@media (min-width:960px){
  .findpanel{left:auto;right:0;width:max(420px,32vw);max-width:560px}
}

/* ==========================================================================
   Time off
   ==========================================================================
   Lee: *"the calendar takes over the screen when on desktop."* It did, and the
   cause was one declaration: `.calday{aspect-ratio:1}`. A square day is right
   at 390px, where the cell comes out 52px. On a 1,200px page the same rule made
   every day a 135px square and the month 700px tall — a month view you have to
   scroll is not a month view.

   A day is square only while square is small. Above that it keeps a sensible
   height and gets wider, which is what a wall planner does.
   ========================================================================== */

@media (min-width:700px){
  .calday{aspect-ratio:auto;min-height:58px}
}

/* And the calendar itself stops filling whatever it is given. Nothing is
   learned from a month 900px wide. */
@media (min-width:960px){
  .cal{max-width:560px}
}

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

.lvhero{background:var(--forest);color:#fff;border-radius:var(--r-surface);padding:20px;margin-bottom:12px;position:relative;overflow:hidden;box-shadow:var(--lift-2);display:flex;flex-wrap:wrap;gap:18px;align-items:center}
.lvhero::after{content:"";position:absolute;right:-50px;top:-60px;width:210px;height:210px;border-radius:50%;background:none}

.lvmain{flex:1 1 210px;min-width:0;position:relative}
.lvmain .k{font-size:13px;color:#8FD3AE;font-weight:500}
.lvmain b{display:block;font-family:var(--fd);font-size:44px;font-weight:600;letter-spacing:-.05em;line-height:1;margin:6px 0 7px;color:#fff;font-variant-numeric:tabular-nums}
.lvmain em{font-style:normal;font-size:13px;color:#9EC6B0;line-height:1.5;display:block;max-width:42ch}

/* Nothing waiting is the normal state and should not shout. Something waiting
   should — it is the only thing on this page that is somebody else's day held
   up by yours. */
.lvhero.hot .lvmain b{color:#F0CE86}

.lvfigs{flex:0 1 auto;display:flex;gap:8px;position:relative;flex-wrap:wrap}
.lvfig{background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.13);border-radius:var(--r-ctl);padding:10px 14px;min-width:92px}
.lvfig b{display:block;font-family:var(--fd);font-size:21px;font-weight:600;letter-spacing:-.04em;line-height:1;color:#fff;font-variant-numeric:tabular-nums}
.lvfig span{display:block;font-size:11px;color:#9EC6B0;margin-top:5px;line-height:1.35}
/* The dates behind a word like "this week", so the figure can be checked. */
.lvfig i{display:block;font-style:normal;font-size:10px;color:#7FA893;margin-top:2px;letter-spacing:.01em;font-variant-numeric:tabular-nums}
.lvfig.under{background:rgba(232,179,58,.14);border-color:rgba(232,179,58,.36)}
.lvfig.under b{color:#F0CE86}

@media (max-width:520px){
  .lvfigs{width:100%}
  .lvfig{flex:1 1 0;min-width:0;padding:10px 11px}
}

/* ---- a card that is one quiet sentence with a mark beside it -------------- */

.quietcard{display:flex;align-items:flex-start;gap:12px}
.quietcard svg{width:22px;height:22px;flex:none;color:var(--steel);margin-top:1px}
.quietcard > span{font-size:13px;color:var(--muted);line-height:1.55}
.quietcard b{color:var(--text);font-weight:600}

/* ---- two columns, the way the plot screen already does it ----------------- */

.leavegrid,.leavemain,.leaveside{display:contents}

@media (min-width:1180px){
  .leavegrid{display:grid;gap:26px;align-items:start;grid-template-columns:minmax(0,1.5fr) minmax(320px,1fr)}
  .leavemain,.leaveside{display:block}

  /* The month stays put while a long queue of requests scrolls past it. */
  .leaveside{position:sticky;top:88px}
  .leavemain > .sectionlabel:first-child,
  .leaveside > .sectionlabel:first-child{margin-top:0}
  .leaveside .cal{max-width:none}
  .leaveside .btn.sm{margin-top:14px}
}

/* ---- the crew's balance panel -------------------------------------------
   Same fault as the calendar, in the other direction: an element sized for a
   390px screen, handed 1,200 and told to centre. The ring sat in the middle of
   a very large dark box with nothing either side of it, and "Book time off"
   was a green button eleven hundred pixels wide.

   Below 760px it stacks and reads exactly as it always did. */

.balanceside{position:relative}
.bookbtn{margin-top:14px}
.balancenote{margin:12px 0 0;font-size:11.5px;line-height:1.55;color:#7FB39A}

@media (min-width:760px){
  .balance{padding:26px 28px}
  .balanceside{max-width:none}
  .balancenext{font-size:31px}
  /* Written as `.btn.bookbtn`, and the reason is worth the line.
     The rule to beat is `body.app a.btn{display:block}` — (0,2,2), because it
     carries **two element selectors**. `body.app .bookbtn` is (0,2,1) and loses
     to it, so the button stayed 854px wide while a rule that read perfectly
     well sat in the stylesheet doing nothing. Adding `.btn` makes it (0,3,1),
     which is exactly the shape `body.app .btn.sm` already uses a few hundred
     lines up — that rule is the worked example, and it was there all along. */
  body.app .btn.bookbtn{width:auto;display:inline-block;margin-top:16px}
  .balancenote{max-width:52ch}
}

/* --------------------------------------------------------------------------
   The lift plan's two popups
   --------------------------------------------------------------------------
   Lee, 21 Sept 2026: a popup when boxes are still blank, and a popup in the
   middle of the screen when the plan saves. And 22 Sept: *"rather than say 23
   more if there are still lots checks to do make the popup scrollable, make it
   work on mobile too, all of this has to look premium."*

   So it is built as a dialog with three bands — a head that stays put, a list
   that scrolls, and the buttons pinned under it — rather than as a box that
   grows until it runs off the screen. Nothing is truncated: forty blanks is a
   forty-item list and the box takes the scroll.

   On a phone it comes off the bottom edge, which is where a thumb already is
   and where every other sheet in this app comes from. Above 560px it is a
   panel in the middle of the screen. One set of rules, one breakpoint. */

.lppop{position:fixed;inset:0;z-index:120;display:flex;align-items:flex-end;justify-content:center;background:rgba(9,50,32,.44);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);animation:lppopfade .18s ease-out both}

.lppopbox{display:flex;flex-direction:column;width:100%;max-height:88dvh;background:var(--card);border-radius:var(--r-surface) var(--r-surface) 0 0;box-shadow:0 12px 32px -12px rgba(6,24,15,.28), 0 2px 6px rgba(6,24,15,.06);animation:lppopup .22s cubic-bezier(.2,.8,.3,1) both;padding-bottom:env(safe-area-inset-bottom);overflow:hidden}

/* ---- the head: title, one line of why, and a grab handle above it -------- */

.lppophead{padding:18px 20px 14px;position:relative;flex:0 0 auto}
.lppophead::before{content:"";position:absolute;top:7px;left:50%;transform:translateX(-50%);width:40px;height:4px;border-radius:var(--r-pill);background:var(--line-2)}
.lppoph{display:block;font-size:17.5px;line-height:1.25;color:var(--text);letter-spacing:-.01em}
.lppopsub{margin:7px 0 0;font-size:13px;line-height:1.5;color:var(--steel)}

/* ---- the list: the only part that scrolls -------------------------------- */

.lppopscroll{flex:1 1 auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;padding:2px 14px 6px;border-top:1px solid var(--line-2);background:linear-gradient(var(--card) 30%, rgba(255,255,255,0)) top/100% 14px no-repeat,
    linear-gradient(rgba(9,50,32,.055), rgba(255,255,255,0)) top/100% 8px no-repeat}

.lppopsec{position:sticky;top:0;z-index:1;padding:12px 6px 6px;background:var(--card);font-size:12.5px;font-weight:600;color:var(--muted)}

.lppoplist{margin:0 0 4px;padding:0;list-style:none}

.lppopitem{display:flex;align-items:center;gap:10px;width:100%;min-height:46px;padding:11px 12px;background:none;border:0;border-radius:var(--r-ctl);font:inherit;font-size:14.5px;line-height:1.35;color:var(--text);text-align:left;cursor:pointer}
/* The chevron is drawn rather than loaded: one more request for six pixels of
   furniture is not worth it, and this file is already the only stylesheet. */
.lppopitem::after{content:"";margin-left:auto;flex:0 0 auto;width:7px;height:7px;border-right:2px solid var(--muted);border-top:2px solid var(--muted);transform:rotate(45deg)}
.lppopitem::before{content:"";flex:0 0 auto;width:6px;height:6px;border-radius:var(--r-pill);background:var(--amber)}
.lppopitem:hover,.lppopitem:focus-visible{background:var(--paper);outline:none}
.lppopitem:focus-visible{box-shadow:inset 0 0 0 2px var(--signal)}
.lppopitem:active{background:var(--line-2)}

/* ---- the buttons: pinned, never scrolled past --------------------------- */

.lppopbtns{flex:0 0 auto;display:grid;gap:9px;padding:14px 20px 18px;border-top:1px solid var(--line-2);background:var(--card)}
.lppopbtns .btn{width:100%;margin:0}

@media (min-width:560px){
  .lppop{align-items:center;padding:24px}
  .lppopbox{width:min(100%,468px);max-height:min(78dvh,680px);border-radius:var(--r-surface);box-shadow:0 12px 32px -12px rgba(6,24,15,.28), 0 2px 6px rgba(6,24,15,.06);animation-name:lppopin}
  .lppophead{padding-top:22px}
  .lppophead::before{display:none}
  .lppopbtns{grid-template-columns:1fr 1fr}
}

@keyframes lppopfade{from{opacity:0}to{opacity:1}}
@keyframes lppopup{from{transform:translateY(14px)}to{transform:none}}
@keyframes lppopin{from{opacity:0;transform:scale(.975)}to{opacity:1;transform:none}}

/* ---- and where it sends you --------------------------------------------- */

/* The field the popup jumped to, for a second and a half. Without it the page
   scrolls and you are looking at the middle of a form wondering which box it
   meant. */
.lpfound{border-radius:var(--r-ctl);animation:lpfound 1.6s ease-out both}
@keyframes lpfound{
  0%,60%{box-shadow:0 0 0 3px var(--amber-lo)}
  100%{box-shadow:0 0 0 3px rgba(0,0,0,0)}
}

/* ---- "Lift plan saved" ---------------------------------------------------
   Lee, 21 Sept 2026: *"When saving the lift plan show a popup in centre of
   screen to say lift plan saved."*

   No buttons and nothing to acknowledge: it fades after a couple of seconds
   and a tap takes it early. A confirmation you have to dismiss costs a tap on
   every single save, and this form is saved a dozen times while a plan is
   being written. */

.savepop{position:fixed;inset:0;z-index:130;display:grid;place-items:center;background:rgba(9,50,32,.20);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:savepopin .18s ease-out both}
.savepop.gone{opacity:0;transition:opacity .25s ease-in}

.savepopbox{display:flex;align-items:center;gap:13px;padding:17px 24px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-surface);box-shadow:0 12px 32px -12px rgba(6,24,15,.28), 0 2px 6px rgba(6,24,15,.06);font-size:16px;letter-spacing:-.01em;color:var(--text)}

.savepopmark{display:inline-grid;place-items:center;width:32px;height:32px;flex:0 0 32px;border-radius:var(--r-pill);background:var(--signal-lo);color:var(--signal)}
.savepopmark svg{width:18px;height:18px}

@keyframes savepopin{from{opacity:0;transform:scale(.97)}to{opacity:1;transform:none}}

@media (prefers-reduced-motion:reduce){
  .savepop,.lppop,.lppopbox{animation:none}
  .savepop.gone{transition:none}
  .lpfound{animation:none;box-shadow:0 0 0 3px var(--amber-lo)}
}

/* ---- a signature kept on file -------------------------------------------
   Shown on white with a hairline round it, the way it will print. A mark on
   the page background reads as decoration; a mark in a box reads as a record
   of something. */

.sigonfile{display:grid;place-items:center;padding:14px;margin:4px 0 12px;background:#fff;border:1px solid var(--line-2);border-radius:var(--r-card)}
.sigonfile img{display:block;max-width:100%;max-height:120px;height:auto}

/* ---- a stage heading inside an editable table ----------------------------
   The checklist preview shows the checks grouped under their stage, because
   "did the stages come out right" is the question somebody has when they are
   looking at twenty-two rows that arrived in a spreadsheet — and a flat table
   cannot answer it. The stage is still an editable box on every row; this is
   a heading over them, not a control. */

body.app .rtable .grouprow td{background:var(--paper);border-top:1px solid var(--line);padding:10px 14px;font-size:12.5px;font-weight:600;color:var(--muted)}
body.app .rtable .grouprow td em{font-style:normal;font-weight:500;letter-spacing:0;text-transform:none;color:var(--steel);margin-left:10px}

/* Below the grid-table breakpoint the rows stack, and a full-width heading
   between them is the only thing keeping them apart — so it keeps its shape
   rather than becoming another stacked "cell" with a data-label. */
@media (max-width:700px){
  body.app .rtable .grouprow{display:block;margin-top:10px}
  body.app .rtable .grouprow td{display:block;width:auto;border-radius:var(--r-ctl)}
  body.app .rtable .grouprow td::before{content:none}
}

/* Two buttons where a page head carries one. `.btn` is width:100% and `.btn.sm`
   is the auto-width variant (gotcha 39), so the pair only needs holding
   together and keeping off the title. */
.headacts{display:flex;gap:8px;flex:none;align-items:center}

/* ==========================================================================
   Folders — drawings and the two handovers
   ==========================================================================

   Lee, 23 Sept 2026: three folders on every development, and the crew see them
   too. They sit above the plots rather than in the Staff-only Manage block, so
   they are styled as a way through rather than as a tile on an admin screen.
   ========================================================================== */

.folders{display:grid;gap:9px;margin-bottom:6px}

.folder{display:grid;grid-template-columns:auto minmax(0,1fr) auto;grid-template-rows:auto auto;align-items:center;column-gap:13px;background:#FFFFFF;border:1px solid var(--line);border-radius:var(--r-card);box-shadow:var(--lift-1);padding:14px 15px;text-decoration:none;color:inherit;transition:box-shadow .16s ease,border-color .16s ease,transform .1s ease}
.folder:hover{box-shadow:var(--lift-2);border-color:var(--signal-mid)}
.folder:active{transform:scale(.995)}

.folder .fi{grid-row:1 / -1;width:38px;height:38px;border-radius:11px;display:grid;place-items:center;background:var(--line-2);color:var(--muted);flex:none}
.folder .fi svg{width:20px;height:20px}
.folder .fi.ok{background:var(--signal-lo);color:var(--signal)}
.folder .fi.go{background:var(--amber-lo);color:var(--amber)}

.folder .fn{grid-column:2;grid-row:1;font:600 15px var(--fd);letter-spacing:-.02em;color:var(--text)}
.folder .fm{grid-column:2;grid-row:2;font-size:12.5px;color:var(--muted);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.folder .fchev{grid-column:3;grid-row:1 / -1;width:17px;height:17px;color:var(--steel);flex:none}

@media (min-width:760px){
  .folders{grid-template-columns:repeat(3,minmax(0,1fr))}
  .folder{grid-template-columns:auto minmax(0,1fr);align-items:start}
  .folder .fchev{display:none}
  .folder .fm{white-space:normal}
}
/* Lift plans made a fourth folder on Taylor Wimpey sites (7 Oct 2026). Three
   across would leave one on a row by itself: four go two by two until there is
   room for all of them on one row. A site with three folders keeps three across. */
@media (min-width:760px){
  .folders:has(> .folder:nth-child(4)){grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (min-width:1500px){
  .folders:has(> .folder:nth-child(4)){grid-template-columns:repeat(4,minmax(0,1fr))}
}

/* ---- The badges on a plot card ------------------------------------------ */

/* Small, last, and only drawn when they say something — see the note in
   `element/plot_card.php`. A badge on every one of two hundred cards is a row
   of noise, and what has to be readable from arm's length is the plot number. */
.pbadges{display:flex;flex-wrap:wrap;gap:5px;margin-top:7px}
.pbadge{display:inline-flex;align-items:center;gap:4px;border-radius:var(--r-tag);padding:3px 8px 3px 6px;font:600 10.5px var(--fb);letter-spacing:.01em;background:var(--line-2);color:var(--muted);white-space:nowrap}
.pbadge svg{width:11px;height:11px;flex:none}
.pbadge.draw{background:var(--indigo-lo);color:var(--indigo)}
.pbadge.ok{background:var(--signal-lo);color:#0A5C31}
.pbadge.go{background:var(--amber-lo);color:#7A5510}

/* ==========================================================================
   Drawings
   ========================================================================== */

.drawlist{display:grid;gap:10px;margin-bottom:16px}

.drawcard{display:grid;grid-template-columns:104px minmax(0,1fr);gap:0;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);box-shadow:var(--lift-1);overflow:hidden}

.drawshot{display:block;background:var(--line-2);min-height:104px;position:relative;text-decoration:none;color:var(--steel)}
.drawshot img{width:100%;height:100%;object-fit:cover;display:block}
.drawnone{position:absolute;inset:0;display:grid;place-items:center}
.drawnone svg{width:26px;height:26px}

.drawbody{padding:13px 14px 12px;min-width:0}
.drawbody b{display:block;font:600 14.5px var(--fd);letter-spacing:-.02em;color:var(--text);line-height:1.3}
.drawmeta{display:block;font-size:12px;color:var(--muted);margin-top:4px}
.drawmeta .rev{font-style:normal;font-weight:700;color:var(--forest-3);background:var(--signal-lo);border-radius:5px;padding:1px 6px;margin-right:4px}
.drawnote{display:block;font-size:12.5px;color:var(--text);margin-top:7px;line-height:1.45}
.drawwho{display:block;font-size:11.5px;color:var(--steel);margin-top:7px}
.drawacts{display:flex;align-items:center;gap:14px;margin-top:11px;flex-wrap:wrap}

@media (max-width:420px){
  .drawcard{grid-template-columns:78px minmax(0,1fr)}
  .drawshot{min-height:78px}
}
@media (min-width:860px){
  .drawlist{grid-template-columns:repeat(2,minmax(0,1fr))}
}

/* The plot types with nothing against them. */
.typechips{display:flex;flex-wrap:wrap;gap:7px}
.typechip{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--line);border-radius:var(--r-tag);padding:6px 7px 6px 12px;font:600 12.5px var(--fb);color:var(--text);text-decoration:none;background:var(--card);transition:border-color .14s,background .14s}
a.typechip:hover{border-color:var(--signal-mid);background:var(--signal-lo)}
.typechip i{font-style:normal;font-size:11px;font-weight:700;color:var(--muted);background:var(--line-2);border-radius:var(--r-tag);padding:2px 7px}

/* ==========================================================================
   Handovers
   ========================================================================== */

.hohero{background:#09301F;color:#fff;border-radius:var(--r-surface);padding:22px;margin-bottom:12px;position:relative;overflow:hidden;box-shadow:var(--lift-2)}
.hohero .k{font:600 12.5px var(--fb);color:#93D9B2}
.hohero b{display:block;font-family:var(--fd);font-size:27px;font-weight:600;letter-spacing:-.035em;margin:9px 0 6px;line-height:1.1}
.hohero em{font-style:normal;font-size:13px;color:#A6CDB8;line-height:1.5;display:block}
.hofacts{display:flex;gap:22px;flex-wrap:wrap;margin-top:18px;padding-top:15px;border-top:1px solid rgba(255,255,255,.14)}
.hofacts span{font-size:11.5px;color:#9EC6B0}
.hofacts b{display:block;font-family:var(--fd);font-size:19px;font-weight:600;letter-spacing:-.03em;color:#fff;margin-bottom:2px;font-variant-numeric:tabular-nums}

.hoacts{display:flex;gap:10px;flex-wrap:wrap;margin:14px 0 4px}
.hoacts .exbtn{flex:0 1 210px}

.hogrid{display:grid;gap:14px}
@media (min-width:620px){
  .hogrid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .hogrid .input:first-child{grid-column:1 / -1}
}

/* ---- One line on a handover -------------------------------------------- */

.card.holist{padding:0 !important;overflow:hidden}
.horow{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:12px;padding:13px 15px;border-bottom:1px solid var(--line-2);align-items:start}
.horow:last-child{border-bottom:none}
.horow.open{background:var(--amber-lo)}
.hoq{min-width:0}
.hoq b{display:block;font:600 13.5px var(--fb);color:var(--text);line-height:1.4}
.hoq em{display:block;font-style:normal;font-size:12px;color:var(--muted);margin-top:3px;line-height:1.4}

/* A three-way segmented control built from radios, so it works with the script
   blocked and a screen reader reads it as the group it is. */
.hoseg{display:inline-flex;flex:none;border:1px solid var(--line);border-radius:var(--r-ctl);overflow:hidden;background:var(--card);align-self:center}
.hoseg label{display:block;position:relative;cursor:pointer}
.hoseg label + label{border-left:1px solid var(--line)}
.hoseg input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
.hoseg i{display:block;font:600 12px var(--fb);font-style:normal;color:var(--muted);padding:9px 11px;text-align:center;white-space:nowrap;transition:background .12s,color .12s}
.hoseg label:hover i{background:var(--paper)}
.hoseg .ok input:checked + i{background:var(--signal);color:#fff}
.hoseg .go input:checked + i{background:var(--amber);color:#fff}
.hoseg .na input:checked + i{background:var(--steel);color:#fff}
.hoseg input:focus-visible + i{outline:2px solid var(--forest-3);outline-offset:-2px}

.honote{grid-column:1 / -1;margin-top:9px;width:100%;border:1px solid var(--line);border-radius:var(--r-ctl);background:var(--card);padding:9px 11px;font:400 13px var(--fb);color:var(--text)}
.honote:focus{outline:none;border-color:var(--signal-mid);box-shadow:0 0 0 3px var(--signal-lo)}
p.honote.read{border:none;background:none;padding:0;margin-top:7px;font-size:12.5px;color:var(--muted);line-height:1.45}

@media (max-width:480px){
  .horow{grid-template-columns:minmax(0,1fr)}
  .hoseg{align-self:stretch;margin-top:10px}
  .hoseg label{flex:1}
  .hoseg i{padding:10px 8px}
}

/* ---- The two signatures ------------------------------------------------- */

.hosigs{display:grid;gap:10px;margin-bottom:16px}
@media (min-width:620px){.hosigs{grid-template-columns:1fr 1fr}}

.hosig{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);box-shadow:var(--lift-1);padding:15px 16px 16px}
.hosig.signed{border-color:var(--signal-mid);background:#FFFFFF}
.hosigk{display:block;font:600 12.5px var(--fb);color:var(--steel);margin-bottom:9px}
.hosig img{display:block;max-width:100%;height:54px;object-fit:contain;object-position:left bottom;margin-bottom:8px}
.hosig b{display:block;font:600 14.5px var(--fd);letter-spacing:-.02em;color:var(--text)}
.hosig span{display:block;font-size:12px;color:var(--muted);margin-top:3px;line-height:1.4}
.hosig .linky{margin-top:10px;display:inline-block}
.hosignone{margin:0 0 12px;font-size:13px;color:var(--muted)}

.hoblanks{margin:0;padding-left:20px}
.hoblanks li{font-size:13px;color:var(--text);margin-bottom:6px;line-height:1.45}
.hoblanks li em{font-style:normal;color:var(--muted)}

/* ---- The two handovers on a plot: a rail, not two cards --------------------
   One row of two pills — icon, name, state and count — above the checks on a
   phone and at the top of the right-hand column on a desktop. The cards this
   replaced carried a sentence each and pushed the first check 450px down. */

.horail{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:0 0 16px}
.hopill{display:flex;align-items:center;gap:10px;min-width:0;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:9px 10px 9px 9px;text-decoration:none;color:inherit;box-shadow:var(--lift-1);transition:box-shadow .16s ease,border-color .16s ease,transform .1s ease}
.hopill:hover{box-shadow:var(--lift-2);border-color:var(--signal-mid)}
.hopill:active{transform:scale(.985)}
.hopill .hsmark{width:34px;height:34px;border-radius:10px;flex:none}
.hopill .hsmark svg{width:17px;height:17px}
.hopilltext{flex:1;min-width:0}
.hopilltext b{display:block;font:600 13px var(--fd);letter-spacing:-.02em;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hopilltext span{display:block;font-size:11.5px;color:var(--muted);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hopill.ok .hopilltext span{color:var(--signal)}
.hopill.go .hopilltext span{color:var(--amber)}
.hopillgo{width:7px;height:7px;flex:none;margin-right:3px;border-right:1.8px solid var(--steel);border-top:1.8px solid var(--steel);transform:rotate(45deg)}
@media (max-width:380px){.horail{grid-template-columns:1fr}}

/* ==========================================================================
   Sharing somebody the app
   ========================================================================== */

.sharecard .sub{margin-bottom:14px}

.sharebtns{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:12px}
.sharebtns .btn{width:auto;flex:0 1 auto}

/* `[hidden]` loses to `display:block`, and `body.app button.btn{display:block}`
   is exactly that rule — so a button rendered hidden shows anyway unless it is
   said again here. Gotcha 12, for the third time on this project. */
.sharebtns[hidden],
.sharebtns .btn[hidden],
.sharesaid[hidden]{display:none}

.sharesaid{margin:0 0 12px;font:600 12.5px var(--fb);color:var(--signal)}

/* The message itself. A `<pre>` because it is a message with its own line
   breaks and they are the thing that makes it readable in WhatsApp — but wrapped
   rather than scrolling, because a link that runs off the side of a phone is a
   link nobody can read to check. */
.sharemsg{margin:0;white-space:pre-wrap;word-break:break-word;font:400 13px/1.6 var(--fb);color:var(--text);background:var(--paper);border:1px solid var(--line);border-radius:var(--r-ctl);padding:14px 15px;-webkit-user-select:all;user-select:all}

/* ==========================================================================
   The next screen, sketched, while it loads

   `skeleton.js` swaps the page for one of these the moment a link is tapped.
   Grey ghosts of the shapes the app is made of — a hero, a board of tiles, a
   run of rows — in the same widths and breakpoints as the real things, so the
   sketch is where the screen will be. The shine is the one piece of motion,
   and it goes under reduced-motion. Nothing here is information.
   ========================================================================== */

.skel{pointer-events:none;user-select:none;animation:skin .18s ease both}
@keyframes skin{from{opacity:0}}
.ghosting{cursor:progress}

.sk{display:block;border-radius:8px;position:relative;overflow:hidden;background:linear-gradient(90deg,#E3E9E4 0%,#F3F6F3 45%,#E3E9E4 90%);background-size:220% 100%;animation:skshine 1.3s linear infinite}
@keyframes skshine{from{background-position:130% 0}to{background-position:-90% 0}}

.skl{height:12px;margin:0 0 9px;width:100%}
.skl.thin{height:9px}
.skl.big{height:17px;margin-bottom:11px}
.skl.huge{height:34px;margin-bottom:12px}
.skl.gap{margin-top:20px}
.skel .w20{width:20%}.skel .w25{width:25%}.skel .w30{width:30%}.skel .w35{width:35%}.skel .w40{width:40%}
.skel .w55{width:55%}.skel .w60{width:60%}.skel .w70{width:70%}.skel .w75{width:75%}.skel .w85{width:85%}

/* A hero is dark; its ghost is too, with the lines as lighter dark. */
.skhero{background:var(--forest);border-radius:var(--r-surface);padding:22px;margin:0 0 12px}
.skhero .sk{background:linear-gradient(90deg,rgba(255,255,255,.09) 0%,rgba(255,255,255,.16) 45%,rgba(255,255,255,.09) 90%);background-size:220% 100%}
.skhero .skbar{height:6px;margin:10px 0 0;border-radius:var(--r-pill)}

.sktitle{margin:0 0 16px}
.skfold{height:56px;border-radius:var(--r-card);margin:0 0 12px}
.skfield{height:46px;border-radius:var(--r-ctl);margin:0 0 12px}
.skchips{display:flex;gap:7px;margin:0 0 14px;overflow:hidden}
.skchip{flex:none;width:92px;height:34px;border-radius:var(--r-pill)}
.skchips .skchip:nth-child(2){width:72px}.skchips .skchip:nth-child(3){width:110px}
.skbar{height:6px;border-radius:var(--r-pill);margin:8px 0 0}
.skpills{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:0 0 16px}
.skpillbox{height:54px;border-radius:var(--r-card)}
.skbtns{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:0 0 18px}
.skbtn{height:48px;border-radius:var(--r-ctl)}
.skanswers{display:grid;grid-template-columns:repeat(3,1fr);gap:9px;margin:0 0 14px}
.skans{height:92px;border-radius:var(--r-ctl)}

.skboard{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--line);border:1px solid var(--line);border-radius:var(--r-surface);overflow:hidden;margin:0 0 14px}
.sktile{background:var(--card);padding:16px 14px}
.sktile .skl{margin-bottom:8px}

.skcard{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);box-shadow:var(--lift-1);margin:0 0 10px}
.skcard.rows{padding:0;overflow:hidden}
.skrow{display:grid;grid-template-columns:40px minmax(0,1fr) auto;align-items:center;column-gap:13px;padding:12px 14px;border-bottom:1px solid var(--line-2)}
.skrow:last-child{border-bottom:none}
.skrow .skl{margin:0 0 6px}
.skrow .skl:last-child{margin-bottom:0}
.skdot{width:40px;height:40px;border-radius:12px}
.skrow .skchip{width:78px;height:24px}
.skcards{display:grid;gap:10px;margin:0 0 14px}
.skcard.site{display:grid;grid-template-columns:62px minmax(0,1fr);column-gap:14px;align-items:start;padding:14px}
.skthumb{width:62px;height:62px;border-radius:14px}
.skpill{width:140px;height:22px;border-radius:var(--r-pill);margin:2px 0 10px}

.skcard.pad{padding:16px}
.skside{display:none}
.skel .w50{width:50%}.skel .w90{width:90%}

@media (min-width:700px){
  .skboard{grid-template-columns:repeat(3,1fr)}
  .skboard.n6{grid-template-columns:repeat(6,1fr)}
}
@media (min-width:1180px){
  .skcards{grid-template-columns:1fr 1fr}
  .sksplit{display:grid;grid-template-columns:minmax(0,1.6fr) minmax(300px,1fr);gap:26px;align-items:start}
  .skside{display:block}
  .skhero{display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,34%);column-gap:46px;padding:26px 28px}
  .skhero .skl:nth-child(1){grid-column:1}
  .skhero .skl:nth-child(2){grid-column:1;height:52px}
  .skhero .skl:nth-child(3){grid-column:1}
  .skhero .skbar{grid-column:2;grid-row:1 / 4;align-self:center}
}

@media (prefers-reduced-motion:reduce){
  .sk,.skel{animation:none}
}

/* ==========================================================================
   Press states, everywhere

   Design review, 23 Sept 2026: cards and pills answered the finger; buttons,
   rows, chips, the rail and the tiles mostly did not, and a thing that does
   not move when it is pressed reads as a thing that did not hear you. One
   rule per family, all the same: a hair smaller under the finger, back the
   moment it lifts. Set as `transition-property` additions where a family
   already animates something else, so nothing it had is lost.
   ========================================================================== */

.stat,.rstat,.filters a,.sitechip,.snlink,.iconbtn,.folder,.wksite,.lvfig,.ptick,
.pickrow,.findres a,.navsheet a,.sheetitem,.leaverow .linky,.card.tap,.tile,
.allclear .btn,.chip[href],a.chip,.hsrow,.teamcard,.rowlink,.qrow,.arow .btn{transition-property:transform,background-color,border-color,box-shadow,color,opacity;transition-duration:.1s,.16s,.16s,.16s,.16s,.16s;transition-timing-function:ease}

.stat:active,.rstat:active,.filters a:active,.sitechip:active,.snlink:active,
.iconbtn:active,.folder:active,.wksite:active,.lvfig[href]:active,.ptick:active,
.pickrow:active,.findres a:active,.navsheet a:active,.sheetitem:active,
.card.tap:active,.tile:active,.chip[href]:active,a.chip:active,.hsrow:active,
.teamcard a:active,.rowlink:active,.qrow:active,.arow .btn:active,.linky:active{transform:scale(.985)}

/* Rows dip rather than shrink — a full-width row scaling reads as the page
   moving. The same tint the People rows already use. */
a.prow:active,a.actrow:active,.leaverow a:active,.crow:active{transform:none;background:var(--signal-lo)}

/* Small things press harder, or the movement is not seen. */
.filters a:active,.sitechip:active,.chip[href]:active,a.chip:active,.iconbtn:active,.linky:active{transform:scale(.95)}

/* The tab bar's icon already dips; the label follows it. */
.tabbar a:active{transform:translateY(1px)}

@media (prefers-reduced-motion:reduce){
  .stat:active,.rstat:active,.filters a:active,.sitechip:active,.snlink:active,.iconbtn:active,
  .folder:active,.wksite:active,.lvfig[href]:active,.ptick:active,.pickrow:active,.findres a:active,
  .navsheet a:active,.sheetitem:active,.card.tap:active,.tile:active,.chip[href]:active,a.chip:active,
  .hsrow:active,.teamcard a:active,.rowlink:active,.qrow:active,.arow .btn:active,.linky:active,
  .tabbar a:active{transform:none}
}

/* Figures that count up (count.js) keep their width while they do it. */
.hero > b,.heromain > b,.stat > b,.rstat > b,.lvmain > b,.lvfig > b,.phtxt > b,.phring > b{font-variant-numeric:tabular-nums}

/* Lee, 24 Sept 2026 — the on-the-day card on a lift plan (the crew need
   to tick the hazards, ground/CBR and put the crane's details in). */
.daycard .daytop{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}
.daycard h2{margin:0}
.daycard .chip{flex:none}

/* Email a copy of a lift plan (LiftPlans/view). Hidden until the Email button
   is pressed — a link to #emailcopy, so :target opens it with no script — and
   left open when the screen comes back with something to say about the send.

   Lee, 24 Sept 2026, on the first desktop look: "the buttons look a bit out of
   place and very long … This needs to look premium, all of the time." So on a
   wide screen the address and Send sit on one line, the buttons are the size of
   what they say rather than the width of the page, and the three actions over
   the plan sit in one row instead of two-and-one.

   `body.app` on the two buttons because `body.app a.btn, body.app button.btn
   {display:block}` is (0,2,2) and would otherwise win — the same trap noted
   further up this file. */
.emailcard{display:none;margin-top:14px;scroll-margin-top:96px;padding:20px 20px 18px}
.emailcard:target,.emailcard.open{display:block;animation:emin .22s ease-out}
@keyframes emin{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
.emailcard .emhead{display:flex;align-items:flex-start;gap:14px;margin-bottom:16px}
.emailcard .embadge{flex:none;width:42px;height:42px;border-radius:12px;display:grid;place-items:center;background:#EAF5EE;color:var(--signal);box-shadow:inset 0 0 0 1px rgba(6,137,67,.14)}
.emailcard .embadge svg{width:21px;height:21px}
.emailcard .emtitle{flex:1;min-width:0}
.emailcard h2{margin:1px 0 0;font-size:17px;letter-spacing:-.2px}
.emailcard .emtitle .sub{margin:4px 0 0;line-height:1.5}
.emailcard .emclose{flex:none;width:34px;height:34px;border-radius:10px;display:grid;place-items:center;color:var(--steel);text-decoration:none;transition:background .15s,color .15s}
.emailcard .emclose:hover{background:#F1F4F2;color:var(--forest)}
.emailcard .emclose svg{width:18px;height:18px}
.emailcard .emailwarn{margin:0 0 14px;padding:11px 13px;border-radius:11px;background:#FBF2E1;color:#7A520A;font-size:13px;line-height:1.45;box-shadow:inset 0 0 0 1px #F0E2C4}
.emailcard .emrow{display:flex;flex-direction:column;gap:10px}
body.app .emailcard .embtn{display:inline-flex;align-items:center;justify-content:center;gap:9px}
.emailcard .embtn svg{width:17px;height:17px;flex:none}
.emailcard .emform .hint{display:block;margin-top:8px}
.emailcard .emlast{display:flex;align-items:flex-start;gap:8px;margin:14px 0 0;padding-top:13px;border-top:1px solid var(--line);font-size:13px;line-height:1.45;color:var(--muted)}
.emailcard .emlast svg{width:16px;height:16px;flex:none;margin-top:1px;color:var(--signal)}
.daycard .daybtn{margin-top:14px}

@media (max-width:639px){
  .exports > .exbtn:nth-child(3):last-child{grid-column:1/-1}
}
@media (min-width:640px){
  .exports{display:flex;flex-wrap:wrap;gap:10px}
  .exports > .exbtn{flex:0 0 200px}
  .emailcard{padding:22px 24px 20px}
  .emailcard .emform{max-width:720px}
  .emailcard .emrow{flex-direction:row;align-items:stretch}
  .emailcard .emrow input{flex:1;min-width:0}
  body.app .emailcard .embtn{width:auto;flex:none;padding:0 24px;white-space:nowrap}
  .emailcard .emailwarn,.emailcard .emlast{max-width:720px}
  body.app .daycard .daybtn{display:inline-flex;align-items:center;justify-content:center;width:auto;min-width:260px;padding:14px 28px}
}

/* ==========================================================================
   Fire-stopping (the report, 26 Sept 2026)
   Staff: Handovers/firestop_form.php and FirestopSettings/index.php.
   Crew:  Handovers/firestop_crew.php — the photo list and nothing else.
   ========================================================================== */

/* ---- The Staff report: two columns on a desktop ------------------------- */
.fslayout{display:grid;gap:0 22px;grid-template-columns:minmax(0,1fr)}
.fsmain,.fsrail{min-width:0}
@media (min-width:1180px){
  .fslayout{grid-template-columns:minmax(0,1fr) 330px;align-items:start}
  .fsrail{position:sticky;top:84px}
  .fsrail > .card:first-child{margin-top:0}
}

.fshero{padding-bottom:26px}
.fsheroprog{position:absolute;left:0;right:0;bottom:0;height:4px;background:rgba(255,255,255,.08)}
.fsheroprog i{display:block;height:100%;width:var(--fp,0%);background:#12AC61;border-radius:0 3px 3px 0;transform-origin:left center;animation:barin .75s cubic-bezier(.2,.75,.3,1) both}

/* Four quiet tools under the hero, never four long buttons. */
.fstools{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:6px;margin:12px 0 6px}
@media (max-width:699px){
  body.app a.fstool{min-height:62px;padding:9px 2px;font-size:11px;line-height:1.2;gap:5px;border-radius:12px}
  body.app a.fstool svg{width:18px;height:18px}
}
body.app a.fstool{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;min-height:66px;padding:10px 6px;border-radius:var(--r-card);background:var(--card);border:1px solid var(--line);box-shadow:var(--lift-1);color:var(--text);text-decoration:none;font:600 12.5px var(--fb);text-align:center;transition:border-color .15s,box-shadow .15s,transform .09s}
body.app a.fstool svg{width:19px;height:19px;color:var(--signal)}
body.app a.fstool:hover{border-color:var(--signal-mid);box-shadow:var(--lift-2)}
body.app a.fstool:active{transform:scale(.97)}
@media (min-width:700px){
  .fstools{display:flex;flex-wrap:wrap}
  body.app a.fstool{flex-direction:row;min-height:0;padding:10px 15px;border-radius:var(--r-ctl);font-size:13px}
  body.app a.fstool svg{width:16px;height:16px}
}

.fssub{margin:-4px 0 12px !important}

/* ---- Next step, and the checklist's own header --------------------------- */
.fsnext{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin:6px 0 4px;padding:14px 16px;border-radius:var(--r-card);background:var(--signal-lo);border:1px solid #CDE7D9}
.fsnextk{flex:none;font:700 12.5px var(--fb);color:#fff;background:var(--signal);border-radius:var(--r-tag);padding:4px 9px}
.fsnextt{flex:1 1 200px;min-width:0}
.fsnextt b{display:block;font:600 15px var(--fd);letter-spacing:-.015em;color:var(--text)}
.fsnextt em{display:block;font-style:normal;font-size:12.5px;color:var(--muted);margin-top:2px;line-height:1.4}
body.app a.fsnextgo{flex:none;display:inline-flex;align-items:center;gap:8px;padding:11px 16px;border-radius:var(--r-ctl);background:var(--forest);color:#fff;font:600 13.5px var(--fb);text-decoration:none;box-shadow:var(--lift-2)}
body.app a.fsnextgo svg{width:16px;height:16px}
body.app a.fsnextgo:active{transform:scale(.97)}
@media (max-width:520px){body.app a.fsnextgo{width:100%;justify-content:center}}
.fschkhead{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
body.app .fsallfit{display:inline-flex;align-items:center;gap:8px;border:none;border-radius:var(--r-ctl);padding:10px 17px;margin:14px 0 8px;font:600 13.5px var(--fb);color:#fff;cursor:pointer;background:#0A9A4D;box-shadow:none;transition:transform .1s,box-shadow .2s}
body.app .fsallfit:hover{box-shadow:none;transform:translateY(-1px)}
body.app .fsallfit:active{transform:scale(.97)}
body.app .fsallfit svg{width:16px;height:16px;color:#fff}
body.app .fsallfit:disabled{background:var(--signal-lo);color:var(--forest-3);box-shadow:none;transform:none;cursor:default}
body.app .fsallfit:disabled svg{color:var(--signal)}

/* The three answers — Approved, Outstanding, N/A — as three real buttons, each
   tinted in its own colour at rest and solid with a lift when chosen. */
.hoseg.fsseg{display:inline-flex;gap:8px;border:none;border-radius:0;overflow:visible;background:none;flex:none}
.hoseg.fsseg label{flex:0 0 auto;min-width:0}
.hoseg.fsseg label + label{border-left:none}
.hoseg.fsseg i{display:flex;align-items:center;justify-content:center;gap:7px;min-height:44px;padding:0 15px;border-radius:12px;border:1.5px solid;font:600 13.5px var(--fb);letter-spacing:-.005em;white-space:nowrap;box-shadow:0 1px 2px rgba(6,24,15,.06);transition:background .15s,color .15s,border-color .15s,box-shadow .2s,transform .1s}
.hoseg.fsseg i svg{width:17px;height:17px;flex:none;stroke-width:2.2}
.hoseg.fsseg .ok i{background:#F1F8F4;border-color:#CBE5D5;color:#0B6E3A}
.hoseg.fsseg .go i{background:#FDF6EA;border-color:#EDD6A9;color:#8A5B08}
.hoseg.fsseg .na i{background:#F6F8F7;border-color:#DCE3DF;color:#5F7268}
.hoseg.fsseg label:hover i{transform:translateY(-1px);box-shadow:0 8px 16px -12px rgba(6,24,15,.55)}
.hoseg.fsseg label:active i{transform:scale(.97)}
.hoseg.fsseg .ok input:checked + i{background:#0A9A4D;border-color:#067A3B;color:#fff;box-shadow:none}
.hoseg.fsseg .go input:checked + i{background:#BF8418;border-color:#9A6509;color:#fff;box-shadow:none}
.hoseg.fsseg .na input:checked + i{background:#95A69C;border-color:#74867B;color:#fff;box-shadow:none}
.hoseg.fsseg input:focus-visible + i{outline:2px solid var(--forest-3);outline-offset:2px}
@media (max-width:560px){
  .fsrowtop .hoseg.fsseg{display:flex;width:100%;margin-left:0}
  .hoseg.fsseg label{flex:1 1 0}
}
@media (max-width:430px){
  .hoseg.fsseg{gap:6px}
  .hoseg.fsseg i{flex-direction:column;gap:4px;min-height:58px;padding:6px 4px;font-size:12.5px}
  .hoseg.fsseg i svg{width:19px;height:19px}
}
@media (prefers-reduced-motion: reduce){.hoseg.fsseg i{transition:none}}

/* ---- Project details ------------------------------------------------------ */
.card.fsdetails{padding:0 !important;overflow:hidden}
.fsfacts{margin:0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;background:var(--line-2);border-bottom:1px solid var(--line-2)}
.fsfacts > div{background:var(--card);padding:13px 16px;min-width:0}
.fsfacts > div.wide{grid-column:1 / -1}
.fsfacts dt{font:600 12.5px var(--fb);color:var(--muted);margin-bottom:5px}
.fsfacts dt label{font:inherit;color:inherit;margin:0}
.fsfacts dd{margin:0;color:var(--text);font:600 14.5px/1.4 var(--fb);overflow-wrap:anywhere}
.fsfacts .warntext{color:var(--amber);font-weight:700;margin-right:6px}
body.app a.fsinline{color:var(--signal);font-weight:600;text-decoration:none;white-space:nowrap}
body.app a.fsinline:hover{text-decoration:underline}
body.app .fsdate{border:none;padding:0;margin:0;background:transparent;font:600 14.5px var(--fb);color:var(--text);width:100%;min-height:22px;outline:none}
body.app .fsdate:focus{color:var(--forest-3)}
@media (min-width:900px){
  .fsfacts{grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr) minmax(0,1.6fr) minmax(0,1fr)}
  .fsfacts > div.wide{grid-column:auto}
}

.fsparties{display:grid;gap:12px;padding:14px}
@media (min-width:760px){.fsparties{grid-template-columns:1fr 1fr}}
.fsparty{margin:0;min-width:0;border:1px solid var(--line);border-radius:12px;padding:12px 14px 12px;background:#FBFCFB;display:grid;gap:9px}
.fsparty legend{float:left;width:100%;padding:0;margin:0 0 2px;font:600 13.5px var(--fb);color:var(--text);display:flex;align-items:center;gap:8px}
.fsparty legend i{font-style:normal;font-weight:500;color:var(--muted);font-size:12.5px}
.fsdot{width:8px;height:8px;border-radius:50%;background:var(--signal);box-shadow:0 0 0 3px var(--signal-lo)}
.fsdot.to{background:var(--indigo);box-shadow:0 0 0 3px var(--indigo-lo)}
.fsparty label{display:grid;grid-template-columns:78px minmax(0,1fr);align-items:center;gap:10px;margin:0}
.fsparty label span{font:600 12px var(--fb);color:var(--muted)}
body.app .fsparty input{width:100%;min-width:0;border:1px solid var(--line);border-radius:9px;padding:9px 11px;font:500 14px var(--fb);color:var(--text);background:#fff;outline:none;transition:border-color .15s,box-shadow .15s}
body.app .fsparty input:focus{border-color:var(--signal-mid);box-shadow:0 0 0 3px var(--signal-lo)}
body.app .fsparty input:disabled{border-color:transparent;background:transparent;padding-left:0}
.fshint{margin:0;font-size:11.5px;color:var(--muted);line-height:1.45}

/* ---- The checklist ------------------------------------------------------- */
.fsrow{padding:14px 16px 15px;border-bottom:1px solid var(--line-2);transition:background .2s}
.fsrow:last-child{border-bottom:none}
.fsrow.open{background:var(--amber-lo)}
.fsrow.na .fsfields{opacity:.45}
.fsrowtop{display:flex;align-items:center;gap:11px;margin-bottom:11px}
.fsrowtop > b{font:600 14.5px var(--fb);letter-spacing:-.01em;color:var(--text);line-height:1.35;min-width:0;flex:1}
.fsrowtop .hoseg{margin-left:auto}
.fsrown{flex:none;width:26px;height:26px;border-radius:50%;display:grid;place-items:center;background:var(--paper);border:1px solid var(--line);color:var(--muted);font:700 11.5px var(--fb)}
.fsrown i{font-style:normal}
.fsrown svg{display:none;width:15px;height:15px}
.fsrow.fitted .fsrown{background:var(--signal);border-color:var(--signal);color:#fff}
.fsrow.fitted .fsrown i{display:none}
.fsrow.fitted .fsrown svg{display:block}
.fsrow.open .fsrown{background:var(--amber);border-color:var(--amber);color:#fff}
.fsrow.na .fsrown{background:var(--steel);border-color:var(--steel);color:#fff}
.fsfields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px 10px;padding-left:37px}
.fsfields label{display:block;min-width:0;margin:0}
.fsfields label.wide{grid-column:1 / -1}
.fsfields span{display:block;font:600 11px var(--fb);letter-spacing:.02em;color:var(--muted);margin-bottom:4px}
body.app .fsfields input{width:100%;border:1px solid transparent;border-radius:9px;padding:8px 10px;font:500 14px var(--fb);color:var(--text);background:var(--paper);outline:none;transition:border-color .15s,background .15s,box-shadow .15s}
body.app .fsfields input:hover{border-color:var(--line)}
body.app .fsfields input:focus{border-color:var(--signal-mid);background:#fff;box-shadow:0 0 0 3px var(--signal-lo)}
body.app .fsfields input:disabled{background:transparent;border-color:transparent;padding-left:0;color:var(--text)}
@media (min-width:700px){
  .fsfields{grid-template-columns:minmax(0,1.5fr) minmax(0,.8fr) minmax(0,1.3fr) minmax(0,.7fr)}
}
@media (max-width:560px){
  .fsrowtop{flex-wrap:wrap}
  .fsrowtop .hoseg{width:100%;margin-left:0;display:flex}
  .fsrowtop .hoseg label{flex:1}
  .fsfields{padding-left:0}
}

/* ---- Photographs, as the report shows them ------------------------------- */
.fsgallery{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-bottom:6px}
@media (min-width:620px){.fsgallery{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (min-width:1000px){.fsgallery{grid-template-columns:repeat(4,minmax(0,1fr))}}
.fsgallery.sm{grid-template-columns:repeat(auto-fill,minmax(130px,1fr));margin-bottom:12px}
.fstile{margin:0;min-width:0;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);box-shadow:var(--lift-1);overflow:hidden;transition:box-shadow .18s,border-color .18s}
.fstile:hover{box-shadow:var(--lift-2)}
.fstile.done{border-color:#CFE6D8}
.fstileimg{position:relative;aspect-ratio:4 / 3;background:var(--paper);display:grid;gap:2px}
.fstileimg.n2{grid-template-columns:1fr 1fr}
.fstileimg.n3{grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr}
.fstileimg.n3 .fspic:first-child{grid-row:1 / 3}
.fspic{position:relative;display:block;min-width:0;min-height:0;overflow:hidden}
.fspic a{display:block;height:100%}
.fspic img{display:block;width:100%;height:100%;object-fit:cover;transition:transform .35s}
.fstile:hover .fspic img{transform:scale(1.03)}
body.app .fspic .fsx{position:absolute;top:7px;right:7px;width:26px;height:26px;border-radius:50%;display:grid;place-items:center;background:rgba(6,24,15,.62);color:#fff;font:600 16px/1 var(--fb);text-decoration:none;backdrop-filter:blur(4px);opacity:0;transition:opacity .15s}
.fstile:hover .fsx,.fspic .fsx:focus-visible{opacity:1}
@media (hover:none){body.app .fspic .fsx{opacity:1}}
.fsempty{position:absolute;inset:8px;border:1.5px dashed #CBD6CF;border-radius:10px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;color:var(--muted);text-align:center;pointer-events:none}
.fsempty svg{width:26px;height:26px;color:var(--signal)}
.fsempty i{font-style:normal;font:600 12.5px var(--fb)}
.fsempty b{font:700 18px var(--fd);letter-spacing:-.02em;color:var(--steel)}
.fstile.empty .fstileimg{background:#F7F9F7}
.fstile.empty:hover .fsempty{border-color:var(--signal-mid);color:var(--forest)}
.fstile.na{box-shadow:none;background:var(--paper)}
.fstile.na .fstileimg{background:#F1F4F2}
.fstile.na .fsempty{border-color:transparent}
.fstile.na .fsempty i{font-weight:500;font-size:11.5px}
/* The camera: the whole empty tile, or a small + over a taken one. */
.fsup{margin:0}
.fstileimg > .fsup{position:absolute;inset:0}
.fstileimg > .fsup .fscam{position:absolute;inset:0;cursor:pointer;display:block}
.fstileimg > .fsup.more{inset:auto 7px 7px auto;width:30px;height:30px}
.fstileimg > .fsup.more .fscam{border-radius:50%;background:rgba(255,255,255,.94);color:var(--forest);display:grid;place-items:center;box-shadow:var(--lift-2)}
.fstileimg > .fsup.more .fscam svg{width:16px;height:16px}
.fscam input[type=file]{position:absolute;inset:0;opacity:0;cursor:pointer;width:100%;height:100%}
.fsup.busy{opacity:.55;pointer-events:none}
.fstile:has(.fsup.busy) .fsempty i:after{content:"…"}
.fstile figcaption{display:grid;grid-template-columns:18px minmax(0,1fr) auto;align-items:center;gap:5px 8px;padding:10px 11px 11px;min-width:0}
.fstile figcaption b{grid-column:2 / 4;font:600 13px/1.3 var(--fb);color:var(--text);min-width:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.fstile figcaption em{grid-column:2;font-style:normal;font:600 11.5px var(--fb);color:var(--muted);font-variant-numeric:tabular-nums}
.fstile.done figcaption em{color:var(--signal)}
.fstile figcaption .fsnatoggle{grid-column:3}
.fstile figcaption .fstick{grid-row:1;align-self:start;margin-top:0}
.fsmain .stickysave{margin-top:4px}
/* On a computer the Save bar reached past its own column and its fade sat over
   the first letters of the right-hand one (found 6 Oct 2026). It stops at the
   edge of the column it belongs to. */
@media (min-width:1180px){
  .fsmain .stickysave{margin-left:0;margin-right:0;padding-left:0;padding-right:0}
}
.fsgallery.sm figcaption b{grid-column:1 / -1}
.fstick{flex:none;width:18px;height:18px;border-radius:50%;border:1.5px solid var(--line);display:grid;place-items:center;color:#fff}
.fstick svg{width:12px;height:12px;opacity:0}
.fstile.done .fstick,.fssteps li.done .fstick{background:var(--signal);border-color:var(--signal)}
.fstile.done .fstick svg,.fssteps li.done .fstick svg{opacity:1}
.fstile.na .fstick{background:var(--steel);border-color:var(--steel)}
body.app a.fsnatoggle{flex:none;font:600 11px var(--fb);color:var(--muted);text-decoration:none;border:1px solid var(--line);border-radius:var(--r-tag);padding:3px 8px;background:#fff}
body.app a.fsnatoggle:hover{color:var(--forest);border-color:var(--steel)}
.fsmore{position:absolute;right:8px;top:8px;background:rgba(6,24,15,.7);color:#fff;font:700 11px var(--fb);border-radius:var(--r-tag);padding:3px 8px}

.card.fsextras{padding:14px !important}
.fsup.extra{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
body.app .fsup .fscap{flex:1 1 220px;min-width:0;border:1px solid var(--line);border-radius:var(--r-ctl);padding:11px 12px;font:500 14px var(--fb);background:var(--paper);outline:none}
body.app .fsup .fscap:focus{border-color:var(--signal-mid);background:#fff;box-shadow:0 0 0 3px var(--signal-lo)}
body.app .fsaddbtn{position:relative;display:inline-flex;align-items:center;gap:8px;margin:0;overflow:hidden;cursor:pointer;width:auto;padding:11px 16px}
.fsaddbtn input[type=file]{position:absolute;inset:0;opacity:0;cursor:pointer;width:100%;height:100%}
.fsaddbtn svg{width:17px;height:17px}

/* ---- The rail: what is left, the signatures, the sign-off ---------------- */
.card.fsstatus{padding:16px !important;margin-top:18px}
@media (min-width:1180px){.card.fsstatus{margin-top:0}}
.fsstatushead{display:flex;align-items:center;gap:14px;margin-bottom:12px}
.fsstatushead > div b{display:block;font:600 16px var(--fd);letter-spacing:-.02em;color:var(--text)}
.fsstatushead > div em{display:block;font-style:normal;font-size:12.5px;color:var(--muted);margin-top:2px}
.fsring{flex:none;width:54px;height:54px;border-radius:50%;display:grid;place-items:center;background:conic-gradient(var(--signal) calc(var(--p) * 1%),var(--line-2) 0);animation:ringin .85s cubic-bezier(.2,.75,.3,1) both}
.fsring b{width:42px;height:42px;border-radius:50%;background:#fff;display:grid;place-items:center;font:700 13px var(--fb);color:var(--forest);font-variant-numeric:tabular-nums}
.fsring b svg{width:20px;height:20px;color:var(--signal)}
.fssteps{list-style:none;margin:0 0 12px;padding:0;border-top:1px solid var(--line-2)}
.fssteps li{border-bottom:1px solid var(--line-2)}
body.app .fssteps a{display:grid;grid-template-columns:18px minmax(0,1fr) auto;align-items:center;gap:10px;padding:10px 2px;text-decoration:none;color:var(--text)}
body.app .fssteps a:hover b{color:var(--forest-3)}
.fssteps b{font:600 13.5px var(--fb)}
.fssteps em{font-style:normal;font-size:12px;color:var(--muted);text-align:right;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:150px}
.fssteps li.done em{color:var(--signal)}
body.app .fscomplete{display:block;width:100%;text-align:center;margin:0}
.fsstatus .fshint{margin-top:8px}
.fsblanks summary{cursor:pointer;font:600 12.5px var(--fb);color:var(--forest-3);list-style:none}
.fsblanks summary::-webkit-details-marker{display:none}
.fsblanks summary:after{content:" ›";display:inline-block;transition:transform .15s}
.fsblanks[open] summary:after{transform:rotate(90deg)}
.fsblanks ul{margin:8px 0 0;padding-left:16px;font-size:12.5px;color:var(--muted);line-height:1.5}
.fsblanks li{margin-bottom:4px}
.fssigs{grid-template-columns:1fr !important}
@media (min-width:700px) and (max-width:1179px){.fssigs{grid-template-columns:1fr 1fr !important}}
.fssig{display:grid;gap:6px;justify-items:start}
.fssig img{max-width:100%;height:64px;object-fit:contain;background:linear-gradient(transparent 52px,var(--line) 52px 53px,transparent 53px);padding:0 4px}
.fssig b{font:600 14px var(--fb)}
.fssig > span:not(.hosigk){font-size:12px;color:var(--muted)}
.fssigempty{display:flex;align-items:center;gap:10px;width:100%;padding:2px 0 0;color:var(--muted);font-size:12.5px}
.fssigempty svg{width:26px;height:26px;flex:none;color:#B9C7BE}
.fssigempty span{margin:0 !important}
.fssig .btn.sm{margin-top:4px}
body.app a.fsdelete{display:flex;align-items:center;justify-content:center;gap:8px;margin:16px 0 4px;padding:11px 14px;border:1px solid #EBCFC6;border-radius:var(--r-ctl);background:#fff;color:var(--rust);font:600 13px var(--fb);text-decoration:none;transition:background .15s,border-color .15s}
body.app a.fsdelete:hover{background:var(--rust-lo);border-color:#DDB3A6}
body.app a.fsdelete svg{width:16px;height:16px}
.fsdecltext{font-size:12px !important;line-height:1.5;margin-top:10px !important}

/* ---- The crew's photos, one at a time (Lee, 26 Sept: "just ask for one
   photo at a time then move onto the next one, make this look premium") ---- */
.fscpreview{margin:0 0 12px}
.fsw{position:relative;max-width:560px;margin:0 auto 20px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-surface);box-shadow:var(--lift-2);overflow:hidden;overflow:clip}
.fswhead{padding:16px 18px 15px;color:#fff;background:#09301F}
.fswmeta{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
.fswref{font:600 12.5px var(--fb);color:#93D9B2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.fswtally{font-size:12px;color:#A6CDB8;white-space:nowrap}
.fswtally b{font:600 17px var(--fd);letter-spacing:-.03em;color:#fff;font-variant-numeric:tabular-nums}
.fswbar{display:flex;gap:3px;margin-top:12px}
.fswbar i{flex:1;height:5px;border-radius:3px;background:rgba(255,255,255,.15);transition:background .3s,transform .3s}
.fswbar i.ok{background:#45E09A}
.fswbar i.na{background:rgba(255,255,255,.4)}
.fswbar i.cur{background:#fff;transform:scaleY(1.5)}
.fswbar i.ok.cur{background:#A8F5CE}

.fswstage{padding:18px 18px 6px}
.fswjs .fswstep{display:none}
.fswjs .fswstep.on{display:block;animation:fswin .34s cubic-bezier(.2,.8,.2,1)}
.fswjs .fswstep.on.back{animation-name:fswback}
.fswstep + .fswstep{margin-top:22px;padding-top:18px;border-top:1px solid var(--line-2)}
.fswjs .fswstep + .fswstep{margin-top:0;padding-top:0;border-top:none}

.fswtitle{margin-bottom:14px}
.fswnum{display:block;font:600 12px var(--fb);color:var(--signal);letter-spacing:.02em}
body.app .fswtitle h2{margin:4px 0 0;font:600 26px/1.12 var(--fd);letter-spacing:-.035em;color:var(--text)}
.fswpart{display:inline-block;margin-top:9px;font-style:normal;font:600 11.5px var(--fb);color:var(--forest-3);background:var(--signal-lo);border-radius:var(--r-tag);padding:4px 10px}

/* The viewfinder. */
.fswframe{position:relative;display:block;aspect-ratio:4 / 3;margin:0;border-radius:16px;overflow:hidden;cursor:pointer;background:#0B2E1E;box-shadow:var(--lift-2)}
.fswframe img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:none}
.fswframe img[src]{display:block}
.fswframe input[type=file]{position:absolute;inset:0;opacity:0;width:100%;height:100%;cursor:pointer;z-index:3}
.fswcorners i{position:absolute;width:24px;height:24px;border:0 solid rgba(255,255,255,.8);z-index:1;transition:opacity .2s}
.fswcorners i:nth-child(1){top:14px;left:14px;border-width:3px 0 0 3px;border-top-left-radius:8px}
.fswcorners i:nth-child(2){top:14px;right:14px;border-width:3px 3px 0 0;border-top-right-radius:8px}
.fswcorners i:nth-child(3){bottom:14px;left:14px;border-width:0 0 3px 3px;border-bottom-left-radius:8px}
.fswcorners i:nth-child(4){bottom:14px;right:14px;border-width:0 3px 3px 0;border-bottom-right-radius:8px}
.fswprompt{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;color:#fff;z-index:1}
.fswprompt svg{width:34px;height:34px;padding:17px;box-sizing:content-box;border-radius:50%;background:rgba(255,255,255,.12);box-shadow:0 0 0 9px rgba(255,255,255,.05);animation:fswpulse 2.4s ease-in-out infinite}
.fswprompt b{font:600 14px var(--fb);color:#CFEBDD;letter-spacing:.01em}
.fswna{position:absolute;inset:0;display:none;flex-direction:column;align-items:center;justify-content:center;gap:4px;z-index:1;background:#EEF2EF}
.fswna b{font:700 30px var(--fd);letter-spacing:-.03em;color:var(--steel)}
.fswna i{font-style:normal;font:600 13px var(--fb);color:var(--muted)}
.fswsent{position:absolute;top:12px;right:12px;z-index:2;width:34px;height:34px;border-radius:50%;display:none;place-items:center;background:var(--signal);color:#fff;box-shadow:0 6px 16px -6px rgba(0,0,0,.5)}
.fswsent svg{width:19px;height:19px}
.fswstep[data-state=taken] .fswsent{display:grid}
.fswstep.sending .fswsent{background:rgba(6,24,15,.55)}
.fswstep.sending .fswsent svg{display:none}
.fswstep.sending .fswsent:after{content:"";width:16px;height:16px;border-radius:50%;border:2.5px solid rgba(255,255,255,.35);border-top-color:#fff;animation:fscspin .8s linear infinite}
.fswstep.unsent .fswsent{background:var(--amber)}
.fswstep[data-state=taken] .fswprompt,.fswstep[data-state=review] .fswprompt,.fswstep[data-state=na] .fswprompt{display:none}
.fswstep[data-state=taken] .fswcorners i,.fswstep[data-state=na] .fswcorners i{opacity:0}
.fswstep[data-state=na] .fswna{display:flex}
.fswstep[data-state=review] input[type=file],.fswstep[data-state=na] input[type=file]{pointer-events:none}
.fswstep.kept .fswframe:after{content:"";position:absolute;inset:0;z-index:4;background:rgba(6,137,67,.42) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.6 12.6l4.2 4.2 8.6-9.4'/%3E%3C/svg%3E") center/84px no-repeat;animation:fswkept .5s ease-out both}

/* The two buttons for whichever state the step is in. */
.fswacts{display:none;gap:10px;margin-top:14px}
.fswstep[data-state=empty] .when-empty,.fswstep[data-state=review] .when-review,
.fswstep[data-state=taken] .when-taken,.fswstep[data-state=na] .when-na{display:flex}
body.app .fswbtn{display:flex;align-items:center;justify-content:center;gap:9px;flex:1;min-height:58px;padding:0 16px;margin:0;border:none;border-radius:15px;font:600 16px var(--fb);cursor:pointer;transition:transform .09s,background .15s,box-shadow .15s}
body.app .fswbtn:active{transform:scale(.97)}
.fswbtn svg{width:21px;height:21px;flex:none}
body.app .fswbtn.go{background:var(--forest);color:#fff;box-shadow:var(--lift-2)}
body.app .fswbtn.ok{background:var(--signal);color:#fff;box-shadow:none}
body.app .fswbtn.ghost{flex:0 0 41%;padding:0 10px;background:var(--card);color:var(--text);border:1px solid var(--line);font-size:15px;white-space:nowrap}
@media (max-width:370px){body.app .fswbtn.ghost{font-size:14px}}
body.app .fswbtn.big{width:100%;min-height:60px;font-size:17px}
body.app .fswbtn:disabled{opacity:.7;cursor:progress}
.fswnalabel{display:flex;align-items:center;gap:8px;margin-top:12px;font:600 14px var(--fb);color:var(--muted)}
.fswjs .fswnalabel{display:none}
.fsw:not(.fswjs) .fswacts{display:none !important}
.fsw:not(.fswjs) .fswgrid{display:none}

.fswnav{display:none}
.fswjs .fswnav{display:flex;justify-content:space-between;padding:4px 12px 12px}
body.app .fswlink{display:inline-flex;align-items:center;gap:6px;background:none;border:none;padding:10px 8px;margin:0;font:600 13.5px var(--fb);color:var(--muted);cursor:pointer;border-radius:10px}
body.app .fswlink:hover{color:var(--forest);background:var(--paper)}
.fswlink svg{width:17px;height:17px}
.fsw.at-start [data-act=back],.fsw.at-summary [data-act=list]{visibility:hidden}

/* The summary: every shot on one screen. */
.fswgrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px 8px;align-items:start}
@media (min-width:480px){.fswgrid{grid-template-columns:repeat(4,minmax(0,1fr))}}
body.app .fswcell{display:block;align-self:start;background:none;border:none;padding:0;margin:0;text-align:left;cursor:pointer;min-width:0;vertical-align:top;font:inherit;color:inherit}
.fswthumb{position:relative;display:block;aspect-ratio:1;border-radius:11px;overflow:hidden;background:var(--paper);border:1.5px dashed #C9D5CD;transition:transform .12s}
body.app .fswcell:active .fswthumb{transform:scale(.96)}
.fswthumb img{display:none;width:100%;height:100%;object-fit:cover}
.fswthumb img[src]{display:block}
.fswcell.ok .fswthumb{border:none}
.fswcell.ok .fswthumb:after{content:"";position:absolute;right:5px;bottom:5px;width:20px;height:20px;border-radius:50%;background:var(--signal) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.6 12.6l4.2 4.2 8.6-9.4'/%3E%3C/svg%3E") center/13px no-repeat;box-shadow:0 2px 6px rgba(0,0,0,.35)}
.fswcell.unsent .fswthumb:after{background-color:var(--amber)}
.fswcell.na .fswthumb{border:none;background:#EEF2EF}
.fswcell.na .fswthumb:before{content:"N/A";position:absolute;inset:0;display:grid;place-items:center;font:700 14px var(--fd);color:var(--steel)}
.fswcell b{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;margin-top:6px;font:600 11.5px/1.3 var(--fb);color:var(--text)}
.fswcell:not(.ok):not(.na) b{color:var(--muted)}
body.app a.fswmark{display:flex;align-items:flex-start;gap:12px;margin:18px 0 0;padding:13px 14px;border-radius:14px;background:var(--indigo-lo);border:1px solid #D6E0F0;color:var(--text);text-decoration:none}
.fswmark svg{width:26px;height:26px;flex:none;color:var(--indigo)}
.fswmark b{display:block;font:600 14px var(--fb)}
.fswmark i{display:block;font-style:normal;font-size:12.5px;color:var(--muted);margin-top:2px;line-height:1.45}
.fswmarkok{display:flex;align-items:center;gap:8px;margin:16px 0 0;font:600 12.5px var(--fb);color:var(--forest-3)}
.fswmarkok svg{width:16px;height:16px;color:var(--signal)}
.fswsave{position:sticky;bottom:calc(76px + env(safe-area-inset-bottom));z-index:5;margin:14px -18px 0;padding:18px 18px 8px;background:#fff}
@media (min-width:900px){.fswsave{bottom:0}}
.fswsave p{margin:9px 0 0;text-align:center;font-size:12px;color:var(--muted)}

body.app a.fswemail{flex:none;width:auto;min-height:50px;margin-top:18px;padding:0 22px;text-decoration:none}
.fswemailwrap{max-width:560px;margin:0 auto}
.fswdone{display:flex;flex-direction:column;align-items:center;text-align:center;gap:8px;padding:38px 24px}
.fswbadge{width:64px;height:64px;border-radius:50%;display:grid;place-items:center;background:var(--signal);color:#fff;box-shadow:0 0 0 10px var(--signal-lo)}
.fswbadge svg{width:32px;height:32px}
.fswdone b{margin-top:14px;font:600 22px var(--fd);letter-spacing:-.03em}
.fswdone em{font-style:normal;font-size:13.5px;color:var(--muted);max-width:300px;line-height:1.5}

@keyframes fswin{from{opacity:0;transform:translateX(28px)}}
@keyframes fswback{from{opacity:0;transform:translateX(-28px)}}
@keyframes fswpulse{50%{box-shadow:0 0 0 15px rgba(255,255,255,.02)}}
@keyframes fswkept{0%{opacity:0;background-size:30px}40%{opacity:1;background-size:96px}100%{opacity:1;background-size:84px}}
@keyframes fscspin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion: reduce){
  .fswjs .fswstep.on,.fswprompt svg,.fswstep.kept .fswframe:after{animation:none}
}

/* ---- Setting up the report (FirestopSettings/index, Staff) --------------- */
.fsnav{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:4px 0 8px}
@media (min-width:900px){.fsnav{grid-template-columns:repeat(4,minmax(0,1fr))}}
body.app .fsnav a{display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto auto;column-gap:12px;align-items:center;padding:14px 16px;border-radius:var(--r-card);background:var(--card);border:1px solid var(--line);box-shadow:var(--lift-1);text-decoration:none;color:var(--text);transition:border-color .15s,box-shadow .15s,transform .09s}
body.app .fsnav a:hover{border-color:var(--signal-mid);box-shadow:var(--lift-2)}
body.app .fsnav a:active{transform:scale(.98)}
.fsnav b{grid-row:1 / 3;font:600 26px var(--fd);letter-spacing:-.04em;color:var(--forest);min-width:34px;font-variant-numeric:tabular-nums}
.fsnav span{font:600 13.5px var(--fb)}
.fsnav i{font-style:normal;font-size:11.5px;color:var(--muted);line-height:1.35}
@media (min-width:900px){.fsnav i{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}}

.fsset .fssethead{display:none}
.fssetrow{position:relative;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:8px;padding:12px 14px 12px 52px;border-bottom:1px solid var(--line-2);align-items:center;margin:0;transition:background .15s}
/* A phone: the number in the margin, the name across the top, the rest two by two under it. */
.fssetrow > .fsseq{position:absolute;left:14px;top:17px}
.fssetrow > input:first-of-type,.fssetrow > .fssetacts,.fssetrow.line input[name=default_comment]{grid-column:1 / -1}
.fssetrow.prod > .fssetacts{grid-column:2;justify-content:flex-end}
.fssetrow:last-child{border-bottom:none}
.fssetrow:hover{background:#FAFBFA}
.fssetrow.dirty{background:#F4FAF6}
.fssetrow.add{background:var(--paper)}
.fssetrow.off input{color:var(--muted);text-decoration:line-through}
.fsseq{grid-row:1;align-self:start;margin-top:7px;width:26px;height:26px;border-radius:50%;display:grid;place-items:center;background:var(--paper);border:1px solid var(--line);font:700 11.5px var(--fb);color:var(--muted)}
.fsseq svg{width:14px;height:14px;color:var(--signal)}
.fssetrow.add .fsseq{background:#fff;border-style:dashed;color:var(--signal);font-size:15px}
body.app .fssetrow input[type=text],body.app .fssetrow select{width:100%;min-width:0;border:1px solid transparent;border-radius:9px;padding:8px 10px;font:500 14px var(--fb);color:var(--text);background:var(--paper);outline:none;transition:border-color .15s,background .15s,box-shadow .15s}
body.app .fssetrow input[type=text]:hover,body.app .fssetrow select:hover{border-color:var(--line)}
body.app .fssetrow input[type=text]:focus,body.app .fssetrow select:focus{border-color:var(--signal-mid);background:#fff;box-shadow:0 0 0 3px var(--signal-lo)}
body.app .fssetrow.add input[type=text]{background:#fff;border-color:var(--line)}
.fssetacts{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.fsmove{display:inline-flex;gap:2px}
body.app .fssave,body.app .fsadd{border:none;border-radius:var(--r-ctl);padding:8px 15px;font:600 13px var(--fb);cursor:pointer;background:var(--signal);color:#fff;transition:opacity .15s,background .15s,transform .09s}
body.app .fssave:active,body.app .fsadd:active{transform:scale(.96)}
.fsjs .fssetrow:not(.dirty) .fssave{background:transparent;color:#B2C1B8;pointer-events:none}
.fssetrow.saving .fssave{opacity:.6}
body.app .fsadd{background:var(--forest)}
body.app a.fsicon,span.fsicon{width:32px;height:32px;border-radius:9px;display:inline-grid;place-items:center;color:var(--muted);text-decoration:none;transition:background .15s,color .15s}
.fsicon svg{width:17px;height:17px}
body.app a.fsicon:hover{background:var(--line-2);color:var(--forest)}
body.app a.fsicon.danger:hover{background:var(--rust-lo);color:var(--rust)}
span.fsicon.lockd{color:#B2C1B8;cursor:help}
span.fsicon.off{visibility:hidden}
/* A product in use or switched off, drawn as the switch it is. */
body.app a.fsswitch{display:inline-flex;align-items:center;gap:7px;text-decoration:none;font:600 12px var(--fb);color:var(--muted);padding:4px 4px 4px 0}
.fsswitch i{position:relative;width:34px;height:20px;border-radius:99px;background:#CFD8D2;transition:background .2s;flex:none}
.fsswitch i:after{content:"";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(6,24,15,.3);transition:transform .2s}
.fsswitch.on i{background:var(--signal)}
.fsswitch.on i:after{transform:translateX(14px)}
body.app a.fsswitch.on{color:var(--forest-3)}
.fschk{position:relative;display:inline-flex;align-items:center;gap:8px;font-size:13px;color:var(--muted);white-space:nowrap;cursor:pointer;margin:0}
.fschk input{position:absolute;opacity:0;width:1px;height:1px}
.fschk i{position:relative;width:34px;height:20px;border-radius:99px;background:#CFD8D2;transition:background .2s;flex:none}
.fschk i:after{content:"";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(6,24,15,.3);transition:transform .2s}
.fschk input:checked + i{background:var(--signal)}
.fschk input:checked + i:after{transform:translateX(14px)}
.fschk input:focus-visible + i{outline:2px solid var(--forest-3);outline-offset:2px}
@media (min-width:900px){
  .fsset .fssethead{display:grid;gap:10px;padding:11px 14px 6px;font:600 12.5px var(--fb);color:var(--muted);border-bottom:1px solid var(--line-2)}
  .fssetrow{padding:12px 14px}
  .fssetrow > .fsseq{position:static}
  .fssetrow > input:first-of-type,.fssetrow > .fssetacts,.fssetrow.line input[name=default_comment],
  .fssetrow.prod > .fssetacts{grid-column:auto}
  .fsseq{grid-row:auto;margin-top:0;align-self:center}
  .fssethead.prod,.fssetrow.prod{grid-template-columns:28px minmax(0,1.5fr) minmax(0,1.2fr) minmax(0,1fr) minmax(0,.7fr) 150px}
  .fssethead.line,.fssetrow.line{grid-template-columns:28px minmax(0,1.6fr) minmax(0,1.1fr) minmax(0,.6fr) minmax(0,1.3fr) 170px}
  .fssethead.slot,.fssetrow.slot{grid-template-columns:28px minmax(0,2fr) 76px 170px 170px}
  .fssetacts{flex-wrap:nowrap;justify-content:flex-end}
}

.card.fswording{padding:0 !important;overflow:hidden}
.fswording form{display:grid;grid-template-columns:minmax(0,1fr)}
@media (min-width:900px){.fswording form{grid-template-columns:1fr 1fr}.fsdoccol + .fsdoccol{border-left:1px solid var(--line-2)}}
.fsdoccol{padding:16px;min-width:0}
.fsdoccol label{display:block;margin:0 0 10px}
.fsdoccol label b{display:block;font:600 14px var(--fb);color:var(--text)}
.fsdoccol label span{display:block;font-size:12px;color:var(--muted);margin-top:2px}
body.app .fsdoccol textarea{width:100%;min-height:150px;resize:vertical;border:1px solid var(--line);border-radius:12px;padding:14px 15px;font:400 13.5px/1.6 var(--fb);color:var(--text);outline:none;background:#fff;box-shadow:inset 0 1px 2px rgba(6,24,15,.04)}
body.app .fsdoccol textarea:focus{border-color:var(--signal-mid);box-shadow:0 0 0 3px var(--signal-lo)}
.fsinspector{grid-column:1 / -1;padding:14px 16px;border-top:1px solid var(--line-2);display:grid;gap:10px}
@media (min-width:900px){.fsinspector{grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-items:center}}
.fsinspector label{margin:0}
.fsinspector label b{display:block;font:600 14px var(--fb);color:var(--text)}
.fsinspector label span{display:block;font-size:12px;color:var(--muted);margin-top:2px}
.fsinspfields{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:8px}
body.app .fsinspfields input{width:100%;min-width:0;border:1px solid var(--line);border-radius:10px;padding:10px 12px;font:500 14px var(--fb);color:var(--text);background:#fff;outline:none}
body.app .fsinspfields input:focus{border-color:var(--signal-mid);box-shadow:0 0 0 3px var(--signal-lo)}
.fswordsave{grid-column:1 / -1;display:flex;justify-content:flex-end;padding:12px 16px;border-top:1px solid var(--line-2);background:var(--paper)}
.fswordsave .btn{width:auto;padding:11px 20px}

@media (prefers-reduced-motion: reduce){
  .fsheroprog i,.fsring{animation:none}
  .fspic img{transition:none}
}

/* ==========================================================================
   Reports as a dashboard (26 Sept 2026)
   Lee: "overhaul this to look like a premium dashbaord with some nice
   coluour, capture everything". Reports/index.php.

   Six domain colours, validated as one set by the dataviz skill's
   validate_palette.js (lightness band, chroma floor, CVD and normal-vision
   separation, contrast — all pass): check #068943, lift #2E5590,
   fire #D4572A, violet #6D4BA8, safety #B7791F, teal #0B8FA3. Every use of
   one is labelled in words beside it.
   ========================================================================== */
.dash{--dc:#068943;--dcl:#E6F3EB}
.d-check{--dc:#068943;--dcl:#E6F3EB;--dcd:#046B34}
.d-lift{--dc:#2E5590;--dcl:#E8EEF8;--dcd:#1F3F72}
.d-fire{--dc:#D4572A;--dcl:#FCEDE6;--dcd:#A8401B}
.d-violet{--dc:#6D4BA8;--dcl:#F0EBF8;--dcd:#533684}
.d-safety{--dc:#B7791F;--dcl:#FBF1E0;--dcd:#8A5A12}
.d-teal{--dc:#0B8FA3;--dcl:#E3F4F6;--dcd:#08707F}
.d-rust{--dc:#A33F27;--dcl:#FAEBE6;--dcd:#7F2F1C}
.d-quiet{--dc:#84968C;--dcl:#F1F4F2;--dcd:#5E6F65}

.dashhead{display:flex;align-items:flex-end;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-bottom:14px}
.dashhead .title{margin:0}
.dashhead .sub{margin:4px 0 0}
.dashexports{display:flex;gap:8px}
body.app a.dashbtn{display:inline-flex;align-items:center;gap:8px;padding:10px 16px;border-radius:var(--r-ctl);background:var(--card);border:1px solid var(--line);box-shadow:var(--lift-1);color:var(--text);text-decoration:none;font:600 13px var(--fb);transition:box-shadow .15s,border-color .15s,transform .09s}
body.app a.dashbtn:hover{border-color:var(--signal-mid);box-shadow:var(--lift-2)}
body.app a.dashbtn:active{transform:scale(.97)}
.dashbtn svg{width:16px;height:16px;color:var(--signal)}
.dash .filterwrap{margin-bottom:14px}

/* ---- The hero ------------------------------------------------------------- */
.dhero{position:relative;overflow:hidden;border-radius:24px;padding:24px;color:#fff;display:grid;gap:20px;grid-template-columns:minmax(0,1fr);align-items:center;background:#08301E;box-shadow:var(--lift-2)}
.dhero::after{content:"";position:absolute;inset:0;pointer-events:none;background:none}
.dring{position:relative;width:148px;height:148px;margin:0 auto}
.dring svg{width:100%;height:100%;transform:rotate(-90deg)}
.dring circle{fill:none;stroke-width:11}
.dring .trk{stroke:rgba(255,255,255,.12)}
.dring .val{stroke:url(#dringg);stroke-linecap:round;animation:dringin 1s cubic-bezier(.2,.75,.3,1) both}
@keyframes dringin{from{stroke-dasharray:0 100}}
.dringt{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center}
.dringt b{font:600 32px var(--fd);letter-spacing:-.05em;font-variant-numeric:tabular-nums}
.dringt span{font-size:12.5px;color:#9FD6B9;margin-top:2px}
.dherot{min-width:0;text-align:center}
.dherot .k{font:600 12.5px var(--fb);color:#93D9B2}
.dherot b{display:block;margin:8px 0 4px;font:600 34px/1.05 var(--fd);letter-spacing:-.045em;font-variant-numeric:tabular-nums}
.dherot b i{font-style:normal;font-size:18px;font-weight:500;color:#A6CDB8;letter-spacing:-.02em}
.dherot em{font-style:normal;font-size:13.5px;color:#A6CDB8}
.dherofacts{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.dherofacts div{border-radius:14px;padding:12px 14px;background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.1);backdrop-filter:blur(6px)}
.dherofacts b{display:block;font:600 22px var(--fd);letter-spacing:-.035em;font-variant-numeric:tabular-nums}
.dherofacts span{display:block;font-size:11.5px;color:#A6CDB8;margin-top:2px;line-height:1.3}
@media (min-width:900px){
  .dhero{grid-template-columns:auto minmax(0,1fr) minmax(0,1.1fr);padding:28px 30px;gap:28px}
  .dring{margin:0}
  .dherot{text-align:left}
  .dherot b{font-size:40px}
}
.dashnote{margin:14px 0 0}

/* ---- Six readings ---------------------------------------------------------- */
.dkpis{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0 8px}
@media (min-width:900px){.dkpis{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (min-width:1280px){.dkpis{grid-template-columns:repeat(6,minmax(0,1fr))}}
.dkpi{position:relative;overflow:hidden;border-radius:18px;padding:15px 15px 14px;min-width:0;background:var(--dcl);border:1px solid color-mix(in srgb,var(--dc) 18%,#E2E8E3);box-shadow:0 1px 2px rgba(6,24,15,.05)}
.dkpi::before{content:"";position:absolute;right:-26px;top:-26px;width:90px;height:90px;border-radius:50%;background:none}
.dkpii{display:grid;place-items:center;width:34px;height:34px;border-radius:11px;color:#fff;margin-bottom:12px;background:var(--dc);box-shadow:none}
.dkpii svg{width:18px;height:18px}
.dkpil{display:block;font:600 12.5px var(--fb);color:var(--dcd)}
.dkpi b{display:block;margin:4px 0 2px;font:600 28px var(--fd);letter-spacing:-.045em;color:var(--text);font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dkpis2{display:block;font-size:11.5px;color:var(--muted);line-height:1.35;min-height:2.7em}
.dkpim{display:block;height:6px;border-radius:99px;margin-top:10px;background:color-mix(in srgb,var(--dc) 12%,#EEF2EF);overflow:hidden}
.dkpim i{display:block;height:100%;border-radius:inherit;background:var(--dcd);animation:barin .75s cubic-bezier(.2,.75,.3,1) both;transform-origin:left center}

/* ---- Section headings -------------------------------------------------------- */
body.app .dsec{display:flex;align-items:center;gap:10px;margin:28px 0 12px;font:600 17px var(--fd);letter-spacing:-.025em;color:var(--text)}
body.app .dsec::before{content:"";width:6px;height:20px;border-radius:3px;background:#068943}

/* ---- Across the business ------------------------------------------------------- */
.dmods{display:grid;grid-template-columns:minmax(0,1fr);gap:12px}
@media (min-width:700px){.dmods{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:1200px){.dmods{grid-template-columns:repeat(3,minmax(0,1fr))}}
body.app a.dmod{position:relative;overflow:hidden;display:flex;flex-direction:column;gap:12px;padding:16px 16px 14px;border-radius:18px;background:#fff;border:1px solid var(--line);text-decoration:none;color:var(--text);box-shadow:0 1px 2px rgba(6,24,15,.05), var(--lift-2);transition:transform .12s,box-shadow .18s,border-color .18s}
body.app a.dmod::before{content:"";position:absolute;left:0;right:0;top:0;height:4px;background:var(--dc)}
body.app a.dmod:hover{transform:translateY(-2px);border-color:color-mix(in srgb,var(--dc) 35%,#E2E8E3);box-shadow:0 1px 2px rgba(6,24,15,.05), var(--lift-2)}
body.app a.dmod:active{transform:scale(.99)}
.dmodh{display:flex;align-items:center;gap:11px}
.dmodi{flex:none;display:grid;place-items:center;width:38px;height:38px;border-radius:12px;color:var(--dc);background:var(--dcl)}
.dmodi svg{width:20px;height:20px}
.dmodt{flex:1;min-width:0;font:600 15px var(--fd);letter-spacing:-.015em}
.dmodt em{display:block;font-style:normal;font:500 11.5px var(--fb);color:var(--muted);letter-spacing:0;margin-top:1px}
.dmodgo{flex:none;width:30px;height:30px;border-radius:50%;display:grid;place-items:center;color:var(--muted);background:var(--paper);transition:background .15s,color .15s}
.dmodgo svg{width:15px;height:15px}
body.app a.dmod:hover .dmodgo{background:var(--dc);color:#fff}
.dmodbig b{display:block;font:600 32px var(--fd);letter-spacing:-.05em;color:var(--dcd);font-variant-numeric:tabular-nums;line-height:1}
.dmodbig span{display:block;font-size:12.5px;color:var(--muted);margin-top:5px}
.dmodgrid{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.dmodgrid span{border-radius:12px;background:var(--dcl);padding:9px 11px;font-size:11.5px;color:var(--muted);line-height:1.3}
.dmodgrid b{display:block;font:600 18px var(--fd);letter-spacing:-.03em;color:var(--text);font-variant-numeric:tabular-nums}
.dmodf{margin-top:auto;padding-top:10px;border-top:1px solid var(--line-2);font-size:12px;color:var(--muted)}
body.app a.dmod.alert .dmodf{color:var(--dcd);font-weight:600}
/* A bar in three parts with its key: done, going, not yet. */
.dseg{display:flex;gap:3px;height:12px;border-radius:99px;overflow:hidden;background:#EEF2EF}
.dseg i{display:block;min-width:4px}
.dseg .sg-done{background:var(--dc)}
.dseg .sg-mid{background:color-mix(in srgb,var(--dc) 55%,#fff)}
.dseg .sg-none,.dseg .empty{background:#E4EAE6}
.dsegkey{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:6px 14px}
.dsegkey li{display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--muted)}
.dsegkey li span{width:9px;height:9px;border-radius:3px}
.dsegkey .sg-done{background:var(--dc)}
.dsegkey .sg-mid{background:color-mix(in srgb,var(--dc) 55%,#fff)}
.dsegkey .sg-none{background:#E4EAE6;box-shadow:inset 0 0 0 1px #CFD8D2}
.dsegkey b{font:600 11.5px var(--fb);color:var(--text);font-variant-numeric:tabular-nums}

/* ---- Chart cards, two to a row ------------------------------------------------ */
.dgrid{display:grid;grid-template-columns:minmax(0,1fr);gap:14px;margin-top:14px}
@media (min-width:1100px){.dgrid{grid-template-columns:repeat(2,minmax(0,1fr))}}
.dcard{min-width:0;background:#fff;border:1px solid var(--line);border-radius:18px;padding:18px 18px 16px;box-shadow:0 1px 2px rgba(6,24,15,.05), var(--lift-2)}
.dcard > header{display:flex;align-items:baseline;justify-content:space-between;gap:10px;flex-wrap:wrap;margin:-2px 0 14px;padding-bottom:12px;border-bottom:1px solid var(--line-2)}
body.app .dcard > header h2{margin:0;font:600 15px var(--fd);letter-spacing:-.02em;color:var(--text)}
.dcard > header span{font-size:12px;color:var(--muted)}
.dash > section.dcard{margin-top:14px}
body.app .dsub{margin:18px 0 10px;font:600 13px var(--fb);color:var(--text)}
.dclear{display:flex;flex-direction:column;align-items:center;text-align:center;gap:4px;padding:18px 10px}
.dclear span{width:46px;height:46px;border-radius:50%;display:grid;place-items:center;color:#fff;margin-bottom:6px;background:#31AE69;box-shadow:0 0 0 7px var(--signal-lo)}
.dclear span svg{width:24px;height:24px}
.dclear b{font:600 14px var(--fb)}
.dclear em{font-style:normal;font-size:12.5px;color:var(--muted)}
.dash .dcard .cdonutwrap{justify-content:center}
.dash .dcard .evline{margin-top:16px}

/* New tones for the chart helper's bars, rings and columns. */
.cbt i.t-violet{background-image:#6D4BA8}
.cbt i.t-teal{background-image:#0B8FA3}
.cbt i.t-fire{background-image:#D4572A}
.ccols rect.t-violet{fill:url(#cgc-violet)}
.ccols rect.t-teal{fill:url(#cgc-teal)}
.ccols rect.t-fire{fill:url(#cgc-fire)}

/* ---- Every development --------------------------------------------------------- */
.dsites{padding:6px 8px !important}
.dsitehead{display:none}
body.app a.dsite{display:grid;grid-template-columns:minmax(0,1fr);gap:7px 14px;align-items:center;padding:12px 10px;border-radius:12px;text-decoration:none;color:var(--text);transition:background .14s}
body.app a.dsite + a.dsite{border-top:1px solid var(--line-2)}
body.app a.dsite:hover{background:#F6F9F7}
body.app a.dsite.on{background:var(--signal-lo)}
.dsname{grid-column:1 / -1;min-width:0}
.dsname b{display:block;font:600 14px var(--fb);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dsname em{display:block;font-style:normal;font-size:11.5px;color:var(--muted)}
.dsbar{display:flex;align-items:center;gap:10px;min-width:0}
.dsbar b{font:600 13px var(--fb);min-width:44px;text-align:right;font-variant-numeric:tabular-nums}
.dsm{flex:1;height:8px;border-radius:99px;background:#EEF2EF;overflow:hidden}
.dsm i{display:block;height:100%;border-radius:inherit}
.dsm.c i{background:#068943}
.dsm.f i{background:#D4572A}
.dsnum{display:none}
.dsk{flex:none;width:72px;font:600 12.5px var(--fb);color:var(--muted)}
.dcol{display:flex;flex-direction:column;gap:14px;min-width:0}
.dsnum b{font:600 14px var(--fb);font-variant-numeric:tabular-nums}
.dsnum em{font-style:normal;font-size:11.5px;color:var(--muted);margin-left:4px}
@media (min-width:900px){
  .dsitehead{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(0,1.3fr) 130px minmax(0,1.1fr) 110px;gap:14px;padding:10px 10px 8px;font:600 12.5px var(--fb);color:var(--muted);border-bottom:1px solid var(--line-2)}
  body.app a.dsite{grid-template-columns:minmax(0,1.5fr) minmax(0,1.3fr) 130px minmax(0,1.1fr) 110px}
  .dsname{grid-column:auto}
  .dsnum{display:block}
  .dsk{display:none}
}

@media (prefers-reduced-motion: reduce){.dring .val,.dkpim i{animation:none}}
@media print{
  .dhero,.dkpi,.dmod,.dcard{box-shadow:none}
  .dashexports{display:none}
}

/* A bar row as label and reading on one line with the bar under both — on a
   phone everywhere (the reading used to drop under the bar on its own), and in
   the dashboard's half-width cards, where side by side left the label twelve
   characters. */
@media (max-width:699px){
  .cbar .cbl{grid-row:1;grid-column:1}
  .cbar .cbv{grid-row:1;grid-column:2}
  .cbar .cbt{grid-row:2;grid-column:1 / -1}
  .cbar .cbn{grid-row:3;grid-column:1 / -1}
}
@media (min-width:1100px){
  .repscreen .dgrid .cbar{grid-template-columns:minmax(0,1fr) auto;align-items:baseline}
  .repscreen .dgrid .cbar .cbl{grid-row:1;grid-column:1;white-space:normal}
  .repscreen .dgrid .cbar .cbv{grid-row:1;grid-column:2}
  .repscreen .dgrid .cbar .cbt{grid-row:2;grid-column:1 / -1;margin-top:8px}
  .repscreen .dgrid .cbar .cbn{grid-row:3;grid-column:1 / -1}
}

/* ---------------------------------------------------------------------------
 * Notifications on You (27 Sept 2026) — Dashboard/account.php, js/push.js.
 * ------------------------------------------------------------------------- */
.pushcard [hidden]{display:none!important}
.pushnow{background:var(--line-2);border-radius:14px;padding:13px 14px;margin:4px 0 12px}
.pushstate{display:flex;align-items:center;gap:8px;font:700 14.5px/1.3 var(--fb);color:var(--ink)}
.pushstate::before{content:"";width:9px;height:9px;border-radius:50%;background:#B5C2BA;flex:none}
.pushstate.on::before{background:var(--signal);box-shadow:0 0 0 4px var(--signal-lo)}
.pushnow .sub{margin:6px 0 0}
.pushbtns{display:flex;flex-wrap:wrap;gap:8px;margin-top:11px}
.pushbtns:not(:has(button:not([hidden]))){display:none}
.pushdevices{margin:0 0 6px}
.pushprefs h3{font:700 13px/1 var(--fb);color:var(--muted);margin:14px 0 6px}
.pushpref{display:flex;align-items:flex-start;gap:12px;padding:11px 2px;border-bottom:1px solid var(--line-2);cursor:pointer}
.pushpref:last-of-type{border-bottom:0}
.pushpref input[type=checkbox]{appearance:none;-webkit-appearance:none;flex:none;width:40px;height:24px;border-radius:99px;background:#CFD8D2;position:relative;margin:1px 0 0;cursor:pointer;transition:background .2s}
.pushpref input[type=checkbox]::after{content:"";position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.25);transition:transform .2s}
.pushpref input[type=checkbox]:checked{background:var(--signal)}
.pushpref input[type=checkbox]:checked::after{transform:translateX(16px)}
.pushpref input[type=checkbox]:focus-visible{outline:2px solid var(--signal);outline-offset:2px}
.pushpref span{display:grid;gap:2px}
.pushpref b{font:600 14.5px/1.3 var(--fb);color:var(--ink)}
.pushpref em{font-style:normal;font-size:13px;color:var(--muted)}
.pushprefs .btn{margin-top:12px}


/* ==========================================================================
   The sign-in — a timber frame going up, above the form or beside it.

   Lee, 29 Sept 2026: "the sign in page on desktop seems to use the mobile
   sign in page, can we create a desktop sign in page, maybe a split screen
   view with login on the right hand side".

   Lee, 7 Oct 2026: "i would like to update the left panel with something that
   looks incredible and relevant to his business, it should be animated". It
   was designed on a Claude Design canvas with the Frame Team BQC design
   system; he said yes to it replacing the crane that was here, and to the
   form beside it being white as it is on the canvas. Then, on the canvas, the
   headline and ticking checks and the "Plot 01" chip came out and the logo
   was to be built too ("so it looks like its also being constructed"). And:
   "can we make all this work on mobile too", with the crane that used to lift
   the phone's sign-in screen into place retired for it.

   So there is one sign-in. The green panel (element/auth_side.php) holds the
   logo and the frame, and the white screen (.auth) holds the form:
     - under 1024px wide the panel is a band across the top, logo on the left
       and frame on the right, and the form is a column under it;
     - from 1024px the page splits, panel on the left and form on the right.
   The form comes first in the page either way, so the keyboard and a screen
   reader reach it first; `order` and the grid put the panel where it shows.

   The frame is a two-storey one going up on its slab
   (element/frame_build.php), drawn in the same 2:1 projection as the three
   blocks of the logo, which are lowered in on slings the same way
   (element/mark.php, `slung`). There is no script behind any of it. One
   clock, --ft-t, runs 0 to 1 over a 26 second loop on .auth-side, and every
   moving piece works out where it should be from that clock and its own start
   (--ft-s) with clamp() and calc(): so the logo and the frame cannot drift
   apart. --ft-f fades the lot out together at the end of a loop.

   The clock only runs under html.ft-go, which the layout's head script sets
   on a screen that asks for it. Everywhere else --ft-t rests at .82: the
   finished logo and the finished, signed-off frame. That covers no script,
   reduced motion, a browser without registered custom properties, and the
   set-PIN screens.

   Names: ft- for the panel, fb- for what is inside the drawing. (`--p` is
   already a registered property of the plot ring, so nothing here uses a bare
   one-letter custom property.)
   ========================================================================== */
@property --ft-t{syntax:"<number>";inherits:true;initial-value:.82}
@keyframes ft-t{from{--ft-t:0}to{--ft-t:1}}

.auth-side{display:flex;position:relative;overflow:hidden;flex:none;background:#093220;color:#fff;
  --ft-tm:#EDCF96;--ft-t:.82;
  --ft-f:clamp(0,calc((.94 - var(--ft-t)) * 26),1)}
html.ft-go .auth-side{animation:ft-t 26s linear var(--ft-d,0s) infinite}
@media (prefers-reduced-motion:reduce){
  html.ft-go .auth-side{animation:none}
}

/* ---- the logo, a block at a time ----------------------------------------
   The mark over the wordmark, in the proportions .lockup stacks them in. The
   mark's box is square with room above the blocks, hence the pull upwards. */
.ft-top{position:relative;z-index:2;flex:none}
.ft-logo{--ls:100px;display:inline-flex;flex-direction:column;align-items:center;margin-top:calc(var(--ls) * -.16)}
.ft-mark{display:block;width:var(--ls);height:var(--ls);overflow:visible}
.ft-wm{display:block;width:calc(var(--ls) * 1.5);margin-top:calc(var(--ls) * -.064);color:#fff;
  --ft-p:clamp(0,calc((var(--ft-t) - .085) * 32),1);opacity:calc(var(--ft-p) * 4 * var(--ft-f));
  clip-path:inset(0 calc((1 - var(--ft-p)) * 100%) 0 0)}
.ft-mark .ft-lb{--ft-p:clamp(0,calc((var(--ft-t) - var(--ft-s)) * 37),1);opacity:calc(var(--ft-p) * 3 * var(--ft-f));
  transform:translateY(calc((1 - var(--ft-p)) * (1 - var(--ft-p)) * -420px))}
.ft-mark .ft-ls{--ft-q:clamp(0,calc((var(--ft-t) - var(--ft-s) - .03) * 45),1);opacity:calc(1 - var(--ft-q));
  transform:translateY(calc(var(--ft-q) * -260px));fill:none;stroke:#fff;stroke-opacity:.6;stroke-width:5;stroke-linejoin:round}
.ft-mark .ft-lh{fill:#fff;stroke:none}

.ft-scenebox{position:relative}
.fb-scene{position:absolute;inset:0;width:100%;height:100%;overflow:visible}

/* ---- a phone or a tablet: the panel across the top ----------------------- */
@media (max-width:1023px){
  .auth-side{order:-1;align-items:center;justify-content:center;gap:clamp(10px,4.5vw,48px);
    --ft-h:clamp(168px,29dvh,330px);height:var(--ft-h);
    padding:env(safe-area-inset-top,0px) max(16px,env(safe-area-inset-right,0px)) 0 max(18px,env(safe-area-inset-left,0px))}
  /* as wide as the phone allows, but never a bigger thing than the frame beside it */
  .ft-logo{--ls:clamp(60px,min(calc(4px + 16vw),calc(var(--ft-h) * .38)),108px);margin-top:0}
  .ft-scenebox{flex:0 1 auto;min-width:0;height:100%;aspect-ratio:680 / 520}
  /* the plot numbers on the ground would be 4px high here */
  .fb-scene .fb-lb{display:none}
  /* Every piece of the frame is restyled each time the clock moves, and 60
     times a second that keeps a cheap phone busy doing nothing else. So here
     the clock moves 30 times a second, and it stops while a field has the
     keyboard, so typing a PIN never waits on the picture. */
  html.ft-go .auth-side{animation-timing-function:steps(780)}
  .auth-page:has(.auth input:focus) .auth-side{animation-play-state:paused}

  /* The back button leads, with the kind of sign-in beside it; anything the
     app has to say (a wrong PIN) comes under that row, never under the button. */
  .authback{position:static;order:-2;flex:none;margin:0 0 18px}
  .authback ~ .roletag{order:-1;margin:-52px 0 21px 50px}
  /* 16px, or an iPhone zooms the page in on the field when it is tapped */
  .authfield input{font-size:16px}
}

/* ---- a computer: side by side -------------------------------------------- */
@media (min-width:1024px){
  .auth-page{display:grid;grid-template-columns:minmax(0,1fr) clamp(420px,36vw,540px)}
  .auth-page > .auth{grid-column:2;grid-row:1}
  .auth-page > .auth-side{grid-column:1;grid-row:1}

  .auth-panel{max-width:none;margin:0;justify-content:center;padding:64px max(44px,calc((100% - 392px) / 2)) 96px}
  .auth .spacer{display:none}
  .auth-panel > .legal:last-child{position:absolute;left:0;right:0;bottom:30px;margin:0}
  .auth h1{font-size:34px;margin-bottom:12px}
  .auth .lede{margin-bottom:28px}
  .auth h1 + .roles,.auth h1 + form{margin-top:22px}
  .authback{top:28px;left:28px}

  .auth-side{flex-direction:column;gap:8px;min-height:100dvh;padding:36px clamp(32px,3.2vw,56px)}
  /* the drawing runs up behind the logo, so the frame can be big */
  .ft-scenebox{flex:1 1 auto;min-height:280px;margin-top:-72px}
}

/* ---- the drawing (only ever shown inside .auth-side) --------------------- */
.fb-scene .fb-gd{fill:none;stroke:#fff;stroke-opacity:.055;stroke-width:1}
.fb-scene .fb-s1{fill:#0B3D28}
.fb-scene .fb-s2{fill:#062A1B}
.fb-scene .fb-s3{fill:#124C32;stroke:#A9CFBC;stroke-opacity:.4;stroke-width:1}
.fb-scene .fb-q{opacity:.5}
.fb-scene .fb-nl{fill:none;stroke:#A9CFBC;stroke-opacity:.22;stroke-width:1.1;stroke-linejoin:round}
.fb-scene .fb-lb{font:600 11px var(--fb);fill:#8FBFA6;letter-spacing:.3px}
.fb-scene .fb-tm,.fb-scene .fb-tm2{fill:none;stroke:var(--ft-tm);stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.fb-scene .fb-tm2{stroke-opacity:.7}
.fb-scene .fb-wb{stroke-width:1.2;stroke-opacity:.42}
.fb-scene .fb-far{opacity:.3}
.fb-scene .fb-pf{fill:var(--ft-tm);fill-opacity:.075}
.fb-scene .fb-pf2{fill-opacity:.04}
.fb-scene .fb-dk{fill:var(--ft-tm);fill-opacity:.06}
.fb-scene .fb-sl{fill:none;stroke:#fff;stroke-opacity:.6;stroke-width:1.1;stroke-linejoin:round}
.fb-scene .fb-hk{fill:#fff}
.fb-scene .fb-bld{opacity:var(--ft-f)}

/* a panel or a truss, lowered in on its slings */
.fb-scene .fb-dp{--ft-p:clamp(0,calc((var(--ft-t) - var(--ft-s)) * 29),1);opacity:calc(var(--ft-p) * 3);
  transform:translateY(calc((1 - var(--ft-p)) * (1 - var(--ft-p)) * -170px))}
.fb-scene .fb-dp .fb-sl,.fb-scene .fb-dp .fb-hk{--ft-q:clamp(0,calc((var(--ft-t) - var(--ft-s) - .037) * 40),1);
  opacity:calc(1 - var(--ft-q));transform:translateY(calc(var(--ft-q) * -110px))}
/* a plate, the rim, a joist or the ridge, drawn along its length */
.fb-scene .fb-dr{--ft-p:clamp(0,calc((var(--ft-t) - var(--ft-s)) * var(--ft-k)),1);
  stroke-dasharray:var(--ft-l);stroke-dashoffset:calc(var(--ft-l) * (1 - var(--ft-p)))}
.fb-scene .fb-jo{--ft-k:74;--ft-l:229}
.fb-scene .fb-fd{--ft-p:clamp(0,calc((var(--ft-t) - var(--ft-s)) * 40),1);opacity:calc(var(--ft-p) * .999)}
/* signed off: a light runs across the frame in the order it went up */
.fb-scene .fb-sh{--ft-g:clamp(0,min(calc((var(--ft-t) - .682 - var(--ft-s) * .1) * 70),calc((.71 + var(--ft-s) * .1 - var(--ft-t)) * 70)),1)}
.fb-scene .fb-sh .fb-tm,.fb-scene .fb-sh .fb-tm2{stroke:color-mix(in srgb,#fff calc(var(--ft-g) * 90%),var(--ft-tm))}
.fb-scene .fb-stp{--ft-p:clamp(0,calc((var(--ft-t) - var(--ft-s)) * 70),1);opacity:calc(var(--ft-p) * var(--ft-f));
  transform-box:fill-box;transform-origin:center;transform:scale(calc(.5 + var(--ft-p) * (.8 - .3 * var(--ft-p))))}
.fb-scene .fb-st0{fill:#068943;fill-opacity:.28}
.fb-scene .fb-st1{fill:#068943;stroke:#fff;stroke-width:1.6}
.fb-scene .fb-st2{fill:none;stroke:#fff;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}

/* ---- One fire-stopping report for semis or a block (Lee, 6 Oct 2026: "they
   can choose 2 or 3 plots at the same time") ---- */
/* Which plots, on the crew's screen: in the dark header, large, with a white
   button. (The first version was a pale strip under the header; Lee, 6 Oct:
   "isnt that obvious … wrapping the text so it hidden and hard to spot".) */
.fswwho{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px 12px;margin-bottom:14px;padding-bottom:14px;border-bottom:1px solid rgba(255,255,255,.13)}
.fswwhotext span{display:block;font:600 12.5px var(--fb);color:#93D9B2}
.fswwhotext b{display:block;margin-top:3px;font:600 23px/1.14 var(--fd);letter-spacing:-.035em;color:#fff}
.fswwho.several .fswwhotext b{font-size:20px}
body.app a.fswadd{display:inline-flex;align-items:center;gap:6px;min-height:46px;padding:0 17px 0 13px;border-radius:var(--r-ctl);background:#fff;color:var(--forest);font:600 15px var(--fb);text-decoration:none;white-space:nowrap;box-shadow:var(--lift-2);transition:transform .09s,background .15s}
body.app a.fswadd:hover{background:#E9F7EF}
body.app a.fswadd:active{transform:scale(.96)}
.fswadd svg{width:19px;height:19px;stroke-width:2.5;color:var(--signal)}
.fswwhy{grid-column:1 / -1;margin:0;font-size:12.5px;line-height:1.4;color:#B9D8C7}
/* In between the first photo and the last step the camera gets the room; the
   line under it still names the plots. */
.fswjs:not(.at-start):not(.at-summary) .fswwho{display:none}

/* The same on the Staff report: a card of its own under the hero. */
.fsplotcard{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin:12px 0 0;padding:14px 16px;background:var(--card);border:1.5px solid var(--signal-mid);border-radius:var(--r-card);box-shadow:var(--lift-1, 0 1px 2px rgba(6,24,15,.05))}
.fsplotico{flex:none;display:grid;place-items:center;width:42px;height:42px;border-radius:12px;background:var(--signal-lo);color:var(--signal)}
.fsplotico svg{width:23px;height:23px}
.fsplottext{flex:1 1 240px;min-width:0}
.fsplotk{display:block;font:600 12.5px var(--fb);color:var(--muted)}
.fsplotnums{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.fsplotnums b{font:600 14.5px var(--fd);letter-spacing:-.01em;color:#fff;background:var(--forest);border-radius:9px;padding:6px 11px;white-space:nowrap}
.fsplottext em{display:block;font-style:normal;font-size:12.5px;line-height:1.4;color:var(--muted);margin-top:7px}
body.app a.fsplotgo{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:7px;min-height:46px;padding:0 20px 0 16px;border-radius:var(--r-ctl);background:var(--signal);color:#fff;font:600 14.5px var(--fb);text-decoration:none;white-space:nowrap;box-shadow:none;transition:transform .09s,background .15s}
body.app a.fsplotgo:hover{background:#057A3C}
body.app a.fsplotgo:active{transform:scale(.97)}
.fsplotgo svg{width:18px;height:18px;stroke-width:2.5}
.fsplotcard.several a.fsplotgo{background:var(--card);color:var(--forest);border:1px solid var(--line);box-shadow:none;padding:0 18px}
body.app .fsplotcard.several a.fsplotgo:hover{background:var(--paper);border-color:var(--signal-mid)}
@media (max-width:520px){body.app a.fsplotgo{flex:1 1 100%}}
.fsfactsub{display:block;margin-top:2px;font:500 12.5px var(--fb);color:var(--muted)}

/* The picker. */
.fsphead h2{margin:14px 0 0;font:600 24px/1.15 var(--fd);letter-spacing:-.035em;color:#fff}
.fsphead h2 span{color:#8FE3B7}
.fsphead p{margin:8px 0 2px;font-size:13.5px;line-height:1.5;color:#B9D8C7;max-width:44ch}
.fspbody{padding:18px 18px 6px}
body.app .fsplabel{margin:0 0 9px;font:600 12.5px var(--fb);color:var(--muted)}
.fsplist{display:grid;grid-template-columns:minmax(0,1fr);gap:8px}
.fsprow{position:relative;display:flex;align-items:center;gap:13px;min-height:68px;padding:9px 14px 9px 9px;background:var(--card);border:1.5px solid var(--line);border-radius:15px;cursor:pointer;transition:border-color .15s,background .15s,transform .09s,opacity .15s;-webkit-tap-highlight-color:transparent}
.fsprow:active{transform:scale(.985)}
.fsprow input{position:absolute;inset:0;width:100%;height:100%;margin:0;opacity:0;cursor:pointer}
.fsprow input:disabled{cursor:default}
.fspnum{flex:none;display:flex;flex-direction:column;align-items:center;justify-content:center;width:58px;height:50px;border-radius:11px;background:var(--paper);color:var(--forest);transition:background .15s,color .15s}
.fspnum i{font-style:normal;font:600 12.5px var(--fb);color:var(--muted);transition:color .15s}
.fspnum b{font:600 19px/1.1 var(--fd);letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.fspinfo{flex:1;min-width:0}
.fspinfo b{display:block;font:600 14.5px/1.3 var(--fb);color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fspinfo i{display:block;font-style:normal;font-size:12.5px;color:var(--muted);margin-top:1px}
.fspbox{flex:none;display:grid;place-items:center;width:28px;height:28px;border-radius:50%;border:2px solid #C9D5CD;color:transparent;transition:background .15s,border-color .15s,color .15s,transform .2s cubic-bezier(.3,1.6,.5,1)}
.fspbox svg{width:16px;height:16px;stroke-width:2.6}
.fsprow:hover{border-color:var(--signal-mid)}
.fsprow:has(input:checked){border-color:var(--signal);background:var(--signal-lo)}
.fsprow:has(input:checked) .fspnum{background:var(--forest);color:#fff}
.fsprow:has(input:checked) .fspnum i{color:#8FE3B7}
.fsprow:has(input:checked) .fspbox{background:var(--signal);border-color:var(--signal);color:#fff;transform:scale(1.06)}
.fsprow:has(input:focus-visible){outline:2px solid var(--forest);outline-offset:2px}
.fsprow.is-lead{cursor:default}
.fsprow.is-lead:active{transform:none}
.fsprow.is-blocked{cursor:default;background:var(--paper);border-style:dashed}
.fsprow.is-blocked:hover{border-color:var(--line)}
.fsprow.is-blocked:active{transform:none}
.fsprow.is-blocked .fspnum{background:var(--card);color:var(--muted)}
.fsprow.is-blocked .fspinfo b{color:var(--muted);white-space:normal;font-size:13.5px}
.fsprow.is-blocked .fspbox{border-style:dashed;border-color:#D5DDD7}
.fsprow.is-shut{opacity:.42;cursor:default}
.fsprow.is-shut:hover{border-color:var(--line)}
.fspmore{margin-top:12px;border-top:1px solid var(--line-2)}
.fspmore summary{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:14px 2px;cursor:pointer;font:600 13.5px var(--fb);color:var(--forest);list-style:none}
.fspmore summary::-webkit-details-marker{display:none}
.fspmore summary em{font-style:normal;font:600 12px var(--fb);color:var(--muted);background:var(--paper);border-radius:var(--r-tag);padding:3px 10px}
.fspmore summary span:after{content:"";display:inline-block;width:7px;height:7px;margin-left:9px;border:solid var(--forest);border-width:0 2px 2px 0;transform:translateY(-3px) rotate(45deg);transition:transform .2s}
.fspmore[open] summary span:after{transform:translateY(1px) rotate(-135deg)}
.fspmore .fsplist{padding-bottom:6px}
.fspfull{margin:14px 0 0;padding:10px 13px;border-radius:12px;background:var(--amber-lo);border:1px solid #F0E2C4;font:600 12.5px/1.45 var(--fb);color:#7A5510}
.fspnote{margin:16px 0 0;text-align:center;font-size:12px;line-height:1.45;color:var(--muted)}
.fspsave{margin-top:4px;padding-bottom:14px}

/* ---- A different crane turned up (Lee, 6 Oct 2026: "they can change the
   crane type from say a 35t to a 55t") ---- */
/* The strip at the top of the on-the-day screen. */
body.app a.swstrip{display:flex;align-items:center;gap:12px;margin:14px 0 4px;padding:12px 14px;border-radius:var(--r-card);background:var(--card);border:1px solid var(--line);color:var(--text);text-decoration:none;transition:border-color .15s,box-shadow .15s}
body.app a.swstrip:hover{border-color:var(--signal-mid);box-shadow:var(--lift-2)}
.swstrip svg{width:24px;height:24px;flex:none;color:var(--forest-3)}
.swstriptext{flex:1;min-width:0}
.swstriptext b{display:block;font:600 14.5px/1.25 var(--fb)}
.swstriptext i{display:block;font-style:normal;font-size:12.5px;line-height:1.4;color:var(--muted);margin-top:1px}
.swstrip em{flex:none;font-style:normal;font:600 12.5px var(--fb);color:var(--forest);background:var(--paper);border:1px solid var(--line);border-radius:var(--r-tag);padding:7px 13px}
body.app a.daylink{display:block;margin-top:12px;text-align:center;font:600 13px var(--fb);color:var(--forest-3);text-decoration:none}
body.app a.daylink:hover{text-decoration:underline}
.swnotice strong{font-weight:600}
.swnotice a{color:inherit;font-weight:600}

/* The screen itself. */
body.app .card.swnow{background:var(--paper);border-style:dashed}
.swnowtop{display:flex;align-items:center;justify-content:space-between;gap:10px}
.swfigs{display:grid;gap:7px;margin-top:13px;padding-top:12px;border-top:1px solid var(--line)}
.swfig{display:grid;grid-template-columns:118px minmax(0,1fr);gap:10px;align-items:baseline}
.swfig i{font-style:normal;font:600 12.5px var(--fb);color:var(--muted)}
.swfig b{font:600 13px/1.4 var(--fb);color:var(--text);font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
@media (max-width:420px){.swfig{grid-template-columns:1fr;gap:1px}}
.crane:has(input:checked) .swfigs{border-top-color:#BFE0CE}
.crane:has(input:disabled){cursor:default}
.crane:has(input:disabled) input{cursor:default}
.crane:has(input:disabled):hover{border-color:var(--line)}
.swpick .crane{padding-right:52px}
.swpick .crane:not(.unset):after{content:"";position:absolute;top:16px;right:16px;width:24px;height:24px;border-radius:50%;border:2px solid #C9D5CD;background:var(--card) center/14px no-repeat;transition:background-color .15s,border-color .15s}
.swpick .crane:has(input:checked):after{border-color:var(--signal);background-color:var(--signal);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.6 12.6l4.2 4.2 8.6-9.4'/%3E%3C/svg%3E")}
.swshort{display:flex;gap:9px;align-items:flex-start;margin-top:12px;padding:10px 12px;border-radius:12px;background:var(--rust-lo);border:1px solid #EBD3CB;color:#8A331E;font-size:12.5px;line-height:1.45}
.swshort svg{width:18px;height:18px;flex:none;margin-top:1px}
.swshort b{display:block;font-weight:600}
body.app .card.swwhat{margin-top:14px}
body.app .swwhat h2{margin:0 0 10px;font:600 15px var(--fd);letter-spacing:-.02em}
.swwhat ul{margin:0;padding:0;list-style:none;display:grid;gap:9px}
.swwhat li{position:relative;padding-left:18px;font-size:13.5px;line-height:1.5;color:var(--muted)}
.swwhat li:before{content:"";position:absolute;left:2px;top:.62em;width:6px;height:6px;border-radius:50%;background:var(--signal-mid)}
.swwhat li b{color:var(--text);font-weight:600}
.swkept{color:var(--amber) !important;margin-top:10px}

.howith{font-weight:600;color:var(--forest-3)}

/* ---- Which clients' sites do fire-stopping handovers (Paul via Lee, 6 Oct
   2026: "only the TW sites … for now, Donaldsons do their own") ---- */
.fsclients{padding:0;overflow:hidden;margin-bottom:22px}
.fsclient{display:flex;align-items:center;gap:13px;flex-wrap:wrap;padding:14px 16px;border-bottom:1px solid var(--line-2)}
.fsclient:last-child{border-bottom:none}
.fsclientmark{flex:none;display:grid;place-items:center;width:34px;height:34px;border-radius:50%;background:var(--paper);color:var(--steel)}
.fsclientmark svg{width:18px;height:18px;stroke-width:2.4}
.fsclient.on .fsclientmark{background:var(--signal);color:#fff}
.fsclienttext{flex:1 1 220px;min-width:0}
.fsclienttext b{display:block;font:600 15px var(--fd);letter-spacing:-.015em;color:var(--text)}
.fsclienttext span{display:block;font-size:12.5px;line-height:1.45;color:var(--muted);margin-top:2px}
.fsclienttext em{display:block;font-style:normal;font-size:12.5px;line-height:1.45;color:var(--amber);margin-top:4px}
.fsclient:not(.on) .fsclienttext b{color:var(--muted)}
body.app a.fsclientgo{flex:none;display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:0 16px;border-radius:var(--r-ctl);border:1px solid var(--line);background:var(--card);color:var(--forest);font:600 13.5px var(--fb);text-decoration:none;white-space:nowrap;transition:border-color .15s,background .15s}
body.app a.fsclientgo:hover{border-color:var(--signal-mid);background:var(--paper)}
body.app a.fsclientgo.go{background:var(--signal);border-color:var(--signal);color:#fff}
body.app a.fsclientgo.go:hover{background:#057A3C}
.dsbar.off{opacity:.45}


/* ==========================================================================
   The happy medium (7 Oct 2026)

   The rules above are the original with its gradients, coloured glows, pills
   and tracked capitals taken off. Everything else is as it was. What is here
   is the small number of things that needed a decision rather than a rule.
   ========================================================================== */

/* The app's own values, on `body.app` so the sign-in screens keep theirs. */
body.app{
  --r-surface:16px;
  --r-card:12px;
  --r-ctl:10px;
  --r-tag:6px;
}

/* a tag says its status in its colour and its word; the dot said it a third time */
.chip::before,.windchip::before,.sitechip::before,.sigsrc::before{display:none}
.chip{border-radius:var(--r-tag)}

/* ==========================================================================
   The Staff fire-stopping report, laid out again

   Lee, 7 Oct 2026, with a screenshot of it on his computer: "it looks very
   busy on desktop, is there a better way to lay this out".

   What made it busy: five bands stacked above the first checklist line (the
   dark panel, the plots card, the row of tools, the Next strip, the checklist
   heading), four buttons of equal weight competing (Add plots, Go to the
   checklist, Approve the rest, Save), and the progress said three times (the
   figures in the panel, the Next strip, Still to do on the right).

   Now, where there is room for the right-hand column:
     * one panel at the top holds the title, the tools and the plots
     * progress is said once, in Still to do, which marks the next step
     * the checklist starts straight under the panel, each line on two rows
     * Save is the one green button; the others are quiet
   On a phone the screen is as it was (the Next strip was added there because
   the checklist was two screens down, and it still is).
   ========================================================================== */

/* ---- one panel: title, tools, plots (every width) ------------------------ */
.fstop{position:relative;display:flex;flex-wrap:wrap;align-items:flex-start;gap:14px 20px;
  background:var(--forest);color:#fff;border-radius:var(--r-surface);padding:20px 22px 0;
  margin-bottom:16px;overflow:hidden}
.fstop .fshero{order:1;position:static;flex:1 1 300px;min-width:0;background:none;box-shadow:none;border-radius:0;
  padding:0;margin:0;overflow:visible}
.fstop .fshero::after{display:none}
.fstop .fshero b{font-size:24px;margin:4px 0 5px}
.fstop .fstools{order:2;flex:0 1 auto;display:flex;flex-wrap:wrap;gap:6px;margin:0;justify-content:flex-end}
body.app .fstop a.fstool{flex-direction:row;min-height:0;padding:8px 12px;gap:7px;font-size:13px;
  background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.16);color:#fff;box-shadow:none;
  border-radius:var(--r-ctl)}
body.app .fstop a.fstool:hover{background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.3)}
body.app .fstop a.fstool svg{width:16px;height:16px;color:#8FE3B7}

.fstop .fsplotcard{order:3;flex:1 0 100%;display:flex;flex-wrap:nowrap;align-items:center;gap:12px 16px;margin:2px -22px 0;
  padding:13px 22px 15px;background:rgba(0,0,0,.16);border:0;border-top:1px solid rgba(255,255,255,.1);
  border-radius:0;box-shadow:none}
.fstop .fsplotico{display:none}
.fstop .fsplottext{flex:1 1 auto;display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px;min-width:0}
.fstop .fsplotk{color:#A6CDB8;font-size:13px;font-weight:500}
.fstop .fsplotnums{margin:0}
.fstop .fsplotnums b{background:rgba(255,255,255,.14);color:#fff}
.fstop .fsplottext em{margin:0;color:#A6CDB8;font-size:13px}
body.app .fstop a.fsplotgo{flex:none;min-height:38px;padding:0 15px 0 11px;background:#fff;color:var(--forest);box-shadow:none}
body.app .fstop a.fsplotgo:hover{background:#E9F7EF}
.fstop .fsplotgo svg{color:var(--signal)}
body.app .fstop .fsplotcard.several a.fsplotgo{background:transparent;color:#fff;border:1px solid rgba(255,255,255,.3);padding:0 15px}
body.app .fstop .fsplotcard.several a.fsplotgo:hover{background:rgba(255,255,255,.12)}
.fstop .fsheroprog{height:3px}
/* a report that is signed off has no plots row: the panel needs its own foot */
.fstop:not(:has(.fsplotcard)){padding-bottom:20px}

/* on a phone: the five tools in one row across the panel, as they were under it */
@media (max-width:699px){
  .fstop{padding:18px 16px 0;gap:14px}
  .fstop .fshero b{font-size:22px}
  .fstop .fstools{flex:1 0 100%;display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:6px}
  body.app .fstop a.fstool{flex-direction:column;min-height:56px;padding:8px 2px;font-size:11px;line-height:1.2;gap:5px}
  .fstop .fsplotcard{margin:0 -16px;padding:12px 16px 14px;align-items:flex-start}
  body.app .fstop a.fsplotgo{min-height:42px}
  .fstop:not(:has(.fsplotcard)){padding-bottom:16px}
}

@media (min-width:1180px){
  /* ---- progress once: Still to do says it, and marks what is next ------ */
  .fstop .hofacts{display:none}
  .fsmain > .fsnext{display:none}
  .fssteps li:not(.done) a{position:relative}
  .fssteps li:not(.done) b::after{content:"Next";margin-left:8px;padding:2px 7px;border-radius:var(--r-tag);
    background:var(--signal);color:#fff;font:600 11px var(--fb);vertical-align:1px}
  .fssteps li:not(.done) ~ li:not(.done) b::after{display:none}

  /* ---- the checklist heading: a title and a quiet button --------------- */
  .fsmain .fschkhead{margin-top:2px}
  .fsmain .fschkhead .sectionlabel{margin:6px 0}
  .fsmain .fssub{margin:0 0 10px !important;font-size:13px}

  /* ---- signatures: one card, two lines ------------------------------- */
  .fsrail .fssigs{gap:0;border:1px solid var(--line);border-radius:var(--r-card);background:var(--card);
    box-shadow:var(--lift-1);overflow:hidden}
  .fsrail .fssig{border:0;border-radius:0;box-shadow:none;background:none;padding:13px 15px;
    display:grid;grid-template-columns:minmax(0,1fr) auto;gap:2px 12px;justify-items:start;align-items:center}
  .fsrail .fssig + .fssig{border-top:1px solid var(--line-2)}
  .fsrail .fssig .hosigk{grid-column:1 / -1;margin:0}
  .fsrail .fssig .fssigempty{padding:0;width:auto}
  .fsrail .fssig .fssigempty svg{display:none}
  .fsrail .fssig .btn.sm{margin:0;grid-column:2;grid-row:2;padding:7px 14px}
  .fsrail .fssig.signed{grid-template-columns:minmax(0,1fr)}
  .fsrail .fssig.signed img{height:44px;margin:2px 0 4px}
}

/* Approve the rest: a quiet button. Save is the green one. */
body.app .fsallfit{background:var(--card);color:var(--forest);border:1px solid var(--line);box-shadow:none;padding:8px 14px}
body.app .fsallfit svg{color:var(--signal)}
body.app .fsallfit:hover{border-color:var(--signal-mid);box-shadow:none}
body.app .fsallfit:disabled{border-color:transparent}
body.app .fsallfit{margin:4px 0 8px}
@media (min-width:1180px){body.app .fsallfit{margin:0}}

/* Approved / Outstanding / N/A: one joined control. Fifteen tinted buttons down
   the page was most of the noise; now only the answer that is chosen has colour. */
@media (min-width:700px){
  .fsmain .hoseg.fsseg{gap:0;border:1px solid var(--line);border-radius:var(--r-ctl);overflow:hidden;background:var(--card)}
  .fsmain .hoseg.fsseg label + label{border-left:1px solid var(--line)}
  .fsmain .hoseg.fsseg i{min-height:38px;padding:0 13px;border:0;border-radius:0;box-shadow:none}
  .fsmain .hoseg.fsseg .ok i,.fsmain .hoseg.fsseg .go i,.fsmain .hoseg.fsseg .na i{background:var(--card);color:var(--text)}
  .fsmain .hoseg.fsseg .ok i svg{color:var(--signal)}
  .fsmain .hoseg.fsseg .go i svg{color:var(--amber)}
  .fsmain .hoseg.fsseg .na i svg{color:var(--steel)}
  .fsmain .hoseg.fsseg label:hover i{background:var(--paper);box-shadow:none}
  .fsmain .hoseg.fsseg .ok input:checked + i{background:var(--signal);color:#fff}
  .fsmain .hoseg.fsseg .go input:checked + i{background:var(--amber);color:#fff}
  .fsmain .hoseg.fsseg .na input:checked + i{background:#6F8076;color:#fff}
  .fsmain .hoseg.fsseg input:checked + i svg{color:#fff}
}

@media (min-width:1180px){
  /* Save: a bar along the foot of its column, not a button floating over the form */
  .fsmain .stickysave{background:var(--paper);border-top:1px solid var(--line);padding:12px 0;margin-top:12px;
    display:flex;justify-content:flex-end}
  .fsmain .stickysave .btn{width:auto;max-width:none;margin:0;padding:12px 28px;box-shadow:none}
}

/* a very wide screen: each checklist line's five boxes fit on one row, and the
   names of the boxes are said once, over the first line, like a table's headings */
@media (min-width:1500px){
  .fsmain .fsfields{grid-template-columns:minmax(0,1.5fr) minmax(0,.7fr) minmax(0,1.2fr) minmax(0,.6fr) minmax(0,2fr)}
  .fsmain .fsfields label.wide{grid-column:auto}
  .fsmain .fsrow + .fsrow .fsfields span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
  .fsmain .fsrow{padding-top:12px;padding-bottom:13px}
  .fsmain .fsrowtop{margin-bottom:9px}
}

/* ==========================================================================
   Other screens on a computer

   Lee, 7 Oct 2026: "Please check for any other layout improvements on other
   screens if you feel they can be made better." These are the ones a wide
   screen was showing up. All of them start at desktop width; on a phone every
   one of these screens is as it was.

   `body[data-page]` is the screen's name (controller.action), put on the body
   by the layout so a screen can be laid out without touching its neighbours.
   ========================================================================== */

/* ---- long lists of short rows: columns, not one 1200px-wide row each ----- */
@media (min-width:1180px){
  body[data-page="handovers.index"] .card.plist,
  body[data-page="people.index"] .card.plist{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:0}
  body[data-page="handovers.index"] .card.plist > a.prow,
  body[data-page="people.index"] .card.plist > a.prow{border-bottom:1px solid var(--line-2);border-right:1px solid var(--line-2)}
}
@media (min-width:1500px){
  body[data-page="handovers.index"] .card.plist{grid-template-columns:repeat(3,minmax(0,1fr))}
  body[data-page="people.index"] .card.plist{grid-template-columns:repeat(3,minmax(0,1fr))}
}

/* ---- a single form: a column you can read, not boxes 1200px wide --------- */
@media (min-width:960px){
  body[data-page="people.add"] .pad,body[data-page="people.edit"] .pad,body[data-page="people.photo"] .pad,
  body[data-page="people.signature"] .pad,body[data-page="people.card"] .pad,
  body[data-page="leave.add"] .pad,body[data-page="safety.add"] .pad,
  body[data-page="lift-plans.sign"] .pad,body[data-page="handovers.sign"] .pad,
  body[data-page="drawings.add"] .pad,body[data-page="teams.add"] .pad,body[data-page="teams.edit"] .pad,
  body[data-page="lift-plans.day"] .pad,body[data-page="lift-plans.swap-crane"] .pad,
  body[data-page="handovers.plots"] .pad{max-width:860px}
}

/* ---- a lift plan: what to do on the left, the paperwork on the right ----- */
/* Below that width the two wrappers are taken out of the layout and what is in
   them goes back in the order the screen has always had on a phone: warnings
   and wind, Print and Email, the on-the-day card, signatures, the rest. */
@media (max-width:1179px){
  .lpgrid{display:flex;flex-direction:column}
  .lpmain,.lpside{display:contents}
  .lpmain > *{order:1}
  .lpmain > .daycard,.lpmain > .daycard ~ *,.lpmain > .sectionlabel,.lpmain > .sectionlabel ~ *{order:3}
  .lpside > *{order:4}
  .lpside > .exports,.lpside > .emailcard{order:2}
  /* Margins that overlapped in the ordinary flow add up between flex items.
     These take the doubled part back off, so a phone shows the gaps it did. */
  .lpgrid{margin-top:-14px}
  .lpmain > .windfold:first-child{margin-top:14px}
  .lpmain > .windfold{margin-bottom:0}
  .lpside > .emailcard{margin-top:10px}
  body.app .lpmain > .daycard + .sectionlabel.manage{margin-top:14px}
  body.app .lpgrid:not(:has(.daycard)) .lpmain > .sectionlabel.manage{margin-top:26px}
  body.app .lpside > .sectionlabel.manage{margin-top:14px}
  body.app .lpside > .tiles + .sectionlabel.manage{margin-top:16px}
  body.app .lpside > .sub + .sectionlabel.manage{margin-top:10px}
}
@media (min-width:1180px){
  .lpgrid{display:grid;grid-template-columns:minmax(0,1fr) 330px;gap:0 22px;align-items:start}
  .lpmain,.lpside{min-width:0}
  .lpside{position:sticky;top:84px}
  .lpmain > :first-child,.lpside > :first-child{margin-top:0}
  .lpside .exports{display:grid;grid-template-columns:1fr;gap:8px;margin:0 0 14px}
  .lpside .exports > .exbtn{flex:none;justify-content:flex-start;padding:12px 14px}
  .lpside .emailcard{padding:16px}
  .lpside .emailcard .emrow{flex-direction:column}
  body.app .lpside .emailcard .embtn{width:100%;padding:12px}
  .lpside .tiles.two{grid-template-columns:1fr;max-width:none}
  body.app .lpside .list.plots{grid-template-columns:1fr}
  body.app .lpside .sectionlabel.manage{margin-top:20px;padding-top:18px}
  body.app .lpmain > .sectionlabel.manage:first-child{margin-top:0;padding-top:0;border-top:0}
}

/* ---- Reporting: the six tiles are white; the icon keeps the colour -------- */
.dkpi{background:var(--card);border-color:var(--line)}
.dkpil{color:var(--muted)}

/* the pale disc in the corner of the dark panels was decoration only */
.plothero::after,.hero::after,.balance::after,.hsboard::after,.phero::after,.lvhero::after{display:none}

/* a word for screen readers where the mark already says it */
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
