/* Forge Field — app shell stylesheet (REWRITE R1).
   Extracted mechanically from handoff-kit/mockups/forge-field-receipts.html <style>.
   Source revision ported: sha256 5e77171a46684c58e4c09eb753824a904c5ee3ccce50091772bf2284a66d61f5

   WHAT WAS DELIBERATELY LEFT BEHIND
   - The demo-backdrop token set. MASTER-HANDOFF section 2.2: those six tokens style the dark
     PAGE the mockup sits on, not the app. Everything inside .screen is LIGHT.
   - .head / .mk / h1 / .sub / .demo / .foot — demo page chrome.
   - .phone — the 398px device bezel. In the real app the screen IS the viewport.
   - .sbar and children — a FAKE iOS status bar + notch pill. The OS draws the real one; the
     space it occupied is now env(safe-area-inset-top) on .abar.

   WHAT WAS ADDED
   - --copper:#c9832f. The mockup uses var(--copper) at three sites and never defines it, so
     those three rendered with no color at all. A real defect, fixed on port.
   - The port-hardening block at the bottom, carried forward from the old public/styles.css.
     Those are paid-for Android bug fixes, not preferences. Do not delete them. */

:root{
  /* ---- app tokens (MASTER-HANDOFF section 2.2) — the screen is light, always ---- */
  --scr:#f3f5f7;      /* screen background */
  --card:#ffffff;
  --border:#e3e7ec;   /* hairlines */
  --strong:#cfd6dc;   /* emphasized dividers, input borders */
  --text:#1a2026;     /* primary ink */
  --tmut:#69727b;     /* secondary */
  --tfaint:#9aa2aa;   /* tertiary / placeholder */
  --brand:#2f6a3a;    /* OVRT green — app bar gradient origin */
  --lime:#7DBA3D;     /* primary action / positive */
  --copper:#c9832f;   /* materials, warnings, foreman accent */
  --amber:#b97f1c;    /* pending */
  --red:#c2412e;      /* OT, damage, deny */

  /* ---- RADIUS SCALE (2026-08-13, Wes: '9/5'). Four stops, and nothing between them. ----
     Before this the app carried 154 border-radius declarations across NINETEEN distinct
     values, ten of them between 5px and 16px — which is why nothing read as decided. The
     control stop is deliberately about half the card stop: a control sits INSIDE a card, and
     matching radii make the inner shape look rounder than the outer one.
     Pills (99px) and circles (50%) are shapes, not radii — they are not on this scale. */
  --r-micro:2px;   /* marks inside controls: bar caps, legend swatches */
  --r-ctrl:5px;    /* buttons, inputs, rows, chips — anything you touch */
  --r-card:9px;    /* cards and panels */
  --r-sheet:14px;  /* bottom sheets and other full-width surfaces */
  --blue:#3f6fae;     /* manager accent, fuel category */
  --font:'Segoe UI',-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;
}

/* This is a hand-tuned light theme. Declaring the scheme stops Samsung One UI dark mode and
   Chrome's "Darken websites" from algorithmically inverting it — an auto-darkened UI is
   materially harder to read at 6am in direct sun, which is the only lighting that matters. */
:root{color-scheme:light}

*{margin:0;box-sizing:border-box}

/* overscroll-behavior-y:none kills Chrome-for-Android's pull-to-refresh. Without it, a glove
   flicking the Home tab downward — or a time wheel hitting its scroll limit and chaining the
   gesture up to the document — RELOADS THE APP, taking any staged photo or half-filled receipt
   with it. Per-scroller `contain` rules live in the hardening block at the bottom. */
html{overscroll-behavior-y:none}
html,body{height:100%}
body{
  font-family:var(--font);
  background:var(--scr);
  color:var(--text);
  font-size:13px;
  /* dvh tracks Android's collapsing URL bar; the vh line above it is the fallback. 100vh
     resolves against the LARGE viewport, so the body sits ~56px taller than the screen — a
     constant slop-scroll, which is exactly the gesture that fires pull-to-refresh. */
  min-height:100vh;
  min-height:100dvh;
  overscroll-behavior-y:none;
  display:flex;
  flex-direction:column;
}

/* ---------- the screen ----------
   Was: .phone{width:398px} wrapping .screen{height:812px; border-radius:var(--r-sheet)}.
   Now: the screen IS the viewport. The proportions and the flex column are unchanged, and
   position:relative is KEPT — .nav, .toast, .sheetovl, .sysfull, #onbScr and #bootScr all
   position themselves absolutely against it, exactly as they did inside the bezel. */
.screen{
  position:relative;
  background:var(--scr);
  color:var(--text);
  overflow:hidden;
  flex:1;
  min-height:100vh;
  min-height:100dvh;
  display:flex;
  flex-direction:column;
}

/* ================= verbatim from the mockup, .abar through .aitem .pchip ================= */
  .abar{display:flex;align-items:center;gap:10px;padding:10px 16px 12px;background:linear-gradient(160deg,#2f6a3a,#24512c);color:#fff}
  /* THE MARK — his logo, no words (2026-08-15). It is an <img> now rather than an inline
     SVG, so the tinted rounded tile the old glyph sat in is gone with it: a logo does not
     wear a swatch behind it. Sized to the same 26px optical height so nothing else in the bar
     moved. object-fit:contain because the crop is near-square but not exactly. */
  .abar .bmk{width:28px;height:28px;flex:none;object-fit:contain;display:block}
  .abar .t{font-weight:800;font-size:15px;letter-spacing:.01em}
  .abar .s{font-size:10.5px;color:rgba(255,255,255,.75);font-weight:600;margin-top:1px}
  .avatar{margin-left:auto;width:34px;height:34px;border-radius:50%;background:var(--lime);color:#16240b;font-weight:800;font-size:12.5px;display:flex;align-items:center;justify-content:center;border:2px solid rgba(255,255,255,.5);cursor:pointer}
  .rolechip{font-size:9px;font-weight:800;background:rgba(255,255,255,.16);border-radius:var(--r-ctrl);padding:2px 6px;letter-spacing:.05em}
  /* THE GEAR — your account, from any screen. margin-left:auto pushes it hard right, where
     the avatar used to be, so the bar's shape is unchanged. 38px is above the gloved-thumb
     floor (test/android.cjs measured 44px as the floor for a primary control; this is chrome
     at the edge of the screen, where the thumb lands generously by construction) and the
     negative margin keeps it from adding height to the bar. */
  /* 56px, NOT 38 (eval 2026-08-15, `touch-target`, third occurrence and now a standing
     check). This is the round's "genuinely always reachable" control and it shipped as the
     SMALLEST tap target in the app bar. The two prior instances of this class — a 34px eye
     and a 44px #pwOpen — were both raised to 56, which is the standard this app already
     holds every other primary control to; a gloved thumb in a gravel lot does not care that
     the third one is chrome. The negative margins keep the bigger box from adding height to
     the bar or pushing the icon off the right edge: the button grows around the glyph, which
     stays 22px. */
  .gearbtn{margin-left:auto;flex:none;width:56px;height:56px;margin-right:-14px;margin-top:-9px;margin-bottom:-9px;
    border:0;background:none;color:rgba(255,255,255,.92);display:flex;align-items:center;justify-content:center;
    border-radius:50%;cursor:pointer;-webkit-tap-highlight-color:transparent}
  .gearbtn svg{width:22px;height:22px}
  .gearbtn:active{background:rgba(255,255,255,.16)}
  .content{flex:1;overflow-y:auto;padding:14px 14px 124px}
  .content::-webkit-scrollbar{display:none}

  /* in-app camera */
  .cam{position:relative;height:560px;border-radius:var(--r-card);overflow:hidden;background:radial-gradient(120% 90% at 50% 30%,#2b2e31,#141618 70%);display:flex;flex-direction:column}
  /* PERMISSION CARD ONLY. The stage is sized for a viewfinder that is not there: with .vf and
     .camrow correctly hidden, the fixed 560px left ~470px of black under the card -- a stage
     reserved for absent content, which is exactly what the .cam-shutter:disabled{display:none}
     rule further down already gets right. Height goes to content; nothing else moves. */
  .cam.permonly{height:auto}
  .cam .privacy{position:absolute;top:12px;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:6px;background:rgba(20,24,20,.78);border:1px solid rgba(125,186,61,.5);color:#cfe8b4;font-weight:700;font-size:10px;border-radius:99px;padding:6px 11px;white-space:nowrap;z-index:3}
  .cam .privacy svg{width:11px;height:11px;color:var(--lime)}
  .privx{border:0;background:none;color:#8fa07f;font-size:10px;font-weight:800;cursor:pointer;padding:0 0 0 3px;line-height:1}
  .camx{position:absolute;top:10px;left:10px;z-index:4;width:30px;height:30px;border-radius:99px;border:1px solid rgba(255,255,255,.2);background:rgba(20,24,20,.72);color:#dfe5da;font-size:13px;font-weight:800;cursor:pointer;display:flex;align-items:center;justify-content:center}
  .vf{flex:1;position:relative;margin:52px 40px 18px}
  .vf .guide{position:absolute;inset:0;border:2px dashed rgba(255,255,255,.55);border-radius:var(--r-card)}
  .vf .ck{position:absolute;width:22px;height:22px;border:3px solid var(--lime);z-index:2}
  .vf .ck.tl{top:-3px;left:-3px;border-right:0;border-bottom:0;border-radius:var(--r-ctrl) 0 0 0}
  .vf .ck.tr{top:-3px;right:-3px;border-left:0;border-bottom:0;border-radius:0 var(--r-ctrl) 0 0}
  .vf .ck.bl{bottom:-3px;left:-3px;border-right:0;border-top:0;border-radius:0 0 0 var(--r-ctrl)}
  .vf .ck.br{bottom:-3px;right:-3px;border-left:0;border-top:0;border-radius:0 0 var(--r-ctrl) 0}
  .vf .paper{position:absolute;left:16%;right:16%;top:9%;bottom:9%;background:#f4f2ec;border-radius:var(--r-ctrl);box-shadow:0 10px 30px rgba(0,0,0,.5);transform:rotate(-2deg)}
  .vf .paper i{position:absolute;left:12%;right:12%;height:3px;border-radius:var(--r-micro);background:#c9cdd2}
  .vf .hinttx{position:absolute;bottom:-30px;left:0;right:0;text-align:center;color:rgba(255,255,255,.75);font-size:11px;font-weight:600}
  .camrow{display:flex;align-items:center;justify-content:space-around;padding:16px 30px 22px}
  .flashbtn,.norollbtn{width:44px;height:44px;border-radius:50%;background:rgba(255,255,255,.12);border:0;color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer}
  .flashbtn svg,.norollbtn svg{width:20px;height:20px}
  .shutter{width:70px;height:70px;border-radius:50%;background:#fff;border:5px solid rgba(255,255,255,.35);cursor:pointer;position:relative}
  .shutter:active{transform:scale(.94)}
  .shutter::after{content:'';position:absolute;inset:6px;border-radius:50%;border:2.5px solid #16240b;background:var(--lime)}
  .reading{position:absolute;inset:0;background:rgba(13,15,14,.82);display:none;align-items:center;justify-content:center;flex-direction:column;gap:12px;z-index:5;color:#fff;font-weight:800}
  .reading.on{display:flex}
  .spin{width:34px;height:34px;border-radius:50%;border:3.5px solid rgba(125,186,61,.25);border-top-color:var(--lime);animation:sp 0.9s linear infinite}
  @keyframes sp{to{transform:rotate(360deg)}}
  .reading small{font-weight:600;color:rgba(255,255,255,.7);font-size:11px}

  /* capture screen */
  .snap{border:1.5px dashed var(--strong);border-radius:var(--r-card);background:var(--card);padding:12px;display:flex;gap:12px;align-items:center}
  .rimg{width:64px;height:84px;border-radius:var(--r-ctrl);background:#fff;border:1px solid var(--border);box-shadow:0 3px 10px rgba(20,30,25,.14);position:relative;overflow:hidden;flex:none}
  .rimg .ln{position:absolute;left:8px;right:8px;height:3px;border-radius:var(--r-micro);background:#d7dbe0}
  .rimg .tot{position:absolute;left:8px;right:8px;bottom:7px;height:5px;border-radius:var(--r-micro);background:#8a929a}
  .snap .t1{font-weight:800;font-size:13px;display:flex;align-items:center;gap:7px}
  .snap .ok{width:17px;height:17px;border-radius:50%;background:var(--lime);color:#fff;display:inline-flex;align-items:center;justify-content:center;flex:none}
  .snap .ok svg{width:9px;height:9px}
  .snap .t2{color:var(--tmut);font-size:11px;margin-top:3px;line-height:1.4}
  .relink{background:none;border:0;color:var(--blue);font:inherit;font-weight:700;font-size:11px;cursor:pointer;padding:0;margin-top:6px}
  label{display:block;font-size:10px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--tfaint);margin:14px 2px 6px}
  .ocr{font-size:9px;font-weight:800;color:var(--brand);background:rgba(125,186,61,.14);border:1px solid rgba(125,186,61,.45);border-radius:var(--r-ctrl);padding:1.5px 6px;margin-left:5px}
  .inp{width:100%;font:inherit;font-size:15px;border:1px solid var(--strong);border-radius:var(--r-card);padding:12px 13px;background:var(--card)}
  .inp:focus{outline:2px solid rgba(125,186,61,.4);border-color:var(--lime)}
  .g2{display:grid;grid-template-columns:1fr 1fr;gap:9px}
  .cats{display:flex;gap:7px;overflow-x:auto;padding-bottom:4px}
  .cats::-webkit-scrollbar{display:none}
  .cat{flex:none;border:1px solid var(--strong);background:var(--card);color:var(--tmut);font:inherit;font-weight:700;font-size:12.5px;padding:9px 15px;border-radius:99px;cursor:pointer}
  .cat.on{background:var(--brand);border-color:var(--brand);color:#fff}
  .submit{width:100%;margin-top:18px;background:var(--lime);border:0;color:#16240b;font:inherit;font-weight:800;font-size:15px;border-radius:var(--r-card);padding:15px;cursor:pointer;box-shadow:0 6px 18px rgba(125,186,61,.35)}
  .submit:active{transform:scale(.985)}
  .subnote{text-align:center;color:var(--tfaint);font-size:10.5px;margin-top:9px}

  /* costs screen */
  .jt{font-size:16px;font-weight:800}
  .jm{color:var(--tmut);font-size:11px;margin-top:2px}
  .kpis{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:11px}
  .kpi{border:1px solid var(--border);border-radius:var(--r-card);background:var(--card);padding:10px 12px}
  .kpi .k{color:var(--tfaint);font-size:9px;font-weight:800;letter-spacing:.06em;text-transform:uppercase}
  .kpi .v{font-size:18px;font-weight:800;margin-top:3px}
  .kpi .s{color:var(--tmut);font-size:9.5px;margin-top:1px}
  .kpi.warn .v{color:var(--amber)}
  .kpi.good .v{color:var(--brand)}
  .budget{border:1px solid var(--border);border-radius:var(--r-card);background:var(--card);padding:11px 13px;margin-top:9px}
  .budget .bt{display:flex;justify-content:space-between;font-weight:800;font-size:11.5px;margin-bottom:6px}
  .bbar{height:9px;border-radius:var(--r-ctrl);background:var(--scr);border:1px solid var(--border);overflow:hidden}
  .bbar b{display:block;height:100%;background:var(--lime)}
  .catbars{display:flex;gap:7px;margin-top:9px;overflow-x:auto;padding-bottom:2px}
  .catbars::-webkit-scrollbar{display:none}
  .cb{flex:none;min-width:120px;border:1px solid var(--border);border-radius:var(--r-card);background:var(--card);padding:8px 10px}
  .cb .ct{display:flex;justify-content:space-between;gap:8px;font-size:10.5px;font-weight:700}
  .cb .ct span:last-child{color:var(--tmut)}
  .cb .cl{height:4px;border-radius:var(--r-micro);background:var(--scr);margin-top:6px;overflow:hidden}
  .cb .cl b{display:block;height:100%}
  .exps h5{font-size:9.5px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:var(--tfaint);margin:14px 2px 8px}
  .exp{border:1px solid var(--border);border-radius:var(--r-card);background:var(--card);padding:10px 12px;margin-bottom:7px}
  .exp .top{display:flex;align-items:center;gap:9px}
  .exp .ric{width:30px;height:30px;border-radius:var(--r-ctrl);background:var(--scr);border:1px solid var(--border);display:flex;align-items:center;justify-content:center;flex:none;color:#5b636a}
  .exp .ric svg{width:15px;height:15px}
  .exp .vn{font-weight:800;font-size:13px}
  .exp .meta{color:var(--tmut);font-size:10px;margin-top:1px}
  .exp .amt{margin-left:auto;font-weight:800;font-size:14px}
  .exp .row2{display:flex;align-items:center;gap:6px;margin-top:8px}
  .cat2{font-size:9px;font-weight:800;border-radius:var(--r-ctrl);padding:2.5px 7px;border:1px solid var(--border);background:var(--scr);color:var(--tmut)}
  .stat{font-size:9px;font-weight:800;border-radius:var(--r-ctrl);padding:3px 8px}
  .st-ok{color:#2f6a3a;background:rgba(125,186,61,.13);border:1px solid rgba(125,186,61,.45)}
  .st-pend{color:var(--amber);background:rgba(185,127,28,.10);border:1px solid rgba(185,127,28,.4)}
  .act{margin-left:auto;display:flex;gap:6px}
  .abtn{border:1px solid var(--strong);background:var(--scr);font:inherit;font-weight:800;font-size:11px;border-radius:var(--r-ctrl);padding:7px 13px;cursor:pointer;color:var(--text)}
  .abtn.ok{background:var(--lime);border-color:var(--lime);color:#16240b}

  /* bottom nav */
  .nav{position:absolute;left:0;right:0;bottom:0;background:rgba(255,255,255,.96);backdrop-filter:blur(8px);border-top:1px solid var(--border);display:flex;align-items:flex-end;justify-content:space-around;padding:6px 10px 16px}
  .nv{display:flex;flex-direction:column;align-items:center;gap:3px;color:var(--tfaint);font-size:9px;font-weight:800;background:none;border:0;cursor:pointer;width:56px;position:relative}
  /* The active tab is marked by a hairline, not by colour alone — a second channel, same rule the week chart follows. */
  .nv.on::before{content:"";position:absolute;top:-6px;left:50%;transform:translateX(-50%);width:26px;height:2px;background:var(--brand)}
  .nv.plus.on::before{display:none}
  .nv svg{width:22px;height:22px}
  .nv.on{color:var(--brand)}
  .nv.plus{width:auto}
  /* The one circle in the app, and it stays one: it is the only control that is not a rectangle of content, which is what makes it read as an action rather than a destination. */
  .plusbtn{width:60px;height:60px;border-radius:50%;background:var(--lime);display:flex;align-items:center;justify-content:center;color:#16240b;box-shadow:0 10px 24px rgba(125,186,61,.5);margin-bottom:6px;border:0;cursor:pointer}
  .plusbtn svg{width:28px;height:28px}
  .nv.plus.on .plusbtn{outline:3px solid rgba(125,186,61,.35)}
  /* THE TOAST OUTRANKS THE TAKEOVERS (2026-08-16). It shipped at z-index:20 while #onbScr is
     55 with an OPAQUE background and .sysfull/#bootScr are 60 — so every toast fired while the
     onboarding screen is up rendered underneath it and was never seen by anybody. That is not
     a cosmetic miss: those are status sentences, and several of them are the only answer a
     person gets ("Your work email — the one your office invited", "No signal — the code can't
     be sent from here", both fired from step 1 in app.js requestCode). A message the code
     believes it showed and the screen never painted is the honesty class the ledger is full
     of, so the fix belongs on the component rather than on the one caller that noticed.

     58 — ABOVE #onbScr (55), BELOW #bootScr and .sysfull (both 60). It shipped at 62, which
     cleared all three, and that was one takeover too many: .sysfull is the forced-update screen
     and the "your access changed" screen, and #bootScr is the splash. Those two are deliberately
     blocking — they are the app saying "nothing else is happening until you deal with this" —
     and a status line floating over them is a second voice on a screen that is meant to have
     one. The onboarding screen is the case the raise was written for and 58 still covers it. */
  .toast{position:absolute;left:50%;bottom:104px;transform:translateX(-50%) translateY(14px);background:#1a2026;color:#fff;font-weight:700;font-size:12px;border-radius:var(--r-card);padding:10px 15px;opacity:0;pointer-events:none;transition:.25s;z-index:58;width:max-content;max-width:330px;text-align:center}
  .toast.on{opacity:1;transform:translateX(-50%) translateY(0)}

  /* THE IN-APP CAMERA (camera.js self-injects .cam-* markup into document.body).
     These rules did not exist from the R3 rewrite until 2026-08-17: the camera's styles
     lived with the retired owner.html and died with it while camera.js kept loading, so
     the viewfinder rendered as UNSTYLED divs — the video at intrinsic size squashed into
     the top-left of the page (found by Wes on real glass, day one at Luce Line; the
     receipts UI suite that would have caught it is the known unported gap).
     z-index 56: above the app and #onbScr (55), below .toast (58 — upload status must be
     readable over the viewfinder), below #bootScr/.sysfull (60 — takeovers outrank a tool).
     object-fit CONTAIN, not cover: the shutter draws the full frame (drawImage at
     videoWidth×videoHeight), so a cropped cover-preview would lie about what the photo
     captures. Letterboxing on black is the honest shape of the sensor. */
  .cam-overlay{position:fixed;inset:0;z-index:56;background:#000;display:flex;flex-direction:column}
  .cam-stage{position:relative;flex:1 1 auto;min-height:0;display:flex}
  .cam-video{width:100%;height:100%;object-fit:contain;background:#000}
  .cam-live{position:absolute;top:calc(14px + env(safe-area-inset-top));left:16px;display:flex;align-items:center;gap:7px;color:#fff;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;background:rgba(15,18,16,.55);border-radius:999px;padding:6px 12px}
  .cam-dot{width:8px;height:8px;border-radius:50%;background:#e5484d;animation:camPulse 1.6s infinite}
  @keyframes camPulse{0%,100%{opacity:1}50%{opacity:.35}}
  .cam-msg{position:absolute;inset:auto 16px;top:40%;background:#1a2026;color:#fff;border-radius:var(--r-card);padding:16px 18px;font-size:15px;line-height:1.45;text-align:center}
  .cam-msg[hidden]{display:none}
  .cam-controls{flex:none;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:18px 22px calc(18px + env(safe-area-inset-bottom));background:#000}
  .cam-controls button{-webkit-tap-highlight-color:transparent}
  .cam-lib,.cam-close{min-height:56px;min-width:84px;padding:12px 16px;border:0;border-radius:12px;background:rgba(255,255,255,.14);color:#fff;font-size:16px;font-weight:700}
  .cam-spacer{min-width:84px}
  .cam-shutter{width:76px;height:76px;border-radius:50%;border:5px solid #fff;background:transparent;padding:0;display:flex;align-items:center;justify-content:center}
  .cam-shutter span{width:56px;height:56px;border-radius:50%;background:#fff}
  .cam-shutter:active span{transform:scale(.88)}
  /* Denied/unsupported: the module disables the shutter while its message card explains.
     The house rule is absent-not-disabled — the ring leaves with the capability. */
  .cam-shutter:disabled{display:none}
  .cam-flash::after{content:"";position:fixed;inset:0;z-index:57;background:#fff;animation:camFlash .14s ease-out}
  @keyframes camFlash{from{opacity:.9}to{opacity:0}}

  /* capture hub sheet */
  .sheetovl{position:absolute;inset:0;background:rgba(15,18,16,.45);z-index:30;display:none;align-items:flex-end}
  .sheetovl.on{display:flex}
  /* A sheet opened FROM another sheet. Real build: keep a sheet stack instead of two levels.
     NOTE: `over` is also the spine's back-gesture selector — BACK_LAYERS maps
     `.sheetovl.on.over` to closeJobPicker() BY NAME (app.js §19). Only the job picker may
     wear it; anything else that needs height in the stack gets its own id rule below. */
  .sheetovl.over{z-index:31}
  /* THE JOB EDITOR, EXPLICITLY. It opens FROM the picker (its "+ New job" row) and must paint
     above it — but it must not wear `over`, or the back gesture would close the picker
     underneath and strand it (see the comment on #jobedovl in index.html). DOM order used to
     do this job by accident, which held only because the picker happens not to wear `over` on
     the one path that reaches this sheet. One id rule, above both 30 and 31, states it. */
  #jobedovl{z-index:32}
  .sheet{width:100%;background:#fff;border-radius:var(--r-sheet);padding:14px 14px 26px;animation:up .18s ease-out}
  @keyframes up{from{transform:translateY(30px);opacity:.6}to{transform:translateY(0);opacity:1}}
  .grab{width:40px;height:4px;border-radius:var(--r-micro);background:#d5dade;margin:0 auto 12px}
  .sheet h6{font-size:10px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--tfaint);margin:0 4px 9px}
  .srow2{display:flex;align-items:center;gap:12px;border:1px solid var(--border);border-radius:var(--r-card);background:var(--card);padding:13px 13px;margin-bottom:8px;cursor:pointer}
  .srow2:active{background:var(--scr)}
  .srow2 .sic{width:42px;height:42px;border-radius:var(--r-card);display:flex;align-items:center;justify-content:center;flex:none;color:#fff}
  .srow2 .sic svg{width:21px;height:21px}
  .srow2 .st1{font-weight:800;font-size:14px}
  .srow2 .st2{color:var(--tmut);font-size:10.5px;margin-top:1px;line-height:1.35}
  .srow2 .chev{margin-left:auto;color:var(--tfaint);font-size:18px}

  /* daily log + damage forms */
  textarea.inp{resize:none;min-height:88px;line-height:1.45;font-size:14px}
  .wchip{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--border);background:var(--card);border-radius:99px;padding:8px 13px;font-weight:700;font-size:12.5px}
  .wchip svg{width:16px;height:16px;color:var(--amber)}
  .wchip .auto2{font-size:9px;font-weight:800;color:var(--brand);background:rgba(125,186,61,.14);border-radius:var(--r-ctrl);padding:1.5px 6px;margin-left:2px}
  .thumbs{display:flex;gap:7px;flex-wrap:wrap}
  .th{width:58px;height:58px;border-radius:var(--r-card);background:linear-gradient(180deg,#9db4c4 0 38%,#7a9a58 38% 62%,#8a7a5e 62% 100%);border:1px solid var(--strong);position:relative;overflow:hidden}
  .th::after{content:'';position:absolute;left:12%;top:46%;width:34%;height:20%;background:#5d7442;border-radius:var(--r-micro);box-shadow:26px 6px 0 -2px #6b5a44}
  .th.dmg::before{content:'';position:absolute;right:4px;top:4px;width:12px;height:12px;border-radius:99px;background:#c2412e;border:2px solid #fff;z-index:2}
  .thadd{width:58px;height:58px;border-radius:var(--r-card);border:1.5px dashed var(--strong);background:var(--scr);color:var(--tmut);display:flex;align-items:center;justify-content:center;font-size:20px;font-weight:800;cursor:pointer}
  .tags{display:flex;gap:6px;flex-wrap:wrap}
  .tag{border:1px solid var(--strong);background:var(--card);color:var(--tmut);font:inherit;font-weight:700;font-size:11.5px;padding:8px 13px;border-radius:99px;cursor:pointer}
  .tag.on{background:#c2412e;border-color:#c2412e;color:#fff}
  .tag.on.pre{background:#3f6fae;border-color:#3f6fae}
  .tag.on.doc{background:#69727b;border-color:#69727b}

  /* jobs seg + activity */
  .seg2{display:flex;background:var(--card);border:1px solid var(--border);border-radius:var(--r-card);padding:3px;gap:3px;margin-top:11px}
  .seg2 button{flex:1;border:0;background:transparent;color:var(--tmut);font:inherit;font-weight:800;font-size:12px;padding:8px;border-radius:var(--r-ctrl);cursor:pointer}
  .seg2 button.on{background:var(--brand);color:#fff}
  .aitem{border:1px solid var(--border);border-radius:var(--r-card);background:var(--card);padding:11px 12px;margin-bottom:7px}
  .aitem .top{display:flex;align-items:center;gap:9px}
  .aitem .aic{width:30px;height:30px;border-radius:var(--r-ctrl);display:flex;align-items:center;justify-content:center;flex:none;color:#fff}
  .aitem .aic svg{width:15px;height:15px}
  .aitem .at{font-weight:800;font-size:12.5px}
  .aitem .am{color:var(--tmut);font-size:10px;margin-top:1px}
  .aitem .body{color:var(--text);font-size:11.5px;margin-top:8px;line-height:1.45}
  /* receipt index */
  .kpi.tap{cursor:pointer}
  .kpi.tap .s{color:var(--brand);font-weight:800}
  .rback{display:flex;align-items:center;gap:6px;border:0;background:none;color:var(--brand);font:inherit;font-weight:800;font-size:12px;cursor:pointer;padding:2px 0 10px}
  .alloc{border:1px solid var(--border);border-radius:var(--r-card);background:var(--card);padding:12px}
  .alloc .ah{font-size:9.5px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:var(--tfaint)}
  .alloc .bar{display:flex;height:12px;border-radius:99px;overflow:hidden;margin:9px 0 10px;background:var(--scr)}
  .alloc .bar i{display:block;height:100%}
  .alloc .leg{display:flex;flex-wrap:wrap;gap:6px}
  .lchip{font-size:10px;font-weight:800;border:1px solid var(--border);border-radius:99px;padding:4px 9px;display:inline-flex;gap:6px;align-items:center;color:var(--tmut)}
  .lchip .dot{width:7px;height:7px;border-radius:99px}
  .rfilters{display:flex;gap:6px;overflow-x:auto;margin:12px 0 2px}
  .rf{border:1px solid var(--border);background:var(--card);border-radius:99px;font:inherit;font-weight:800;font-size:11px;padding:7px 12px;cursor:pointer;color:var(--tmut);white-space:nowrap}
  .rf.on{background:#2f6a3a;border-color:#2f6a3a;color:#fff}
  .rsec{margin-top:13px}
  .rsec .rh{display:flex;align-items:center;gap:7px;font-size:9.5px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:var(--tfaint);padding:0 2px}
  .rsec .rh .dot{width:8px;height:8px;border-radius:99px}
  .rsec .rh .sum{margin-left:auto;color:var(--text);font-size:11.5px;letter-spacing:0}
  .rsec .rh .pd{color:var(--amber);font-size:9.5px;letter-spacing:0;margin-left:6px}
  .rrow{border:1px solid var(--border);background:var(--card);border-radius:var(--r-card);padding:9px 11px;margin-top:7px;cursor:pointer}
  .rrow .l1{display:flex;align-items:center;gap:10px}
  .rth{width:34px;height:44px;border-radius:var(--r-ctrl);background:#fff;border:1px solid var(--border);position:relative;overflow:hidden;flex:none}
  .rth i{position:absolute;left:5px;right:5px;height:2px;border-radius:var(--r-micro);background:#d7dbe0}
  .rth .tt{background:#8a929a;height:3px}
  .rrow .vn2{font-weight:800;font-size:12.5px}
  .rrow .m2{color:var(--tmut);font-size:10px;margin-top:1px}
  .rrow .amt2{margin-left:auto;text-align:right}
  .rrow .amt2 b{font-size:13px}
  .rrow .amt2 .st2c{display:block;font-size:8.5px;font-weight:800;margin-top:2px}
  .rxp{border-top:1px dashed var(--border);margin-top:9px;padding-top:10px;display:flex;gap:11px}
  .rxp .trail{font-size:10.5px;color:var(--tmut);line-height:1.5;min-width:0}
  .rxp .trail b{color:var(--text)}
  .rxp .tl2{display:flex;gap:7px;align-items:flex-start;margin-top:4px}
  .rxp .td2{width:6px;height:6px;border-radius:99px;background:var(--brand);margin-top:4px;flex:none}
  /* foreman hours pane */
  .hnote{color:var(--tmut);font-size:10.5px;margin-top:9px;line-height:1.45}
  .drow{display:flex;align-items:center;gap:9px;border:1px solid var(--border);background:var(--card);border-radius:var(--r-card);padding:9px 12px;margin-top:7px;font-size:12px}
  .drow .dd{font-weight:800}
  .drow .dh{margin-left:auto;font-weight:800}
  .drow .dw{color:var(--tmut);font-size:10.5px}
  /* settings */
  .pcard{display:flex;align-items:center;gap:12px;border:1px solid var(--border);background:var(--card);border-radius:var(--r-card);padding:13px 14px}
  .pcard .pav{width:44px;height:44px;border-radius:99px;background:#2f6a3a;color:#fff;font-weight:800;font-size:15px;display:flex;align-items:center;justify-content:center;flex:none}
  .pcard .pn{font-weight:800;font-size:13.5px}
  .pcard .pm{color:var(--tmut);font-size:10.5px;margin-top:1px}
  .pcard .rolechip2{margin-left:auto;font-size:9px;font-weight:800;background:rgba(47,106,58,.12);color:#2f6a3a;border-radius:var(--r-ctrl);padding:3px 8px;letter-spacing:.05em}
  .sh{font-size:9.5px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:var(--tfaint);margin:16px 2px 8px;display:flex;align-items:center;gap:7px}
  .sh .who{margin-left:auto;font-size:8.5px;background:var(--scr);border:1px solid var(--border);border-radius:var(--r-ctrl);padding:2.5px 7px;color:var(--tmut);letter-spacing:.04em}
  .trow{display:flex;align-items:center;gap:10px;border:1px solid var(--border);background:var(--card);border-radius:var(--r-card);padding:10px 12px;margin-top:7px}
  /* AVATAR is a component, not a row detail — do not re-scope this under .trow.
     It is used in the settings rows, the roster, and the time-adjust sheet header. */
  .tav{width:30px;height:30px;border-radius:99px;background:#69727b;color:#fff;font-weight:800;font-size:10.5px;display:flex;align-items:center;justify-content:center;flex:none}
  .trow .tn{font-weight:800;font-size:12px}
  .trow .tm{color:var(--tmut);font-size:9.5px;margin-top:1px}
  .trow .rl{margin-left:auto;font-size:9px;font-weight:800;border-radius:var(--r-ctrl);padding:3px 8px;letter-spacing:.05em}
  .rl.own{background:rgba(47,106,58,.12);color:#2f6a3a}
  .rl.mgr{background:rgba(63,111,174,.13);color:#3f6fae}
  .rl.frm{background:rgba(201,131,47,.13);color:#a06322}
  .invite{width:100%;margin-top:7px;border:1.5px dashed var(--strong);background:none;border-radius:var(--r-card);color:var(--tmut);font:inherit;font-weight:800;font-size:11.5px;padding:10px;cursor:pointer}
  .prow{display:flex;align-items:center;gap:10px;border:1px solid var(--border);background:var(--card);border-radius:var(--r-card);padding:11px 12px;margin-top:7px}
  .prow .pt{font-weight:800;font-size:12px}
  .prow .ps{color:var(--tmut);font-size:10px;margin-top:2px;line-height:1.35}
  .sw{margin-left:auto;flex:none;width:38px;height:22px;border-radius:99px;background:#c9ced3;border:0;cursor:pointer;position:relative;transition:.18s}
  .sw::after{content:'';position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:99px;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.25);transition:.18s}
  .sw.on{background:#7DBA3D}
  .sw.on::after{left:18px}
  /* NO-LOCKS LAW: never render a padlock or a disabled/greyed control anywhere.
     A policy that cannot be changed shows a static ALWAYS chip — information, not a dead switch. */
  .always{margin-left:auto;flex:none;font-size:9.5px;font-weight:800;letter-spacing:.06em;
          background:rgba(47,106,58,.10);color:var(--brand);border:1px solid rgba(47,106,58,.22);
          border-radius:var(--r-ctrl);padding:4px 8px;white-space:nowrap}
  .signout{width:100%;margin-top:14px;border:1px solid rgba(194,65,46,.35);background:none;border-radius:var(--r-card);color:#c2412e;font:inherit;font-weight:800;font-size:12px;padding:11px;cursor:pointer}
  /* home */
  .hcard{border:1px solid var(--border);border-radius:var(--r-card);background:var(--card);padding:13px 14px;margin-top:11px}
  .hh{display:flex;align-items:center;font-size:9.5px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:var(--tfaint);margin-bottom:9px}
  /* .ghost IS A CONTROL, wherever it is drawn. It only ever had a rule as `.hh .ghost` — a
     descendant of a card HEADER — so the ones that are not in one shipped as raw UA buttons:
     the approval queue's Retry (views-jobs paintQueueList), errCard's Retry (views-team), the
     Settings jump rows. A button offering the way out of a failed fetch, styled like nothing
     else in the app, does not read as the way out. The look lives on .ghost; .hh only adds the
     push to the right that a header row needs. */
  .ghost{border:1px solid var(--strong);background:none;border-radius:99px;color:var(--brand);font:inherit;font-weight:800;font-size:10.5px;padding:5px 11px;cursor:pointer;letter-spacing:.02em;text-transform:none}
  .hh .ghost{margin-left:auto}
  .wx{display:flex;align-items:center;gap:13px;border:1px solid var(--border);border-radius:var(--r-card);background:linear-gradient(135deg,#fdfefd,#eef4ee);padding:13px 15px}
  .wx svg{width:38px;height:38px;color:var(--amber);flex:none}
  .wx .t{font-size:22px;font-weight:800}
  .wx .m{color:var(--tmut);font-size:11px;margin-top:1px}
  .wx .auto2{margin-left:auto;font-size:9px;font-weight:800;color:var(--brand);background:rgba(125,186,61,.14);border-radius:var(--r-ctrl);padding:2px 7px}
  .ckjob{display:flex;align-items:center;gap:10px;border:1px solid var(--border);background:var(--scr);border-radius:var(--r-card);padding:10px 12px;cursor:pointer}
  .ckjob .jn{font-weight:800;font-size:12.5px}
  .ckjob .ja{color:var(--tmut);font-size:10px;margin-top:1px}
  .ckjob .chg{margin-left:auto;color:var(--brand);font-weight:800;font-size:11px;white-space:nowrap}
  .ckbtn{width:100%;margin-top:10px;border:0;border-radius:var(--r-card);background:var(--lime);color:#1c2b22;font:inherit;font-weight:800;font-size:14.5px;padding:14px;cursor:pointer;box-shadow:0 6px 18px rgba(125,186,61,.35)}
  .ckbtn.out{background:#26313a;color:#fff;box-shadow:none}
  .ckstat{display:flex;align-items:center;gap:8px;font-weight:800;font-size:13px}
  .ckstat .dot{width:9px;height:9px;border-radius:99px;background:var(--lime);box-shadow:0 0 0 4px rgba(125,186,61,.25);animation:pu 1.6s infinite}
  @keyframes pu{50%{box-shadow:0 0 0 7px rgba(125,186,61,.12)}}
  .ckel{margin-left:auto;font-variant-numeric:tabular-nums;font-weight:800;font-size:16px}
  .gpsnote{color:var(--tfaint);font-size:9.5px;margin-top:8px;text-align:center}
  /* ---- THIS WEEK (2026-08-13) ------------------------------------------------------------
     Replaces a progress ring + seven arbitrary-height bars. The chart sits on a FIXED 0-12h
     domain at 2h = 14px, so two weeks are the same shape and comparable; a stretch-to-fit axis
     draws a 4h week and a 12h week identically. Radii come from the scale, never literals. */
  .wk2hd{display:flex;align-items:flex-end;gap:12px;margin-bottom:12px}
  .wkc{font-size:27px;font-weight:800;letter-spacing:-.02em;line-height:1;font-variant-numeric:tabular-nums}
  .wkc small{display:block;font-size:10px;color:var(--tmut);font-weight:700;margin-top:3px;letter-spacing:0}
  .wk2goal{margin-left:auto;text-align:right;flex:none}
  .wk2meter{width:104px;height:7px;border-radius:99px;background:var(--strong);overflow:hidden;display:flex}
  .wk2meter i{display:block;height:100%;background:var(--lime)}
  .wk2meter i.ot{background:var(--red)}
  .wk2goal small{display:block;font-size:9.5px;font-weight:700;color:var(--tmut);margin-top:5px}
  .wk2chart{position:relative;height:84px}
  .wk2gl{position:absolute;left:0;right:26px;height:1px;background:var(--border)}
  .wk2gl.k{background:var(--strong)}
  .wk2gt{position:absolute;right:0;font-size:9px;font-weight:700;color:var(--tfaint);transform:translateY(-50%)}
  .wk2gt.k{color:var(--tmut);font-weight:800}
  .wk2base{position:absolute;left:0;right:26px;bottom:0;height:1px;background:var(--strong)}
  .wk2bars{position:absolute;left:0;right:26px;bottom:0;top:0;display:flex;align-items:flex-end;justify-content:space-around}
  /* The whole column is the target — the old card carried a title attribute, which never
     fires on a touch screen, so tapping a day did nothing for every crew member. */
  .wk2dc{width:34px;height:100%;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;position:relative;background:none;border:0;padding:0;font:inherit;cursor:pointer}
  .wk2stk{width:26px;display:flex;flex-direction:column;justify-content:flex-end;height:100%}
  .wk2stk i{display:block;width:100%}
  .wk2stk i.reg{background:var(--lime);border-radius:0 0 var(--r-micro) var(--r-micro)}
  .wk2stk i.reg.solo{border-radius:var(--r-micro)}
  .wk2stk i.ot{background:var(--red);border-radius:var(--r-micro) var(--r-micro) 0 0}
  .wk2tip{position:absolute;font-size:9px;font-weight:800;color:var(--text);font-variant-numeric:tabular-nums;white-space:nowrap}
  .wk2days{display:flex;justify-content:space-around;margin-top:7px;padding-right:26px}
  .wk2dl{width:34px;text-align:center;font-size:11px;font-weight:700;color:var(--tmut);position:relative}
  .wk2dl.z{color:var(--tfaint);font-weight:600}
  .wk2dl.td{color:var(--text);font-weight:800}
  .wk2dl.td::after{content:"";position:absolute;left:50%;bottom:-5px;transform:translateX(-50%);width:4px;height:4px;border-radius:99px;background:var(--brand)}
  .wkot{display:flex;align-items:center;gap:7px;margin-top:13px;font-size:10.5px;font-weight:800;color:var(--red)}
  .wkot::before{content:"";width:11px;height:11px;border-radius:var(--r-micro);background:var(--red);flex:none}
  .wk2note{margin-top:10px;font-size:10px;font-weight:700;color:var(--tfaint);letter-spacing:.02em}
  .toffrow{display:flex;align-items:center;gap:10px;border:1px solid var(--border);background:var(--scr);border-radius:var(--r-card);padding:9px 12px;margin-top:7px}
  .toffrow .ti{width:30px;height:30px;border-radius:var(--r-ctrl);background:rgba(63,111,174,.14);color:#3f6fae;display:flex;align-items:center;justify-content:center;flex:none}
  .toffrow .ti svg{width:16px;height:16px}
  .toffrow .tn2{font-weight:800;font-size:12px}
  .toffrow .tm2{color:var(--tmut);font-size:10px;margin-top:1px}
  .toffrow .tst{margin-left:auto;font-size:8.5px;font-weight:800;border-radius:var(--r-ctrl);padding:3px 8px;letter-spacing:.05em}
  .tst.ok{background:rgba(125,186,61,.15);color:#4d7a1f}
  .tst.pend{background:rgba(224,150,60,.16);color:#a06322}
  .jrow{display:flex;align-items:center;gap:11px;border:1px solid var(--border);background:var(--card);border-radius:var(--r-card);padding:12px 13px;margin-top:8px;cursor:pointer}
  .jrow .jic{width:34px;height:34px;border-radius:var(--r-card);background:rgba(47,106,58,.1);color:#2f6a3a;display:flex;align-items:center;justify-content:center;flex:none}
  .jrow .jic svg{width:18px;height:18px}
  .jrow .onchip{margin-left:auto;font-size:8.5px;font-weight:800;background:rgba(125,186,61,.16);color:#4d7a1f;border-radius:var(--r-ctrl);padding:3px 8px}
  .jrow mark{background:rgba(125,186,61,.34);color:inherit;border-radius:var(--r-micro);padding:0 1px}
  /* JOB SEARCH — the picker is a search field, not a list. Typing filters name AND address. */
  .srch{display:flex;align-items:center;gap:9px;border:1.5px solid var(--strong);background:var(--card);
        border-radius:var(--r-card);padding:0 11px;height:46px;margin:0 0 4px}
  .srch:focus-within{border-color:var(--brand);box-shadow:0 0 0 3px rgba(47,106,58,.1)}
  .srch .si{width:17px;height:17px;flex:none;color:var(--tfaint)}
  .srch:focus-within .si{color:var(--brand)}
  .srch input{flex:1;min-width:0;border:0;outline:0;background:none;font:inherit;font-size:15px;font-weight:600;color:var(--text)}
  .srch input::placeholder{font-weight:600;color:var(--tfaint)}
  .sx{border:0;background:var(--scr);color:var(--tmut);width:22px;height:22px;border-radius:50%;flex:none;
      cursor:pointer;font-size:13px;line-height:1;display:none;align-items:center;justify-content:center;padding:0}
  .sx.on{display:flex}
  .srchmeta{display:flex;align-items:center;gap:6px;margin:9px 4px 0;font-size:9.5px;font-weight:800;
            letter-spacing:.07em;text-transform:uppercase;color:var(--tfaint)}
  .srchmeta b{color:var(--brand);font-weight:800}
  .nohit{display:flex;flex-direction:column;align-items:center;gap:7px;padding:26px 0 20px;color:var(--tfaint)}
  .nohit svg{width:28px;height:28px;opacity:.55}
  .nohit .nt{font-size:12px;font-weight:700;color:var(--tmut)}
  /* Results scroll, the search field does NOT. A crew of 40 jobs must not push the
     input off the top of the sheet. Cap is ~4.5 rows so the cut edge reads as scrollable. */
  #jobList{max-height:330px;overflow-y:auto;-webkit-overflow-scrolling:touch;
           margin:0 -2px;padding:0 2px 2px}
  /* ---- CREW ON THE CLOCK (owner/manager) ---- */
  .hh .cnt{margin-left:6px;background:rgba(125,186,61,.18);color:#4d7a1f;border-radius:var(--r-ctrl);padding:2px 7px;font-size:9px;letter-spacing:.03em}
  .crow{display:flex;align-items:center;gap:10px;border:1px solid var(--border);background:var(--card);
        border-radius:var(--r-card);padding:9px 10px;margin-top:7px;cursor:pointer}
  .crow:active{background:var(--scr)}
  .crow .cav{width:31px;height:31px;border-radius:50%;flex:none;color:#fff;font-size:10.5px;font-weight:800;
             display:flex;align-items:center;justify-content:center;position:relative}
  .crow .cav i{position:absolute;right:-1px;bottom:-1px;width:9px;height:9px;border-radius:50%;
               background:var(--lime);border:2px solid var(--card)}
  .crow .cmid{flex:1;min-width:0}
  .crow .cn{font-weight:800;font-size:12.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  /* The EDITED chip lives on the job line, NOT inside .cn — an ellipsis on the name
     would eat it, and "this punch was edited" outranks the tail of a job name. */
  /* WRAPS, and the job name keeps a floor. A punch row (W1 1.5) can carry three chips at
     once — edited, closed by an admin, no GPS — and on one nowrap line flex shrank the
     JOB NAME, the one thing the row is actually about, down to a single letter to make
     room for them. Chips drop to a second line instead. */
  .crow .cj{display:flex;align-items:center;flex-wrap:wrap;gap:5px 5px;color:var(--tmut);font-size:10px;margin-top:1px}
  .crow .cjt{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1 1 auto;min-width:46%}
  .crow .cr{margin-left:auto;text-align:right;flex:none;padding-left:6px}
  .crow .ch{font-weight:800;font-size:12.5px;font-variant-numeric:tabular-nums}
  .crow .cs{color:var(--tfaint);font-size:9.5px;margin-top:1px}
  .crow .cpen{flex:none;width:28px;height:28px;border-radius:var(--r-ctrl);background:var(--scr);color:var(--tmut);
              display:flex;align-items:center;justify-content:center;margin-left:8px}
  .crow .cpen svg{width:14px;height:14px}
  .edchip{display:inline-flex;align-items:center;gap:3px;background:rgba(201,131,47,.14);color:#8a5a17;
          border-radius:var(--r-ctrl);padding:1px 5px;font-size:8.5px;font-weight:800;letter-spacing:.04em;flex:none}
  .edchip svg{width:8px;height:8px}
  /* ---- TIME-ADJUST SHEET ---- */
  .adjhd{display:flex;align-items:center;gap:11px;padding:0 2px 12px;border-bottom:1px solid var(--border);margin-bottom:4px}
  .adjhd .tav{width:38px;height:38px;font-size:12px}
  .adjhd .adjn{font-weight:800;font-size:14.5px}
  .adjhd .adjm{color:var(--tmut);font-size:10.5px;margin-top:1px}
  .adjhd .adjtot{margin-left:auto;font-weight:800;font-size:15px;color:var(--brand);font-variant-numeric:tabular-nums}
  .stepper{display:flex;align-items:center;gap:6px}
  .stp{flex:none;width:44px;height:42px;border:1px solid var(--strong);background:var(--card);border-radius:var(--r-card);
       font:inherit;font-weight:800;font-size:12px;color:var(--tmut);cursor:pointer}
  .stp:active{background:var(--scr);border-color:var(--brand);color:var(--brand)}
  .stepval{flex:1;text-align:center;font-weight:800;font-size:15px;font-variant-numeric:tabular-nums;line-height:1.15}
  .stepval small{display:block;font-size:9px;font-weight:700;color:var(--copper);margin-top:1px;letter-spacing:.02em}
  .req{margin-left:6px;font-size:8.5px;font-weight:800;letter-spacing:.06em;color:var(--copper);
       background:rgba(201,131,47,.13);border-radius:var(--r-ctrl);padding:2px 6px}
  .req.met{color:#4d7a1f;background:rgba(125,186,61,.16)}
  .whychips{display:flex;flex-wrap:wrap;gap:7px}
  .wc{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--strong);background:var(--card);
      color:var(--tmut);font:inherit;font-weight:700;font-size:11px;padding:8px 11px;border-radius:99px;cursor:pointer}
  .wc svg{width:13px;height:13px}
  .wc.on{background:var(--brand);border-color:var(--brand);color:#fff}
  .adjaud{border:1px solid var(--border);background:var(--scr);border-radius:var(--r-card);padding:9px 11px;margin-top:12px}
  .adjaud .at{font-size:9px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:var(--tfaint);margin-bottom:5px}
  .adjaud .ar{font-size:10.5px;color:var(--tmut);line-height:1.5}
  .adjaud .ar b{color:var(--text);font-weight:800}
  .adjout{width:100%;margin-top:8px;border:1px solid var(--strong);background:var(--card);color:var(--red);
          font:inherit;font-weight:800;font-size:13.5px;padding:12px;border-radius:var(--r-card);cursor:pointer}
  /* The break the CREW punched, stated above chips that cannot change it (#adjBrkPunch). Muted
     and small: it is a fact about the shift, not a control — and it is the sentence that stops
     the same half hour being deducted twice. */
  .brknote{color:var(--tmut);font-size:10px;line-height:1.4;margin:-2px 0 8px;overflow-wrap:anywhere}
  .kindchips{display:flex;gap:7px}
  .kc{border:1px solid var(--strong);background:var(--card);color:var(--tmut);font:inherit;font-weight:700;font-size:11.5px;padding:8px 14px;border-radius:99px;cursor:pointer}
  .kc.on{background:#3f6fae;border-color:#3f6fae;color:#fff}
  /* One sheet, one affirmative colour. The break chips sit next to a green total and a
     green Save, so they select green — the blue above belongs to the time-off flow. */
  #adjBrk .kc.on{background:var(--brand);border-color:var(--brand)}
  /* job header: address / docs / personal time */
  .addrrow{display:flex;align-items:center;gap:7px;margin-top:9px;font-size:11.5px;font-weight:700;color:var(--tmut)}
  .addrrow svg{width:15px;height:15px;color:#c2412e;flex:none}
  .maps{display:flex;gap:5px} /* LEFT-aligned — myTime owns the right side */
  .dirbadge{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--border);background:var(--card);border-radius:var(--r-card);padding:8px 12px;font:800 11.5px 'Segoe UI',-apple-system,system-ui,sans-serif;color:var(--text);cursor:pointer}
  .dirbadge svg{width:16px;height:16px;flex:none}
  .mapchip{border:1px solid var(--strong);background:var(--card);border-radius:99px;font:inherit;font-weight:800;font-size:10px;color:var(--brand);padding:4.5px 10px;cursor:pointer}
  .mapchip.ic{width:30px;height:30px;padding:0;display:inline-flex;align-items:center;justify-content:center}
  .mapchip.ic svg{width:15px;height:15px}
  .m .mi{width:11px;height:11px;vertical-align:-1.5px;margin:0 1px 0 7px}
  .m .mi:first-child{margin-left:0}
  /* Docs tab rows */
  .drow{display:flex;align-items:center;gap:11px;border:1px solid var(--border);background:var(--card);border-radius:var(--r-card);padding:11px 12px;margin-top:7px;cursor:pointer}
  .drow .dico{width:34px;height:34px;border-radius:var(--r-ctrl);background:rgba(47,106,58,.08);color:var(--brand);display:flex;align-items:center;justify-content:center;flex:none}
  .drow .dico svg{width:17px;height:17px}
  .drow .dn{font-size:12.5px;font-weight:800}
  .drow .fvc{margin-left:auto;font-size:8.5px;font-weight:800;background:rgba(47,106,58,.1);color:var(--brand);border-radius:var(--r-ctrl);padding:3px 7px;letter-spacing:.04em;white-space:nowrap}
  .drow .chev2{color:var(--tfaint);font-size:16px;flex:none}
  /* crew sheet rows */
  .cwrow{display:flex;align-items:center;gap:9px;border:1px solid var(--border);background:var(--card);border-radius:var(--r-card);padding:8px 10px;margin-top:6px}
  .cwrow .tav{width:26px;height:26px;font-size:10px}
  .cwrow .cn{font-size:12px;font-weight:800}
  .cwrow .ct{font-size:9.5px;color:var(--tfaint);font-weight:700;text-transform:uppercase;letter-spacing:.05em}
  .cwrow .star{margin-left:auto;border:0;background:none;font-size:15px;color:var(--strong);cursor:pointer;padding:2px 4px}
  .cwrow .star.on{color:var(--lime)}
  .cwrow .kick{border:0;background:none;color:var(--tfaint);font-size:15px;font-weight:800;cursor:pointer;padding:2px 4px}
  .leadchip{font-size:8px;font-weight:800;background:rgba(125,186,61,.16);color:#4e7f1f;border-radius:var(--r-ctrl);padding:2px 6px;letter-spacing:.05em;margin-left:6px}
  /* onboarding */
  #onbScr{position:absolute;inset:0;z-index:55;background:var(--scr);overflow-y:auto}
  #onbScr .onb{display:none;flex-direction:column;padding:26px 22px;min-height:100%}
  #onbScr .onb.on{display:flex}
  #onbScr .onb[data-step="1"]{background:linear-gradient(160deg,#2f6a3a,#24512c);color:#fff;align-items:center;justify-content:center;text-align:center}
  .onblogo{width:120px;margin-bottom:14px}
  .onbword{display:flex;gap:8px;font-size:24px;font-weight:800;letter-spacing:.14em}
  .onbword b{color:var(--lime)}
  .onbsub{font-size:11px;font-weight:700;color:rgba(255,255,255,.6);margin:6px 0 26px}
  .onblab{align-self:flex-start;font-size:9.5px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:rgba(255,255,255,.6);margin-bottom:5px}
  .onbinp{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.25);color:#fff;text-align:center;font-size:16px}
  .onbinp::placeholder{color:rgba(255,255,255,.4)}
  #onbScr .onb[data-step="1"] .submit{width:100%}
  /* the emailed code POPS UP MID-SCREEN — the whole group rides the vertical centre (Wes) */
  #onbScr .onb[data-step="2"]{justify-content:center;text-align:center}
  #onbScr .onb[data-step="2"] .onbh{margin-top:0}
  .onbh{font-size:19px;font-weight:800;margin:34px 0 18px;text-align:center}
  .onbcode{display:flex;gap:8px;justify-content:center;margin-bottom:22px}
  .onbcode span{width:44px;height:52px;border:1.5px solid var(--strong);border-radius:var(--r-card);background:var(--card);display:flex;align-items:center;justify-content:center;font-size:20px;font-weight:800}
  .onbcode span.f{border-color:var(--brand)}
  .onbghost{margin:10px auto 0;border:0;background:none;color:var(--tmut);font:700 12px 'Segoe UI',-apple-system,system-ui,sans-serif;cursor:pointer}
  .onbrole{text-align:center;font-size:11px;font-weight:800;color:var(--tmut);background:var(--card);border:1px solid var(--border);border-radius:999px;padding:8px 14px;margin:0 auto 16px;width:fit-content}
  .onbrole b{color:var(--copper)}
  .onblab2{font-size:9.5px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--tfaint);margin:12px 0 4px;display:block}
  /* onboarding step 4 — the location disclosure (R3 §2c) */
  #onbScr .onb[data-step="4"]{justify-content:center}
  .onbconsent{font-size:13px;line-height:1.6;color:var(--tmut);margin-bottom:18px}
  .onbconsent p{margin:0 0 12px}
  .onbconsent b{color:var(--text)}
  .onbconsent a{color:var(--brand);font-weight:800}

  /* ---- THE PASSWORD SURFACES (auth round, 2026-08-13) --------------------------------------
     The door's password box, "Set your password" (step 5) and Settings → Password → Change.
     These three shipped as ~14 hardcoded inline style="" strings on the elements themselves,
     plus cssText set from app.js — the code even said "styles.css is not this phase's file".
     It is this file's now, and the reason is not tidiness: the whole-app system pass restyles
     the app FROM this stylesheet, and it physically cannot reach anything written inline.
     (EVAL-LEDGER 2026-08-13, class system-drift.)

     The sizes are the brief these screens were designed to and were measured against: a gloved
     hand, direct sun, 6:15am in a gravel lot. Everything tappable is at least 56px; the copy
     that a person has to READ to get out of trouble — the refusal line, the rule ticks, the way
     past — was 10.5-13px and is not any more. */

  /* the box + its eye. The eye was 34x34, which is smaller than a fingertip. */
  .pwwrap{position:relative;width:100%}
  .pwwrap .inp{padding-right:64px}
  /* the door's box is centre-aligned, so it needs the same gutter on both sides or the text
     sits visibly off-centre behind the eye */
  #onbScr .onb[data-step="1"] .pwwrap .inp{padding-left:64px}
  .pweye{position:absolute;right:4px;top:50%;transform:translateY(-50%);width:56px;height:56px;
    display:flex;align-items:center;justify-content:center;border:0;background:none;
    color:var(--tmut);cursor:pointer;padding:0}
  .pweye svg{width:23px;height:23px}
  #onbScr .onb[data-step="1"] .pweye{color:rgba(255,255,255,.72)}

  /* THE ONE MESSAGE LINE, on all three surfaces. Absent until there is something to say — and
     that is a CLASS, never an inline display, so a refusal can never be left visible by a
     stylesheet that thinks it is hidden. */
  .pwmsg{display:none;width:100%;font-size:20px;font-weight:700;line-height:1.45;margin:14px 2px 0;color:var(--tmut)}
  .pwmsg.on{display:block}
  .pwmsg.warn{color:var(--copper)}
  #onbScr .onb[data-step="1"] .pwmsg{text-align:center;color:rgba(255,255,255,.72)}
  /* var(--copper) is unreadable on the dark green door; this is the same warning in a copper
     that actually carries on that background */
  #onbScr .onb[data-step="1"] .pwmsg.warn{color:#f3c78a}

  /* the live rule ticks. MET IS AN ATTRIBUTE (app.js sets data-ok), so met/unmet is one rule
     here instead of a hex pair inside a string.

     AND IT IS NOT A COLOUR ANY MORE. The old pair — words in --tfaint and a 9px --strong dot,
     going green together — was state by hue and nothing else: MEASURED at 2.36:1 for the words
     and 1.35:1 for the dot on --scr, which is invisible to the ~8% of a crew with red-green
     colour deficiency and close to invisible to everyone else at 6:15am in a gravel lot. app.js
     now writes a GLYPH into the <i> (a tick, or an empty ring), so met and unmet are two shapes
     before they are two colours, and unmet moved from --tfaint to --tmut — the app's own
     secondary ink, used by every other secondary line in the product. A bespoke darker hex
     invented here instead would be the `palette-drift` class.

     ZERO VERTICAL PIXELS: 17px x 1.4 = a 23.8px line box, and the glyph's line-height is that
     same 23.8px, so the row is exactly the height it was. Only the gutter got wider. */
  .pwrules{margin-top:12px}
  .pwrule{display:flex;align-items:flex-start;gap:9px;font-size:17px;font-weight:700;line-height:1.4;margin-top:9px;color:var(--tmut)}
  .pwrule i{flex:none;width:19px;font-style:normal;font-weight:800;font-size:15px;
    line-height:23.8px;text-align:center;color:inherit}
  .pwrule[data-ok="1"]{color:var(--brand)}
  .pwnote{font-size:15px;font-weight:700;color:var(--tfaint);margin-top:11px;line-height:1.45}

  /* the current-password box and the way-out button: ABSENT until they are the thing being
     asked for, and then the layout closes up around them (LAW 1 — never greyed, never a
     padlock). A class, so nothing has to remember which display value to put back. */
  .pwreveal{display:none}
  .pwreveal.on{display:block}

  /* 56px+ on everything these screens ask a gloved hand to hit (the house minimum is 56-64;
     the primary buttons measured 50). #delovl joins every one of these lists rather than
     getting a sizing block of its own: it is the same password flow wearing a different verb,
     and a second set of numbers for it is exactly how the two drift. */
  #onbScr .submit,#pwovl .submit{min-height:60px;font-size:17px}
  #onbScr .onb[data-step="1"] .inp,#onbScr .onb[data-step="5"] .inp,#pwovl .inp,#delovl .inp{min-height:56px}
  #onbScr .onbghost,#delovl .onbghost{min-height:56px;font-size:15px;padding:0 12px}
  /* THE ONE-WAY DOOR (2026-08-16). Its confirm is .signout — this app's EXISTING destructive
     treatment, red-outlined, already sitting at the foot of the very sheet this flow starts
     from — grown to the same 60px/17px the password sheet's primary button carries, because
     it is the primary action of its own screen. Nothing new is invented: no class, no colour,
     no hex. .submit (lime) is deliberately NOT reused, and neither is var(--lime) anywhere in
     this block: green is the positive/primary colour in this product, and a green button that
     ends an account is the screen contradicting its own words.

     The consequence copy is 20px — the same size as .pwmsg, the app's largest body type, set
     by the same rule the auth surfaces were raised to (a gloved hand, direct sun, 6:15am). It
     is the part a person MUST read before typing anything, so it is not allowed to be the
     smallest text on the sheet. */
  #delovl .signout{min-height:60px;font-size:17px;margin-top:18px}
  /* THE TWO REGIONS THAT GIVE WAY, IN THE ORDER THEY GIVE WAY. See the block below for why
     there are two and not one, and why the shrink factors are 1000 and 1 rather than 1 and 1. */
  #delovl .delsay{font-size:20px;font-weight:700;line-height:1.45;color:var(--tmut);margin:2px 2px 0;
    flex:0 1000 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}
  #delovl .delsay p+p{margin-top:12px}
  #delovl .pwmsg{flex:0 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}
  /* THE COPY AND THE REFUSAL SCROLL; THE BOX AND THE TWO BUTTONS DO NOT MOVE.
     MEASURED at 375x407 — an iPhone SE with the keyboard up, which is not an edge case on this
     sheet but its ONLY state, because it cannot be used without typing: #delPw sat at y=450,
     #delGo at 524 and #delCancel at 594, i.e. the password box, the confirm and the way out
     were ALL below the fold, and the sheet's own scrollHeight was 627 against a 358 clientHeight.
     The screen showed three paragraphs of consequence and no way to act on any of them.
     Making the copy shorter would have been the wrong repair — the copy is the point of the
     screen, and the next long refusal (the sole-owner 400 is eight lines at this width) would
     fold it again at FULL height. So the SHAPE changes: the sheet is a column, and the parts of
     it that are PROSE are the parts allowed to shrink and scroll.

     THE MESSAGE IS ONE OF THOSE PARTS — THIS IS THE HALF THAT WAS MISSING, AND THE PARAGRAPH
     ABOVE USED TO CLAIM THE OPPOSITE OF THE TRUTH. It read "everything below them keeps its
     measured height, which is what puts the box, the confirm and Cancel on screen at every
     size". That sentence was false at every width anyone measured. .pwmsg was in the flex:none
     list, so the moment ANY refusal printed it took its full measured height out of a 358px
     box and pushed the actions back out: with the everyday wrong-password 401 up, #delGo ended
     at 398 and #delCancel began at 408 in a 407px viewport — the destructive button the only
     control still lit and the way out below the fold — identical at 375, 390, 414 and 430
     (sheet 441/358 at all four), and worse under the sole-owner 400 (go 454, cancel 524, sheet
     557/358). The app GUARANTEES that state: submitDeleteAccount's 401 arm calls focusPw(),
     which re-raises the keyboard itself. (EVAL-LEDGER 2026-08-17, `keyboard-fold`, 4th
     occurrence + `doc-drift` on this very comment.)

     WHY 1000 AND 1, NOT 1 AND 1. With both regions at flex-shrink:1 the browser divides the
     deficit in proportion to their content heights, so at 375x407 under the sole-owner refusal
     .delsay lands ~26px and .pwmsg ~7px — a seven-pixel sliver of the sentence that just told
     somebody why they cannot leave. The two are not equally sacrificial: the consequence copy
     is the part you have already read (you got past it to reach the box), the refusal is the
     part that just arrived and names the next tap. So the copy is spent FIRST and completely,
     and the message loses height only after there is no copy left to give. Flexbox has no
     "shrink in this order" keyword; a shrink factor two decades larger is the idiom for it,
     and it is exact rather than approximate here because the copy hits its 0 floor, freezes,
     and the remainder is redistributed to the one item still able to move. MEASURED after:
     375x407 everyday 401 → sheet 358/358, pw · go · cancel all on screen, message 33px and
     scrolling under a fade; 390x900 sole-owner → copy 233px, message untouched at its full
     232px with no fade, exactly as before.
     flex:none on the action region is load-bearing — a flex item with a min-height can still be
     out-shrunk by its siblings' content, and "the button is 60px tall" is exactly the assertion
     that must not be able to become false. */
  #delovl .sheet{display:flex;flex-direction:column}
  #delovl label,#delovl .pwwrap,#delovl .signout,#delovl .onbghost{flex:none}
  /* AND THE FOCUS RING IS NOT GREEN. .inp:focus paints rgba(125,186,61,.4) over a var(--lime)
     border, and openDeleteAccount() focuses this box on every open — so the single green thing
     on the app's one destructive surface was the field it asks you to type your password into,
     on a sheet whose own comment says --lime appears nowhere in it (EVAL-LEDGER, `palette-drift`).
     Same SHAPE as the house ring, in the house's neutral inks: the primary ink for the border,
     the hairline-strong token for the halo. No new value is invented for this one screen. */
  #delovl .inp:focus{outline:2px solid var(--strong);border-color:var(--text)}
  /* AND THE CUT HAS TO LOOK LIKE A CUT. With the copy scrolling, a sliced sentence with nothing
     under it reads as broken text, not as "scroll". A soft fade at the bottom edge is the whole
     affordance.
     GATED ON THE OVERFLOW ITSELF, NOT ON A VIEWPORT HEIGHT. This shipped inside
     `@media (max-height:560px)` on the stated argument that a short screen was "the only case
     where the region can overflow". MEASURED at 390x900 — a full-height phone — with the real
     sole-owner refusal on screen: .delsay scrollHeight 401 against clientHeight 233, 168px of
     copy below the cut, computed mask `none`. The region overflows when a long MESSAGE pushes
     it, which is the flex column doing its job, and that has nothing to do with how tall the
     phone is (EVAL-LEDGER 2026-08-17, `keyboard-fold`). So the condition is now the two numbers
     themselves: app.js delCutSync() sets `.cut` when scrollHeight exceeds clientHeight, on open,
     on every message render, and on every viewport resize while the sheet is open — the keyboard
     coming up IS a resize on this sheet, and it is the event that moves both regions the most.
     The media query is gone — a second gate would only be a second place for the two to
     disagree. The constraint the old rule was right about is kept: with nothing to scroll to
     there is no mask, so a full paragraph's last line is never faded.
     ONE SELECTOR FOR BOTH REGIONS, because as of the fix above there are two scrollable regions
     on this sheet and a refusal squeezed to 33px is sliced exactly as badly as a paragraph is —
     a second rule for the message would only be a second place for the cue to drift. */
  #delovl .delsay.cut,#delovl .pwmsg.cut{-webkit-mask-image:linear-gradient(180deg,black calc(100% - 20px),transparent);
    mask-image:linear-gradient(180deg,black calc(100% - 20px),transparent)}
  /* Same ink as the sheet it opens, on the same .ghost component and the same 56px box as
     #pwOpen. var(--red) is the app's existing danger token (.adjout draws its label in it on a
     bordered card exactly like this); the alternative was leaving the word "Delete" in brand
     green, which is the one colour this product reserves for the action you DO want. */
  #delOpen{color:var(--red)}
  /* AND THE DOOR TO ALL OF THAT. #pwOpen is the ONLY way into the change sheet, and it measured
     44px tall at 10.5px type while the surfaces it opens are 56-60px at 15-17px — two standards
     inside one flow, the smallest control in the flow being the one that starts it. 44px is not
     wrong in isolation: it is the gloved-thumb FLOOR measured on a Galaxy A12 by test/android.cjs
     (see the port-hardening block at the bottom of this file). A floor is not the size a password
     control gets. margin-left:auto lives here now instead of an inline style on the button. */
  /* white-space:nowrap is not a nicety here: #delOpen's row carries a two-line sub-sentence, and
     under .prow's flex the label was squeezed until the chevron wrapped onto a second line — a
     115px control reading "Delete" over "›", which is a button that looks broken on the one row
     in the app that most needs to look deliberate. CAUGHT BY OPENING THE SCREENSHOT, not by an
     assertion: every measurement passed, because a wrapped button is TALLER than the floor. */
  #pwOpen,#delOpen{min-height:56px;font-size:15px;padding:0 16px;margin-left:auto;white-space:nowrap;flex:none}

  /* the second label on the door — the password box needs air under the email box */
  .onblab.pwlab{margin-top:14px}
  #onbScr .onb[data-step="1"] .onbghost{color:rgba(255,255,255,.78)}
  #onbScr .onb[data-step="1"] .subnote{color:rgba(255,255,255,.55)}

  /* ---- THE DOOR AT KEYBOARD HEIGHT (2026-08-17, EVAL-LEDGER `keyboard-fold`) ---------------
     MEASURED at 375x407 — an iPhone SE with the software keyboard up, which is the ONLY state
     a sign-in screen is in from the moment somebody taps the email box. #onbEm was the only
     control on screen: #onbPw ended at 418, "Sign in" ran 436-496 and "Forgot password" — the
     other road, and the ONLY other road since the access button became config — ran 506-562,
     against a 407px window. #onbScr scrollHeight 611 vs clientHeight 407.

     THE FIX IS THE SHAPE, NOT THE COPY, per the standing check: the column has ONE region that
     is allowed to give way, and it is the BRANDING — a 125px logo, a wordmark and a subtitle,
     218px of the 611 spent saying whose app this is to somebody who is already holding it.
     Nothing that a thumb touches moves: every control keeps its 56/60px box and its type.
     Three steps rather than one, because a 568px-tall phone, a 407px keyboard state and a
     407px keyboard state WITH A REFUSAL ON IT are not the same problem. At 620 the logo
     shrinks; at 460 it goes altogether, along with "Invited by your company · nothing to
     create", leaving the wordmark and the company line; and at 460 WITH a message showing
     (`.saying`, below) those last two go as well, because at that point the sentence a person
     is trying to read is worth more than the sentence telling them whose app it is. Keyed on
     HEIGHT only — a short LANDSCAPE window gets the same treatment for the same reason, and a
     tall narrow one is untouched. Measured after: 611 → 407 clean, 407 with a refusal.

     WHAT IS LEFT OVER IS SCROLLED, AND SAID SO. Even trimmed, this column still exceeds 407:
     the third road is ON by default (DEFAULT_CONFIG.accessCodeEnrollment is TRUE — this line
     named "Luce Line" as the install that had it, which was true for the hours between Wes's
     two calls on 2026-08-17 and stopped being true that evening), and a two-line refusal in
     #onbSignMsg pushes it further. MEASURED on the default door at 375x407: 458 against 407,
     with the third road the part below the fold. A column that silently overflows is the
     defect whether or not it can be scrolled. .onbmore below is the cue, gated on the overflow
     itself. */
  @media (max-height:620px){
    /* THE CHROME YIELDS ON EVERY STEP, NOT JUST THE DOOR (2026-08-17, fix round 3). The two
       rules directly below are the whole of the discipline applied to the other five screens:
       26px of column padding and a 34px heading margin are decoration, and at keyboard height
       they were the difference between a primary action on screen and one clipped by 7px.
       MEASURED at 375x407, before → after: step 3 534→486 (still over — it carries two text
       inputs, a chip row and a switch, and genuinely does not fit; it scrolls and says so),
       step 4 440→392 FITS OUTRIGHT, step 6 518→470 with "Continue" fully on screen and only
       "‹ Back to sign in" below the fold behind the cue. Steps 2 and 5 fit before and after —
       they only gain slack for the message states.
       NOTHING A THUMB TOUCHES MOVES: every control keeps its 56/60px box and its type size,
       which is the same rule the step-1 block below already answers to.
       `[data-step]` IS LOAD-BEARING, not decoration: the PORT HARDENING block at the bottom of
       this file carries `#onbScr .onb{padding-bottom:calc(26px + env(safe-area-inset-bottom))}`
       for the Android 15 gesture pill, at the same specificity as a bare `#onbScr .onb` and
       LATER in the file — so a bare selector here would silently lose the bottom half of the
       trim and only the top 10px would land. The attribute puts this back above it, which is
       the specificity the step-1 rule it replaces already had.
       The heading rule stays at `#onbScr .onb .onbh` deliberately: step 2's own
       `[data-step="2"] .onbh{margin-top:0}` outranks it, so the centred code screen keeps the
       zero top margin it was designed with and only gives up the bottom 6px. */
    #onbScr .onb[data-step]{padding-top:16px;padding-bottom:calc(16px + env(safe-area-inset-bottom,0px))}
    #onbScr .onb .onbh{margin:12px 0 12px}
    #onbScr .onb[data-step="1"] .onblogo{width:76px;margin-bottom:8px}
    #onbScr .onb[data-step="1"] .onbword{font-size:19px}
    #onbScr .onb[data-step="1"] .onbsub{margin:4px 0 14px}
    #onbScr .onb[data-step="1"] .onblab{margin-top:8px}
    #onbScr .onb[data-step="1"] .submit{margin-top:12px}
  }
  @media (max-height:460px){
    /* The LOGO goes and the reassurance line goes. What stays is the wordmark and
       #onbCo — "The crew app for <their company>" — because that line is the one thing on the
       column that tells somebody they are at the right company's door, and 33px is not worth
       taking it. "Invited by your company · nothing to create" is reassurance, which is the
       only thing here that is not a control, a label, a road, or an identity. */
    #onbScr .onb[data-step="1"] .onblogo{display:none}
    #onbScr .onb[data-step="1"] #onbSub1{display:none}
    /* AND WHEN THE SCREEN IS REFUSING SOMEBODY, THE REST OF THE BRAND YIELDS TO THE REFUSAL.
       `.saying` is set by app.js onbSetSignMsg whenever #onbSignMsg carries text. MEASURED at
       375x407: the refusal line is 58px plus a 14px margin and the trimmed column has 16px of
       slack, so ANY message pushed "Forgot password" — the ONLY other road off this door —
       below the fold. What goes instead is a wordmark and "The crew app for <company>", to
       somebody who is holding the phone, has already typed their address into it, and is
       reading a sentence about why they are not in yet. Both come straight back the moment the
       message clears, and above 460px neither ever moves.
       Keyed on the MESSAGE, not on the measured overflow: a rule that hid a block because the
       column overflowed would shorten the column, clear its own condition, and oscillate. */
    #onbScr .onb[data-step="1"].saying .onbword{display:none}
    #onbScr .onb[data-step="1"].saying .onbsub{display:none}
  }
  /* THE CUT. Same affordance and the same gate as the delete sheet's `.cut` (see #delovl
     .delsay.cut): app.js compares scrollHeight to clientHeight and toggles the class, so it is
     on exactly when there is something below the fold and off when there is not — never keyed
     on a viewport height, which is the mistake that rule already records.
     STICKY, not absolute: #onbScr is the scroller, so anything positioned inside it scrolls
     away with the content; sticky pins it to the bottom edge of the window instead. The
     negative margin means it occupies NO layout height — the column it appears over is the
     column that exists without it — and pointer-events:none keeps it from eating the tap
     meant for "Forgot password" underneath.
     NOT SCOPED TO A STEP ANY MORE (2026-08-17, fix round 3). It shipped as
     `#onbScr.cut .onb[data-step="1"] .onbmore`, and .cut is set by onbCutSync() on the SHARED
     scroller for WHICHEVER step is showing — so on steps 3 (534/407), 4 (440/407) and 6
     (518/407) the class was on, the overflow was real, and the one element that could have
     said so was inside a display:none step, computing to a 0x0 box. That is the 6th occurrence
     of `keyboard-fold`, and every one of them has been the same mistake: a correct gate wired
     to too small a scope. The gate was never wrong; only this selector was. The node is now a
     SIBLING of the six steps and last in the scroller (index.html, THE CUT), so `>` is the
     whole of it and there is exactly one of them to keep right.
     THE COLOUR FOLLOWS THE STEP UNDERNEATH. Step 1 is the only green screen in the app —
     #24512c is the end stop of its own background gradient, so the fade is that screen
     dissolving into itself — and steps 2-6 sit on var(--scr). A single `~` off the SHOWING
     step is what picks between them; it works because .onbmore is after all six in the
     markup, which is the other reason it is last rather than first.

     A CUE MAY NOT LAND ON THE THING IT IS ANNOUNCING YOU CAN SCROLL PAST (2026-08-17, fix
     round 4). It shipped as one 40px band with the glyph at its foot on every step, and that
     put both halves of the cue — the WASH and the CHEVRON — wherever the fold happened to
     fall. MEASURED at 375x407, and the two steps that overflow at rest fail it in opposite
     directions:
       * STEP 6 — "Continue" runs 328-388 and the band ran 367-407, so the primary action's
         bottom 21px sat under a gradient reaching rgba(...,.94): about a 49% wash across the
         button's own bottom edge, and the composition at rest was a dimmed CTA under a
         chevron (EVAL-LEDGER `density`). Fixed by making the band 19px — the glyph's own
         height — so it begins at 388, exactly where Continue's box ends. PROVEN BY PIXELS, not
         by geometry: the step photographed with the cue on and again with it hidden differs
         only on rows 389-406, so not one pixel of the primary action is touched by the wash or
         by the chevron's ink.
       * STEP 1 — the fold lands INSIDE a control's text rather than below one: the third road
         runs 385-441 with its first line of type at 393-413, and the chevron's box (382-408
         measured over the text node; a 19px line box at 386-405) landed in the middle of a
         word ("Use the c⌄de your office gave"). A short
         band cannot fix that, because moving the glyph DOWN moves it further into the line.
         So step 1 keeps the tall fade — which is correct there, and was: what the band covers
         is a road below the fold, which is the thing being announced — and moves the GLYPH to
         the TOP of it instead (align-items:flex-start: line box 367-386, and 363-389 measured
         generously over the text node — clear of 393 either way). That lands the chevron in
         the 10px gutter between "Forgot password" (ends 375) and the peeking third road
         (starts 385), clear of both controls' type, pointing straight down at the road it is
         there to reveal.
     TWO GEOMETRIES BECAUSE THERE ARE TWO SHAPES OF FOLD, not because step 1 is special: a
     screen whose last control ends above the fold wants the band to start where that control
     ends, and a screen whose fold cuts through a control wants the glyph out of its type.
     Both are stated as numbers and both are asserted at 375x407 in test/access-code-ui.cjs,
     so a copy change that moves either control turns a suite red rather than quietly
     re-creating this. */
  .onbmore{display:none}
  #onbScr.cut > .onbmore{display:flex;align-items:flex-end;justify-content:center;
    position:sticky;bottom:0;flex:none;height:19px;margin:-19px 0 0;
    pointer-events:none;line-height:1;font-size:19px;font-weight:800;
    color:var(--tmut);
    background:linear-gradient(180deg,rgba(243,245,247,0),rgba(243,245,247,.94))}
  /* height/margin restated rather than inherited: this rule is the WHOLE of step 1's cue
     geometry, so a reader who finds it does not have to hold the base rule in their head to
     know where the band is. The pair still cancels to zero layout height, which is what keeps
     .cut from oscillating — turning the cue on must never change the number onbCutSync()
     reads. */
  #onbScr.cut > .onb[data-step="1"].on ~ .onbmore{
    height:40px;margin:-40px 0 0;align-items:flex-start;
    color:rgba(255,255,255,.78);
    background:linear-gradient(180deg,rgba(36,81,44,0),rgba(36,81,44,.94))}
  /* step 5's own copy — this is the sentence that tells a person the password is theirs alone */
  #onbScr .onb[data-step="5"] .onbconsent{font-size:17px;margin-bottom:6px}
  /* STEP 6 — the access code box. Typed once, from something read aloud over a running
     engine, so it is the largest input in the app: 26px, wide-tracked, centred, tabular
     figures so the eight characters sit in eight fixed columns and a gloved thumb can count
     them against what is being shouted at it. Uppercase is enforced here as well as in JS,
     because a lowercase keyboard must not make the code LOOK different from the one on the
     owner's screen while it is being typed — a person checking their own work is the whole
     point of the box. The placeholder carries the same tracking so the field does not resize
     the instant the first character lands. */
  .onbaccode{text-align:center;font-size:26px;font-weight:800;letter-spacing:.16em;
    text-transform:uppercase;font-variant-numeric:tabular-nums;min-height:64px;padding:0 10px}
  .onbaccode::placeholder{letter-spacing:.16em;font-weight:700;opacity:.32;text-transform:none}
  #onbScr .onb[data-step="6"] .onbconsent{font-size:15px}

  /* ---- THE HANDOFF SHEET (#accovl) — the owner's side of the same code ---------------------
     The code is read ALOUD from this screen while someone types it into theirs, so the two
     have to LOOK like the same eight characters: same uppercase alphabet, same tabular
     columns, same dash after four. The size difference is deliberate — the reader is holding
     the phone at arm's length in the sun, the typist is looking straight down at theirs. */
  .acccode{font-size:38px;font-weight:800;letter-spacing:.14em;text-align:center;
    font-variant-numeric:tabular-nums;color:var(--text);background:var(--scr);
    border:1.5px solid var(--strong);border-radius:var(--r-card);padding:18px 10px;margin:2px 0 10px;
    /* the one place in this app where selecting text is the point */
    user-select:all;-webkit-user-select:all}
  .accwho{font-size:13px;line-height:1.6;color:var(--tmut);margin:0 0 14px}
  .accwho b{color:var(--text)}
  /* The countdown. Copper, never red: an expiring code is a deadline, not a safety event,
     and red in this app is reserved for the second thing. */
  .acclife{display:flex;align-items:center;justify-content:center;gap:7px;font-size:12px;
    font-weight:800;color:var(--copper);margin:0 0 14px;font-variant-numeric:tabular-nums}
  .accsteps{font-size:13px;line-height:1.75;color:var(--tmut);margin:0 0 16px;padding:0 0 0 18px}
  .accsteps li{margin:0 0 4px}
  .accsteps b{color:var(--text)}
  .accrow{display:flex;gap:8px;margin-bottom:10px}
  .accrow>*{flex:1}
  .accerr{font-size:13px;line-height:1.6;color:#c2412e;font-weight:700;margin:0 0 12px}
  /* ---- GET SET UP (#setupCard) — the owner's first morning -------------------------------
     Two row states, and the difference between them is the whole design. A step still to do
     is a full 56px tappable row with a chevron, because it is work. A step that is DONE
     collapses to a quiet 30px line with a check — present, so the list still reads as a list
     and the owner can see how far along they are, but visually out of the way so what is
     left is what the eye lands on. Nothing here greys out and nothing disappears row by row;
     the whole card goes at once, when the last step lands. */
  .setupcard{border-color:var(--strong)}
  .setupcount{margin-left:auto;font-size:9.5px;font-weight:800;letter-spacing:.06em;color:var(--copper)}
  .sturow{display:flex;align-items:center;gap:11px;width:100%;text-align:left;border:0;background:none;
    padding:9px 0;min-height:56px;cursor:pointer;border-top:1px solid var(--border);font:inherit;color:inherit}
  .sturow:first-child{border-top:0}
  .sturow .stt{font-size:14.5px;font-weight:800;color:var(--text);line-height:1.3}
  .sturow .std{font-size:12px;color:var(--tmut);margin-top:2px;line-height:1.45}
  .sturow .stchev{margin-left:auto;flex:none;color:var(--tfaint);font-size:17px;font-weight:800}
  /* The step marker. Copper ring for outstanding, brand fill for done — never red, because
     an unfinished setup is a to-do, not a fault, and red in this app means safety. */
  .stumk{flex:none;width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;
    border:2px solid var(--copper);box-sizing:border-box}
  .stumk svg{width:13px;height:13px}
  .sturow.done{min-height:0;padding:7px 0;cursor:default}
  .sturow.done .stt{font-size:12.5px;font-weight:700;color:var(--tmut)}
  .sturow.done .std{display:none}
  /* A FINISHED STEP IS THE QUIETEST THING ON THE CARD, not the loudest. These check discs
     wore --brand while the page's primary CTA wears --lime, which is the inversion the eval
     called out: the passive marker was carrying more colour weight than the button. Grey
     disc, white tick — it still reads as done at a glance, and it stops competing with the
     one green thing on Home that is actually asking to be pressed. */
  .sturow.done .stumk{width:17px;height:17px;border:0;background:var(--tfaint);color:#fff}
  .sturow.done .stumk svg{width:11px;height:11px}
  /* ---- THE COMPANY OVERVIEW (Office) -----------------------------------------------------
     Ported from design/mockups/office-overview.html, which was built on this stylesheet — so
     almost nothing here is new. The overview reuses .hcard/.hh, .kpis/.kpi and .trow/.tav
     wholesale; these are the four rules the mockup actually invented.

     THE HEADLINE. One number per screen and this is it: what is left after crew and
     materials across every open job. 34px because it is the reason the page exists, tabular
     because a column of other money sits under it. */
  .bignum{font-size:34px;font-weight:800;line-height:1;font-variant-numeric:tabular-nums;
    letter-spacing:-.01em;margin:2px 0 4px}
  .bigsub{font-size:12px;color:var(--tmut);line-height:1.45}
  .bigsub b{color:var(--text)}

  /* A JOB LINE. Quoted, spent, and the bar between them. .bbar's look with a SECOND fill,
     because "where did it go" is the question the bar is being asked: green is crew, copper
     is materials, and the pale remainder is what is left. */
  .jrow2{padding:13px 0;border-top:1px solid var(--border)}
  .jrow2:first-of-type{border-top:0}
  .jr2h{display:flex;align-items:baseline;gap:8px}
  .jr2n{font-size:14.5px;font-weight:800;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .jr2v{margin-left:auto;flex:none;font-size:14.5px;font-weight:800;font-variant-numeric:tabular-nums}
  .jr2s{font-size:11.5px;color:var(--tmut);margin-top:3px;font-variant-numeric:tabular-nums}
  .split{display:flex;height:9px;border-radius:99px;overflow:hidden;margin:8px 0 0;background:var(--scr)}
  .split i{display:block;height:100%}
  .split .lab{background:var(--brand)}
  .split .mat{background:var(--copper)}
  .legend{display:flex;flex-wrap:wrap;gap:12px;margin-top:7px;font-size:10px;font-weight:700;color:var(--tmut)}
  .legend span{display:flex;align-items:center;gap:5px}
  .legend i{width:8px;height:8px;border-radius:2px;display:block;flex:none}

  /* THE HONESTY LINE. Copper, never red — an hour with no wage on it is a gap in the data,
     not a safety event, and red in this app means the second thing. Rendered only when there
     is something to say. */
  .caveat{margin-top:10px;font-size:11.5px;line-height:1.5;color:var(--copper);font-weight:700}

  /* Money right-aligned against a label, on the overhead rows. */
  .ovnum{margin-left:auto;flex:none;font-weight:800;font-variant-numeric:tabular-nums}

  /* ---- MY WEEK (v-week) — the crew's fifth tab -------------------------------------------
     The hours are the hero, because the hours are the whole reason this screen exists. 40px
     is the largest number anywhere in the crew app on purpose: it is read at arm's length,
     in a truck, in the sun, by someone who wants one fact. Tabular figures so 8.25 and 41.75
     have the same width and the day column below stays a column.
     NOT ONE DOLLAR SIGN LIVES IN HERE, and that is LAW 2, not an oversight. */
  .mwtot{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap}
  .mwtot b{font-size:40px;font-weight:800;line-height:1;font-variant-numeric:tabular-nums;color:var(--text)}
  .mwtot span{font-size:12.5px;font-weight:700;color:var(--tmut)}
  /* Over 40 is COPPER, never red. Red in this app means safety; overtime is a fact about a
     week, and for most of the people reading this it is a good one. */
  .mwot{margin-top:9px;font-size:13px;font-weight:800;color:var(--copper)}
  .mwleft{margin-top:9px;font-size:13px;font-weight:700;color:var(--tmut)}
  /* THE WEEK PAGER. 56px arrows, because this is the crew app and everything tappable in it
     is 56px; the label between them is the week, not a decoration, so it carries the weight. */
  .mwnav{display:flex;align-items:center;gap:6px;margin:-2px 0 10px}
  .mwarrow{flex:none;width:56px;height:56px;margin:-9px 0;border:0;background:none;color:var(--tmut);
    font:800 26px 'Segoe UI',-apple-system,system-ui,sans-serif;line-height:1;border-radius:50%;
    cursor:pointer;-webkit-tap-highlight-color:transparent;display:flex;align-items:center;justify-content:center}
  .mwarrow:active{background:var(--scr)}
  .mwnavlbl{font-size:12.5px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--tmut)}
  /* "This week" is the way BACK from wherever you paged to, so it sits at the end of the row
     and only exists once you have gone somewhere. */
  .mwnow{margin-left:auto;flex:none;min-height:40px;padding:0 14px;border:1px solid var(--border);
    background:var(--card);color:var(--brand);border-radius:999px;
    font:800 12px 'Segoe UI',-apple-system,system-ui,sans-serif;cursor:pointer}
  .mwnow:active{background:var(--scr)}
  .mwday{display:flex;align-items:center;gap:10px;padding:11px 0;border-top:1px solid var(--border);min-height:48px}
  .mwday:first-child{border-top:0}
  .mwdn{font-size:14px;font-weight:800;color:var(--text);flex:none;width:88px}
  /* TWO SITES IN ONE DAY HAS TO SURVIVE THE COLUMN (eval 2026-08-15). This was one nowrap
     line, so a long first job name ate the whole cell and the second site vanished with no
     sign it had ever been there — and because the hours column was auto-width, the
     truncation point moved from row to row. Now: the hours column is FIXED (so every row
     truncates in the same place, and a week of days reads as a column), and the where-cell
     wraps to at most two lines instead of dropping what does not fit. */
  .mwdw{font-size:12px;color:var(--tmut);flex:1;min-width:0;line-height:1.35;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  .mwdh{font-size:15px;font-weight:800;font-variant-numeric:tabular-nums;color:var(--text);
    flex:none;width:58px;text-align:right}
  /* A DAY OFF IS QUIET, NOT EMPTY. It keeps its row so the week still reads as seven days —
     a list that silently skips Saturday makes a person count on their fingers. */
  .mwday.off .mwdn{font-weight:700;color:var(--tfaint)}
  .mwday.off .mwdh{color:var(--tfaint);font-weight:700}
  /* contacts / directory (v-dir, R3 §2a) — rows reuse .trow/.tav; only the call affordances are new */
  .callbtn{display:inline-flex;align-items:center;gap:6px;margin-left:auto;flex:none;border:1px solid rgba(201,131,47,.45);background:rgba(201,131,47,.10);color:#a06322;border-radius:999px;padding:7px 14px;font:800 11.5px 'Segoe UI',-apple-system,system-ui,sans-serif;text-decoration:none;white-space:nowrap}
  .callbtn svg{width:13px;height:13px;flex:none}
  .callbtn:active{background:rgba(201,131,47,.2)}
  .youtag{margin-left:auto;flex:none;font-size:9px;font-weight:800;letter-spacing:.05em;color:var(--tmut);background:var(--scr);border:1px solid var(--border);border-radius:999px;padding:4px 10px}
  .nonum{margin-left:auto;flex:none;font-size:9.5px;font-weight:700;color:var(--tfaint)}
  .dirwhy{font-size:10px;color:var(--tfaint);margin:2px 2px 6px;line-height:1.5}
  .dirwhy b{color:#a06322}
  /* clock break + quick directions */
  .ckhead{display:flex;align-items:center;justify-content:center;gap:7px;font-size:11.5px;font-weight:800;color:var(--tmut);margin-top:2px}
  .ckel2{font-size:36px;font-weight:800;text-align:center;letter-spacing:.5px;margin:8px 0 10px;font-variant-numeric:tabular-nums}
  .ckbrkwrap{text-align:center}
  .ckbrkline{font-size:10.5px;font-weight:800;color:#8a5e13;background:rgba(185,127,28,.1);border-radius:var(--r-ctrl);padding:6px 12px;margin-bottom:10px;display:inline-block}
  .brkbtn{width:100%;border:1.5px solid var(--border);background:var(--card);color:var(--text);border-radius:var(--r-card);padding:12px;font:800 13px 'Segoe UI',-apple-system,system-ui,sans-serif;cursor:pointer;margin-bottom:9px}
  .brkbtn.on{border-color:var(--amber);background:rgba(185,127,28,.1);color:#8a5e13}
  .dot.brk{background:var(--amber);box-shadow:0 0 0 4px rgba(185,127,28,.15)}
  .qkrow{display:flex;gap:7px;overflow-x:auto;padding-bottom:2px;scrollbar-width:none}
  .qk{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--border);background:var(--scr);border-radius:var(--r-card);padding:9px 12px;font-size:11px;font-weight:800;cursor:pointer;white-space:nowrap;color:var(--text)}
  .qk svg{width:14px;height:14px;flex:none;color:var(--brand)}
  .qk.set{border-style:dashed;color:var(--tmut)}
  .qk .pcat{margin-left:2px}
  /* places — rows inside the search sheet (the strip is retired) */
  #navPick .kc,#onbNav .kc{display:inline-flex;align-items:center;gap:7px;padding:9px 13px;white-space:nowrap}
  #navPick .kc svg,#onbNav .kc svg{width:16px;height:16px;flex:none;display:block}
  .pcat{font-size:8px;font-weight:800;background:rgba(201,131,47,.12);color:#a06322;border-radius:var(--r-ctrl);padding:2px 6px;letter-spacing:.05em;vertical-align:1px;white-space:nowrap}
  .plgo{margin-left:auto;flex:none}
  .plgo svg{width:17px;height:17px;display:block}
  .pledit{flex:none;border:0;background:none;color:var(--tfaint);cursor:pointer;padding:2px 0 2px 6px}
  .pledit svg{width:14px;height:14px;display:block}
  .plmaps{display:flex;gap:10px;justify-content:center;margin:12px 0 4px}
  .plmaps .mapchip{width:44px;height:44px}
  .plmaps .mapchip svg{width:24px;height:24px}
  .pladdr{text-align:center;font-size:12px;font-weight:700;color:var(--tmut);margin-top:2px}
  .plnamero{text-align:center;font-size:15px;font-weight:800;margin-top:4px}
  /* time-off calendar */
  .cal{border:1px solid var(--border);background:var(--card);border-radius:var(--r-card);padding:10px 10px 8px;margin-top:2px}
  .calh{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}
  .calh b{font-size:12.5px;font-weight:800}
  .calnav{width:30px;height:30px;border-radius:var(--r-ctrl);border:1px solid var(--border);background:var(--scr);font-size:15px;font-weight:800;color:var(--tmut);cursor:pointer}
  .calg{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
  .calg.dow span{text-align:center;font-size:8.5px;font-weight:800;letter-spacing:.06em;color:var(--tfaint);padding:2px 0}
  .cd{height:34px;display:flex;align-items:center;justify-content:center;border-radius:var(--r-ctrl);font-size:12px;font-weight:700;color:var(--text);cursor:pointer}
  .cd.out{visibility:hidden}
  .cd.past,.cd.wknd{color:var(--tfaint);cursor:default;font-weight:600}
  .cd.wknd{background:var(--scr)}
  .cd.today{box-shadow:inset 0 0 0 1.5px var(--strong)}
  .cd.in{background:rgba(125,186,61,.18)}
  .cd.sel{background:var(--brand);color:#fff;font-weight:800}
  .calsum{margin-top:7px;text-align:center;font-size:10.5px;font-weight:800;letter-spacing:.04em;color:var(--tmut)}
  /* time-off wheels — ONE card, true Apple picker: 5 rows, edges fade to nothing,
     a soft grey pill marks the selection. NO hairlines anywhere (Wes). */
  .whlbox2{position:relative;display:flex;align-items:stretch;border:1px solid var(--border);border-radius:var(--r-card);background:var(--card);margin:8px 0 2px;overflow:hidden}
  .whlbox2:before{content:'';position:absolute;left:8px;right:8px;top:72px;height:36px;background:var(--scr);border-radius:var(--r-card)}
  .whlgap{flex:none;width:16px;display:flex;justify-content:center;color:var(--tfaint);font-weight:800;padding-top:81px;font-size:13px;position:relative}
  .whl{flex:1;height:180px;overflow-y:auto;scroll-snap-type:y mandatory;scrollbar-width:none;position:relative;
    -webkit-mask-image:linear-gradient(transparent,#000 26%,#000 74%,transparent);
    mask-image:linear-gradient(transparent,#000 26%,#000 74%,transparent)}
  .whl::-webkit-scrollbar{display:none}
  .whl>div{height:36px;display:flex;align-items:center;justify-content:center;scroll-snap-align:center;font-size:13px;font-weight:600;color:var(--tmut)}
  .whl>div.on{color:var(--text);font-weight:800;font-size:14px}
  .whl>div.pad{pointer-events:none}
  /* ---- BOOT SPLASH — the bobcat grades the lot; its finished strip IS the
     loading bar. Plays for humans on load; navigator.webdriver and #noboot skip
     it entirely so test runs and shoots never pay the 4 seconds. ---- */
  #bootScr{position:absolute;inset:0;z-index:60;overflow:hidden;color:#fff;
    background:linear-gradient(160deg,#2f6a3a,#24512c);
    display:flex;flex-direction:column;align-items:center;justify-content:center}
  #bootScr .bwrap{display:flex;flex-direction:column;align-items:center;gap:14px;transform:translateY(-46px)}
  /* Wes's REAL logo, animated: his skyline traced into strokes (geometry lifted
     from the logo file) so it still builds itself, then a crossfade into the
     actual PNG — the settled frame is his art. The logo lives ONLY on this boot
     screen, never in the app chrome (Wes's call). */
  #bootScr .blogo{position:relative;width:92px}
  #bootScr .blogo .realogo{width:100%;display:block;opacity:0;animation:btup .45s 1.5s ease-out forwards}
  #bootScr .blogo .trsky{position:absolute;inset:0;width:100%;height:100%;animation:bttrout .3s 1.55s ease forwards}
  #bootScr .trsky path{fill:none;stroke-width:16;stroke-linejoin:miter;
    transform:scaleY(0);transform-box:fill-box;transform-origin:bottom;
    animation:btrise .5s cubic-bezier(.34,1.5,.64,1) forwards}
  #bootScr .trsky .tb{stroke:#e6e8ea}
  #bootScr .trsky .tg{stroke:#7DBA3D}
  @keyframes btrise{to{transform:scaleY(1)}}
  @keyframes bttrout{to{opacity:0}}
  #bootScr .bword{display:flex;gap:9px;font-size:20px;font-weight:800;letter-spacing:.14em;opacity:0;transform:translateY(9px);animation:btup .5s 1.75s ease-out forwards}
  #bootScr .bword b{color:var(--lime);font-weight:800}
  #bootScr .bsub{font-size:9.5px;font-weight:800;letter-spacing:.34em;color:rgba(255,255,255,.55);opacity:0;animation:btup .5s 1.25s ease-out forwards}
  @keyframes btup{to{opacity:1;transform:translateY(0)}}
  /* status microcopy — JS feeds one span from the phrase pool (see BOOTSAY) and
     re-pops the fade on each swap. Loops forever on a slow load; the intro never
     replays. Only the bobcat + these words move after the first second. */
  #bootScr .bstat{position:absolute;bottom:172px;left:0;right:0;text-align:center;font-size:10.5px;font-weight:800;letter-spacing:.16em;color:rgba(255,255,255,.62);height:16px}
  #bootScr .bstat span{display:block;opacity:0;animation:btst 1.05s both}
  @keyframes btst{0%{opacity:0;transform:translateY(5px)}18%,80%{opacity:1;transform:none}100%{opacity:0;transform:none}}
  #bootScr .bsite{position:absolute;left:0;right:0;bottom:96px;height:86px}
  #bootScr .brough{position:absolute;left:26px;right:26px;bottom:18px;height:8px;opacity:.5;
    background:
      radial-gradient(4px 4px at 8px 6px, rgba(255,255,255,.8) 42%, transparent 46%),
      radial-gradient(3px 3px at 24px 3px, rgba(255,255,255,.6) 42%, transparent 46%),
      radial-gradient(4px 3px at 41px 6px, rgba(255,255,255,.7) 42%, transparent 46%);
    background-size:52px 8px;background-repeat:repeat-x}
  #bootScr .bgrade{position:absolute;left:26px;bottom:20px;height:4px;width:0;border-radius:var(--r-micro);
    background:linear-gradient(90deg,rgba(125,186,61,.55),var(--lime));
    box-shadow:0 0 10px rgba(125,186,61,.55);
    animation:btgrade 2.07s .95s linear forwards} /* synced: strip ends at the rear wheel */
  @keyframes btgrade{to{width:calc(100% - 52px)}}
  /* the drive is INFINITE — a slow load means more passes, never a replayed intro.
     Both ends of the pass are off-screen, so the loop jump is invisible. The graded
     strip fills once (forwards) and stays — finished work doesn't un-grade itself. */
  #bootScr .bcat{position:absolute;bottom:14px;left:-96px;width:96px;animation:btdrive 3.1s .3s linear infinite}
  @keyframes btdrive{to{transform:translateX(calc(398px + 118px))}}
  #bootScr .brig{animation:btbob .34s linear infinite}
  @keyframes btbob{0%,100%{transform:translateY(0)}25%{transform:translateY(-1.1px)}75%{transform:translateY(.7px)}}
  #bootScr .bcat svg{width:96px;height:70px;overflow:visible;display:block}
  #bootScr .bwhl{transform-box:fill-box;transform-origin:center;animation:btspin .5s linear infinite}
  @keyframes btspin{to{transform:rotate(360deg)}}
  #bootScr .bpuff{opacity:0;transform-box:fill-box;transform-origin:center;animation:btpuff 1s linear infinite}
  #bootScr .bp2{animation-delay:.33s}#bootScr .bp3{animation-delay:.66s}
  @keyframes btpuff{0%{opacity:0;transform:translate(0,0) scale(.5)}18%{opacity:.7}100%{opacity:0;transform:translate(-9px,-16px) scale(1.5)}}
  #bootScr .bdust{opacity:0;animation:btdust .42s linear infinite}
  #bootScr .bd2{animation-delay:.21s}
  @keyframes btdust{0%{opacity:0;transform:translate(0,0)}30%{opacity:.55}100%{opacity:0;transform:translate(7px,-7px)}}
  /* ---- Fern (agent) page ---- */
  .agctx{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--border);background:var(--card);border-radius:999px;padding:7px 13px;font-size:10.5px;font-weight:800;color:var(--tmut);margin:2px 0 4px}
  .agctx i{width:7px;height:7px;border-radius:50%;background:var(--lime);box-shadow:0 0 6px rgba(125,186,61,.8);flex:none}
  .agb{max-width:86%;border-radius:var(--r-card);padding:10px 13px;margin-top:9px;font-size:12.5px;line-height:1.5}
  .agb.bot{background:var(--card);border:1px solid var(--border);border-bottom-left-radius:5px}
  .agb.me{background:var(--brand);color:#fff;margin-left:auto;border-bottom-right-radius:5px}
  .agb .an{font-size:9px;font-weight:800;letter-spacing:.08em;color:var(--tfaint);margin-bottom:3px}
  .agb b{font-weight:800}
  .agsrc{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}
  .agsrc button{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--border);background:var(--scr);border-radius:var(--r-ctrl);padding:4px 8px;font:800 9.5px inherit;color:var(--tmut);cursor:pointer}
  .agsrc svg{width:11px;height:11px}
  .agtyp{display:inline-flex;gap:4px;padding:12px 14px}
  .agtyp i{width:6px;height:6px;border-radius:50%;background:var(--tfaint);animation:agty 1s infinite}
  .agtyp i:nth-child(2){animation-delay:.18s}.agtyp i:nth-child(3){animation-delay:.36s}
  @keyframes agty{0%,60%,100%{opacity:.3;transform:none}30%{opacity:1;transform:translateY(-3px)}}
  .agchips{display:flex;gap:7px;flex-wrap:wrap;margin-top:12px}
  .agchips button{border:1px solid var(--border);background:var(--card);border-radius:999px;padding:8px 13px;font:700 11.5px inherit;color:var(--text);cursor:pointer}
  .agbar{position:sticky;bottom:0;display:flex;gap:8px;align-items:center;background:var(--scr);padding:10px 0 4px;margin-top:14px}
  .agmic,.agsend{width:40px;height:40px;border-radius:var(--r-card);border:1px solid var(--border);background:var(--card);color:var(--brand);display:flex;align-items:center;justify-content:center;cursor:pointer;flex:none}
  .agmic svg,.agsend svg{width:17px;height:17px}
  .agsend{background:var(--brand);border-color:var(--brand);color:#fff}
  /* voice → text: the mic goes red and pulses; a little wave rides the input */
  .agmic.rec{background:var(--red);border-color:var(--red);color:#fff;animation:micpulse 1.1s ease-in-out infinite}
  @keyframes micpulse{0%,100%{box-shadow:0 0 0 0 rgba(194,65,46,.30)}50%{box-shadow:0 0 0 8px rgba(194,65,46,0)}}
  .agwave{display:none;gap:3px;align-items:center;position:absolute;left:60px;top:50%;transform:translateY(-50%);height:18px;pointer-events:none}
  .agwave i{width:3px;border-radius:var(--r-micro);background:var(--brand);height:7px;animation:agwv .85s ease-in-out infinite}
  .agwave i:nth-child(2){animation-delay:.12s}.agwave i:nth-child(3){animation-delay:.24s}
  .agwave i:nth-child(4){animation-delay:.36s}.agwave i:nth-child(5){animation-delay:.48s}
  @keyframes agwv{0%,100%{height:6px}50%{height:17px}}
  /* ---- SYSTEM STATES (Wes core set). One slim banner, three tones — amber =
     degraded, green = back & syncing. RED IS RESERVED FOR SAFETY ONLY. The bar
     pushes content down; it never covers anything. ---- */
  .netbar{display:none;align-items:center;gap:8px;font:800 11px 'Segoe UI',-apple-system,system-ui,sans-serif;padding:9px 14px;color:#7a4d12;background:#fdf2df;border-bottom:1px solid #eedcb2}
  .netbar.on{display:flex}
  .netbar.ok{color:#2f6a3a;background:#e9f4e4;border-color:#cfe6c6}
  .netbar svg{width:14px;height:14px;flex:none}
  .qchip{display:inline-flex;align-items:center;font:800 8.5px 'Segoe UI',-apple-system,system-ui,sans-serif;letter-spacing:.05em;color:#a06322;background:rgba(224,150,60,.14);border:1px solid rgba(224,150,60,.4);border-radius:99px;padding:2px 7px;margin-left:6px}
  /* permission ask = ONE calm card, one live button — never a dead control */
  .permcard{display:flex;flex-direction:column;align-items:center;text-align:center;gap:8px;background:var(--card);border:1px solid var(--border);border-radius:var(--r-card);padding:26px 20px;margin:40px 6px}
  .permcard .pcic{width:44px;height:44px;border-radius:var(--r-card);background:var(--scr);display:flex;align-items:center;justify-content:center;color:var(--brand)}
  .permcard .pcic svg{width:20px;height:20px}
  .permcard b{font-size:14.5px}
  .permcard>span:not(.pcic){color:var(--tmut);font-size:12px;line-height:1.5;max-width:250px}
  .permcard .submit{margin-top:8px}
  .ntfoff{border-color:rgba(224,150,60,.5)!important;background:#fdf6ea!important}
  /* the ONLY two full screens in the app: forced update + access changed */
  .sysfull{position:absolute;inset:0;z-index:60;display:none;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:34px;background:linear-gradient(160deg,#2f6a3a,#24512c);color:#fff}
  .sysfull.on{display:flex}
  .sysfull .sfmk{color:var(--lime)}
  .sysfull .sfmk svg{width:46px;height:46px}
  .sysfull h6{font-size:20px;font-weight:800;margin:14px 0 0}
  .sysfull .sfsub{color:rgba(255,255,255,.65);font-size:12.5px;line-height:1.55;max-width:260px;margin:10px 0 24px}
  .sysfull .submit{width:min(260px,100%)}
  /* payroll rows */
  .ratebox{display:inline-flex;align-items:center;gap:2px;border:1px solid var(--strong);background:var(--card);border-radius:var(--r-card);padding:7px 9px;font-size:12.5px;font-weight:800;color:var(--tmut)}
  .ratebox input{border:0;outline:0;width:52px;font:800 12.5px 'Segoe UI',-apple-system,system-ui,sans-serif;color:var(--text);background:transparent}
  .ratebtn{margin-left:auto;border:1px solid var(--border);background:var(--scr);border-radius:var(--r-card);padding:8px 11px;font:800 12.5px 'Segoe UI',-apple-system,system-ui,sans-serif;color:var(--text);cursor:pointer}
  .ratebtn small{font-size:9px;color:var(--tfaint);font-weight:800;margin-left:2px}
  .rated{margin-left:auto;font:800 12.5px 'Segoe UI',-apple-system,system-ui,sans-serif;color:var(--text);padding:8px 2px}
  .rated small{font-size:9px;color:var(--tfaint);font-weight:800;margin-left:2px}
  /* home address: enter → CONFIRM → text + pencil, the time-edit manners */
  .homeline{display:flex;align-items:center;gap:8px;flex-basis:100%;margin:8px 0 2px}
  .homeline .inp{flex:1;margin:0}
  #homeTxt{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:700}
  .hpen{border:1px solid var(--border);background:var(--scr);border-radius:var(--r-card);width:36px;height:32px;display:flex;align-items:center;justify-content:center;color:var(--tmut);cursor:pointer;flex:none}
  .hpen svg{width:14px;height:14px}
  .hok{border:none;background:var(--brand);color:#fff;border-radius:var(--r-card);padding:0 14px;height:34px;font:800 12.5px 'Segoe UI',-apple-system,system-ui,sans-serif;cursor:pointer;flex:none}
  /* company name keeps ONE thin line when set — name + a smaller pencil at the far
     right, no second row (Wes: "reduce the vertical space it takes up") */
  .coline{display:flex;align-items:center;gap:7px;margin-left:auto;min-width:0}
  .coline span{font-size:12.5px;font-weight:700;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .coline .hpen{width:30px;height:28px;border-radius:var(--r-ctrl)}
  .coline .hpen svg{width:12px;height:12px}
  .rateedit{display:inline-flex;gap:6px;align-items:center;margin-left:auto}
  /* 56px, THE HOUSE FLOOR FOR A GLOVED HAND — the same number #pwOpen, #delOpen, .peracts and
     the auth boxes were all raised to. These two are the ✓ and the ✕ that COMMIT or cancel a
     wage change and a role change, i.e. the buttons at the end of the only in-app road a
     one-owner company has to deleting its account, and they measured 34x34 — the identical
     number the touch-target class was first logged for, on the show-password eye
     (EVAL-LEDGER 2026-08-17, 4th occurrence). The room was measured and the door was not.
     Sized as a flex box rather than a bare width/height so the glyph stays centred at 56. */
  .rc{width:56px;height:56px;display:inline-flex;align-items:center;justify-content:center;
    border-radius:var(--r-card);border:1px solid var(--border);background:var(--card);font-size:16px;font-weight:800;cursor:pointer;color:var(--tmut);flex:none}
  .rc.ok{background:var(--lime);border-color:var(--lime);color:#153313}
  /* The box they sit beside grows with them: a 33px input next to a 56px button is the row
     admitting the two were sized by different rules. min-height only — the padding and the
     type are unchanged. */
  .rateedit .ratebox{min-height:56px}
  /* THE PERSON EDITOR (W1 1.4) — the SAME enter-then-CONFIRM furniture the wage editor
     uses, widened. A wage is 5 characters and fits beside its row; an email address is 30
     and cannot, so .wide drops the editor onto its own full-width line inside the same
     wrapping .prow rather than squeezing the field to nothing. Nothing new is invented:
     .ratebox / .rc / .rc.ok are the payroll editor's own classes. */
  /* flex-wrap + row-gap so the CHIP BLOCK below can claim a whole line and put the ✓/✕ under it.
     row-gap rather than a margin because it only pays out BETWEEN lines: the .ratebox editors
     that share this class stay one line and are untouched by it. */
  .rateedit.wide{display:flex;flex-wrap:wrap;row-gap:9px;flex-basis:100%;margin-left:0;margin-top:9px}
  .ratebox.wide{flex:1;min-width:0}
  .ratebox.wide input{width:100%;flex:1;min-width:0}
  /* THE CHIPS WRAP, AND THEY WRAP ACROSS THE WHOLE CARD. This comment claimed the wrap for
     months and the rule did not implement it — .kindchips is display:flex with no flex-wrap, so
     a chip row too wide for its box simply overflowed. Three chips fitted, so nobody found out.
     MEASURED the moment the fourth (Owner) arrived at 390px: Manager · Foreman · Laborer · Owner
     pushed the ✕ clean off the right edge, i.e. adding a role took away the way OUT of the
     editor. flex-wrap fixed the overflow — and left a second shape behind it, which is what
     flex-basis:100% closes now.
     WHY THE CHIPS GET THE WHOLE LINE. Sharing the line with the two 56px buttons leaves the
     block ~212px of a 390px screen, so at SEVEN chips — the pay-week anchor picker, which is the
     widest set this editor ever draws — the days came out two-wide over FOUR rows, 245px tall,
     with a large empty gutter beside rows 1, 3 and 4 and the ✓/✕ stranded halfway down beside
     row 2 (EVAL-LEDGER 2026-08-17, `touch-target` NIT; shots R3-16 / R3-17). The chips are the
     content of this editor; the confirm pair is its footer. So the chips take the full width and
     the buttons drop underneath, which is the same order the sentence under them already reads
     in. The four-chip role picker gets the same treatment and the same benefit — one row instead
     of two — and every 56px floor is untouched, because nothing here changes a size. */
  .rateedit.wide .kindchips{flex:1;flex-basis:100%;min-width:0;margin:0;flex-wrap:wrap;row-gap:7px}
  /* AND THE CHIPS ARE THE SAME SIZE AS THE BUTTONS THAT COMMIT THEM. Measured at 390px they
     were 66-78 wide by 33 TALL, next to a ✓ and a ✕ now at 56 — a role picker whose targets
     were half the house floor, on the road the sole-owner refusal sends a one-owner company
     down. Scoped to THIS editor: .kc is the app's general chip and also carries the break
     picker, the nav picker and the crew picker, which are laid out around their own sizes; the
     class this closes is about the controls on the person page. min-height (not height) so a
     label that wraps in another install's copy grows the chip instead of spilling out of it,
     and the flex box keeps the label centred either way. */
  .rateedit.wide .kindchips .kc{min-height:56px;display:inline-flex;align-items:center;
    justify-content:center;padding:0 18px;font-size:13px}
  .perval{color:var(--tmut);font-size:10px;margin-top:2px;line-height:1.35;overflow-wrap:anywhere}
  .perval a{color:var(--copper);text-decoration:none;font-weight:800}
  /* "no email — can't sign in" and "invited, hasn't signed in yet". Amber, never red: it is
     a state to fix, not a destructive action, and red is reserved for safety in this app. */
  .perval.warn{color:#7a4d12;font-weight:800}
  /* Two actions on a sign-in row go BELOW the sentence, full width, instead of squeezing it
     into a ~170px column where it wraps to six lines. flex-basis:100% makes the parent .prow
     (which already wraps) break the line for us. */
  .peracts{display:flex;gap:8px;flex-basis:100%;margin-top:10px}
  .peracts .ratebtn{margin-left:0;flex:1}
  /* THE FLOOR IS ON THE BUTTON, NOT ON THE STRIP (2026-08-17). It lived on the strip
     (`.peracts .ratebtn{min-height:44px}`) — and views-team.js hands a LONE remediation
     button back to the row instead of the strip, so the same control measured 164x44 beside a
     sibling and 107x34 by itself. The knob that decides how many there are is
     accessCodeEnrollment, i.e. the default install ships the small one, on the ONE road a new
     hire has into the app (EVAL-LEDGER 2026-08-17, `touch-target`, 5th occurrence — and its
     own standing note is "the room was measured and the door was not").
     Sized where a thumb actually lands, so no parent, no sibling count and no config value
     can take it away. 56 is the house floor for a gloved hand — the same number .rc, #pwOpen,
     #delOpen and the auth boxes carry — and the padding grows with it so the label stays
     centred rather than floating in a tall box. Every one of these buttons is built by
     views-team.js signBtn(), which is what makes the class impossible to forget. */
  .ratebtn.signbtn{min-height:56px;padding:10px 14px}
  #bootScr.done{animation:btlift .55s ease-in forwards}
  @keyframes btlift{to{opacity:0;transform:translateY(-16px) scale(1.02);visibility:hidden}}
  @media (prefers-reduced-motion: reduce){
    #bootScr *{animation:none!important}
    #bootScr .trsky{display:none}#bootScr .blogo .realogo{opacity:1}
    #bootScr .bword{opacity:1;transform:none}
    #bootScr .bstat,#bootScr .bsite{display:none}
  }
  .mych{display:flex;align-items:center;gap:7px;margin-top:9px;font-size:11px;font-weight:700;color:var(--tmut)}
  .mych b{color:var(--text)}
  .mych svg{width:14px;height:14px;color:var(--brand)}
  /* exports */
  .qbchip{margin-left:auto;font-size:8.5px;font-weight:800;background:rgba(125,186,61,.16);color:#4d7a1f;border-radius:var(--r-ctrl);padding:3px 8px;letter-spacing:.05em}
  .xrow{display:flex;gap:8px;margin-top:7px}
  .xbtn{flex:1;border:1px solid var(--strong);background:var(--card);border-radius:var(--r-card);font:inherit;font-weight:800;font-size:11.5px;color:var(--text);padding:11px 8px;cursor:pointer;line-height:1.3}
  .xbtn small{display:block;font-weight:700;font-size:9px;color:var(--tmut);margin-top:2px}
  .aitem .pchip{margin-left:auto;font-size:9.5px;font-weight:800;border:1px solid var(--border);background:var(--scr);color:var(--tmut);border-radius:var(--r-ctrl);padding:3px 7px;white-space:nowrap}

/* ================= INTEGRATIONS (v-intg) — NOT from the mockup =============================
   The mockup has no Integrations screen; this page is W1 1.2 and its rules are written
   against the existing token set (--card/--border/--brand/--tmut), never new colours.

   .istat is THE honest-status element and its three tones are the point: .on = configured,
   .off = configured-and-empty, .bad = THE LOAD FAILED. The third exists because "couldn't
   reach the server" must never be painted as "not configured" — one is a network problem,
   the other is a statement about this install's security posture, and rendering the first as
   the second tells the owner his connector is off when it may be wide open.

   .ival wraps and breaks anywhere: these are 64 hex characters on a 390px phone, and a value
   that overflows its card is a value that can't be read back to check what was pasted. */
.icard{border:1px solid var(--border);background:var(--card);border-radius:var(--r-card);padding:13px 14px;margin-top:7px}
.icard .pt{font-weight:800;font-size:12.5px}
.icard .ps{color:var(--tmut);font-size:10.5px;margin-top:3px;line-height:1.45}
.icard textarea.inp{margin-top:10px;font-size:13px}
.istat{margin-top:10px;font-weight:800;font-size:11px;line-height:1.4;border-radius:var(--r-ctrl);padding:8px 10px;
       background:var(--scr);border:1px solid var(--border);color:var(--tmut)}
.istat.on{background:rgba(47,106,58,.10);border-color:rgba(47,106,58,.24);color:var(--brand)}
.istat.off{background:var(--scr);border-color:var(--border);color:var(--tmut)}
.istat.bad{background:#fdf2df;border-color:#eedcb2;color:#7a4d12}
.istat.wait{color:var(--tfaint)}
.istat:empty{display:none}
.ibtns{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
.ibtn{border:1px solid var(--strong);background:var(--card);color:var(--text);border-radius:var(--r-card);
      font:800 11.5px 'Segoe UI',-apple-system,system-ui,sans-serif;padding:10px 13px;cursor:pointer;min-height:44px}
.ibtn.go{background:var(--lime);border-color:var(--lime);color:#16240b}
.ibtn.rv{color:#c2412e;border-color:rgba(194,65,46,.4)}
.ibtn.cp{margin-top:9px;width:100%}
/* the SHOW-ONCE block. Amber, not green: this is a "act now, it will not come back" state,
   and red is reserved for safety in this app. */
.irev{margin-top:10px;border:1px solid #eedcb2;background:#fdf2df;border-radius:var(--r-card);padding:11px 12px;color:#7a4d12}
.irev>b{display:block;font-size:11.5px;margin-bottom:8px}
.ikv{display:flex;gap:8px;align-items:flex-start;margin-top:6px;font-size:10.5px}
.ikv>span{flex:none;width:52px;font-weight:800;color:#a06322;text-transform:uppercase;letter-spacing:.05em;font-size:9px;padding-top:2px}
.ikv code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:#5c3a0d;
          word-break:break-all;overflow-wrap:anywhere;min-width:0}
.ihint{margin-top:9px;font-size:10px;line-height:1.5;color:#8a5a17}
.ihint code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:9.5px}
.ijrnl{margin-top:10px;max-height:260px;overflow:auto;white-space:pre-wrap;word-break:break-word;
       background:var(--scr);border:1px solid var(--border);border-radius:var(--r-card);padding:10px 11px;
       font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10.5px;color:var(--text);
       overscroll-behavior:contain}

/* ================= BUSINESS RULES (v-rules) — W1 1.6 ======================================
   ONE new class, and it earns its place: .cfgsay is the sentence a change prints BEFORE it is
   committed, on the two keys that are payroll money (weekStartDay, overtimeMultiplier). It is
   NOT .istat.bad wearing a different name — .istat's three tones are claims about what IS, and
   this is a claim about what WOULD BE if the next tap lands. Amber for the same reason .irev is
   amber: this app reserves red for safety, and amber is "read this before you continue".
   flex-basis:100% so it drops onto its own line inside the wrapping .prow rather than fighting
   the confirm buttons for the same row on a 390px phone. */
.cfgsay{flex-basis:100%;margin-top:9px;border-radius:var(--r-ctrl);padding:8px 10px;
        background:#fdf2df;border:1px solid #eedcb2;color:#7a4d12;
        font-weight:800;font-size:10.5px;line-height:1.45;overflow-wrap:anywhere}
/* The current value, stated plainly next to the control that changes it. Same size and colour
   as .perval (the person editor's value line) because it is the same idea — what is on file
   right now, in words, so nobody has to infer it from a switch position. */
.cfgnow{color:var(--tmut);font-size:10px;margin-top:2px;line-height:1.35;overflow-wrap:anywhere}
.cfgnow b{color:var(--text);font-weight:800}

/* ================= PORT HARDENING — carried forward from the old public/styles.css =========
   Every rule below is a fix that cost real debugging on a real Galaxy A12. None is cosmetic. */

/* --- [hidden] must always win. The UA default is unlayered and loses to ANY author display
   rule (.ghost, .hh button, a flexed row) — which is how a permission-gated control ships
   hidden in markup and still paints. The old styles.css carried this for exactly that
   reason; R1's port missed it, and the time-off Request button was the proof (finding #7:
   visible and TAPPABLE for the whole whoami round trip on a reduced-motion phone).
   Enforced here once so it can't recur element by element. --- */
[hidden]{display:none !important}

/* --- safe areas. viewport-fit=cover in index-v2.html is what makes env() resolve to anything
   other than 0px. Without the .nav rule the bottom tab bar sits UNDER Android 15's gesture
   pill: swiping up to hit "Jobs" fires the system home gesture instead, and the app's primary
   navigation is unreachable. --- */
.abar{padding-top:calc(10px + env(safe-area-inset-top,0px))}
.nav{padding-bottom:calc(20px + env(safe-area-inset-bottom,0px))}
.content{padding-bottom:calc(96px + env(safe-area-inset-bottom,0px))}
.sheet{padding-bottom:calc(26px + env(safe-area-inset-bottom,0px))}
#onbScr .onb{padding-bottom:calc(26px + env(safe-area-inset-bottom,0px))}
/* the two full-screen takeovers must clear both insets — their button is the only way out */
.sysfull{padding-top:calc(34px + env(safe-area-inset-top,0px));padding-bottom:calc(34px + env(safe-area-inset-bottom,0px))}

/* --- overscroll containment on every nested scroller. A scroller that reaches its end and
   chains the gesture to the document is a pull-to-refresh waiting to happen. --- */
.content{overscroll-behavior:contain}
.sheetovl{overscroll-behavior:contain}
.sheet{max-height:88vh;max-height:88dvh;overflow-y:auto;overscroll-behavior:contain}
#jobList{overscroll-behavior:contain}
.whl{overscroll-behavior:contain}
#onbScr{overscroll-behavior:contain}
#agThread{overscroll-behavior:contain}
/* horizontal scrollers: contain the x-axis so a sideways flick on a chip row can't rubber-band
   the page or trigger Chrome's back-navigation gesture. */
.cats,.catbars,.rfilters,.qkrow,.kindchips,.tags,.thumbs{overscroll-behavior-x:contain}

/* --- 44px is the gloved-thumb floor, measured on a Galaxy A12 by test/android.cjs.
   In-flow controls simply grow. Icon buttons keep their DRAWN size and grow only their HIT
   AREA via a ::before overlay, so the layout the mockup specifies is preserved exactly. --- */
.nv,.ckbtn,.brkbtn,.submit,.cat,.tag,.rback,.invite,.signout,.xbtn,.hok,.thadd,
.relink,.adjout,.agsend,.agmic,.ghost{min-height:44px}
.nv{min-width:44px}
.cat,.tag{display:inline-flex;align-items:center}
.rback{align-items:center}

.sx,.hpen,.calnav,.privx,.camx,.sw,.flashbtn,.norollbtn{position:relative}
.sx::before,.hpen::before,.calnav::before,.privx::before,.camx::before,
.sw::before,.flashbtn::before,.norollbtn::before{
  content:'';position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:max(100%,44px);height:max(100%,44px);
}

/* --- the mockup's cursor:pointer implies a mouse. Kill the tap-highlight flash and
   double-tap zoom on controls, which read as lag in gloves. --- */
button,.nv,.cat,.tag,.sw,.trow,.prow{-webkit-tap-highlight-color:transparent;touch-action:manipulation}

/* --- the mockup animates the boot splash unconditionally. Respect the OS switch. --- */
@media (prefers-reduced-motion:reduce){
  *{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}
}
