
:root{
  color-scheme: light;
  --bg:#f2f5f9;
  --ink:#0e1726; --ink2:#46536b; --ink3:#334155;
  --sub:#768397; --meta:#97a1b5; --icon-dim:#c2cad8; --nav-idle:#9aa5b8;
  --line:rgba(15,23,42,.07);
  --blue:#2c5ce5; --blue2:#1a3fb0; --blue-bg:#eaf0ff;
  --green:#16a34a; --green-bg:#e7f8ee;
  --amber:#d97706; --amber-ink:#92600b; --amber-meta:#a16207; --amber-bg:#fef3c7; --amber-soft:#fff9ed; --amber-bd:#fde8bf;
  --red:#ef4444; --zone:#eab308; --off:#c3ccda;
  --glass:rgba(255,255,255,.72); --glass-hi:rgba(255,255,255,.86); --glass-bd:rgba(255,255,255,.65);
  --card-sh:0 3px 14px -6px rgba(15,25,45,.14);
  --safe-top:env(safe-area-inset-top,0px); --safe-bot:env(safe-area-inset-bottom,0px);
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html{height:100%}
/* v1.177.0 (Alex): "Holding down on screen elements shouldn't allow select. Eg. When I held
   down my location button on the map to pin location. It selected the whole page." Nothing
   here had ever set user-select, so a long press did what a long press does on any web page
   - and v1.173.0's hold-to-drop-a-pin made that a gesture people are TOLD to perform. The
   chrome is unselectable and the iOS callout is off; selection is handed back below to the
   places where copying the text is the whole point. */
body{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;margin:0;padding:0;-webkit-text-size-adjust:100%;min-height:100vh;min-height:100dvh;min-height:var(--app-h,100dvh);touch-action:manipulation;color:var(--ink);font-family:'Plus Jakarta Sans',system-ui,-apple-system,sans-serif;-webkit-font-smoothing:antialiased;overflow:hidden;background:var(--bg)}
body::before{content:'';position:fixed;inset:0;background:radial-gradient(ellipse 70% 46% at 18% -8%,rgba(44,92,229,.10),transparent 60%),radial-gradient(ellipse 60% 40% at 96% 108%,rgba(227,32,42,.07),transparent 60%);pointer-events:none;z-index:0}
button{font-family:inherit}
::-webkit-scrollbar{width:0;height:0}
.tn-scroll{-ms-overflow-style:none;scrollbar-width:none}
@keyframes tnpulse{0%{transform:scale(1);opacity:.45}70%{transform:scale(2.4);opacity:0}100%{opacity:0}}
@keyframes tnsheet{from{transform:translateY(100%)}to{transform:translateY(0)}}
@keyframes tnpop{from{opacity:0;transform:scale(.94) translateY(6px)}to{opacity:1;transform:scale(1) translateY(0)}}
@keyframes tnfade{from{opacity:0}to{opacity:1}}
@keyframes spin{to{transform:rotate(360deg)}}

/* ===== glass card (TNFM light glass over template geometry) ===== */
.card{background:var(--glass);backdrop-filter:blur(20px) saturate(160%);-webkit-backdrop-filter:blur(20px) saturate(160%);border:1px solid var(--glass-bd);border-radius:22px;padding:18px 16px;box-shadow:var(--card-sh);margin-bottom:16px}
.card.tap{cursor:pointer;transition:transform .12s}
.asn-head{cursor:pointer;transition:transform .12s}
.asn-head:active{transform:scale(.99)}
.asn-acc{max-height:0;overflow:hidden;transition:max-height .3s cubic-bezier(.22,.61,.36,1)}
.asn-card.exp>.asn-head{border-bottom:1px solid var(--line);padding-bottom:14px;margin-bottom:2px}
.card.tap:active{transform:scale(.99)}
.tile{background:linear-gradient(135deg,rgba(255,255,255,.46) 0%,rgba(255,255,255,.1) 45%,transparent 72%),rgba(112,126,148,.24);backdrop-filter:blur(29px) saturate(200%) brightness(1.06);-webkit-backdrop-filter:blur(29px) saturate(200%) brightness(1.06);border-radius:18px;padding:11px 13px;box-shadow:inset 0 1px 0 rgba(255,255,255,.35)}
.tile .k{font-size:10px;color:var(--meta);font-weight:700}
.tile .v{font-size:14px;color:var(--ink);font-weight:700;margin-top:3px}
.sect{display:flex;align-items:center;justify-content:space-between;margin:4px 2px 10px}
.sect .h{font-size:15px;font-weight:800;color:var(--ink)}
.sect .lnk{font-size:12px;font-weight:700;color:var(--blue);cursor:pointer}
.chip{display:inline-flex;align-items:center;font-size:11px;font-weight:700;padding:5px 10px;border-radius:999px;white-space:nowrap}
.chip.ok{color:var(--green);background:rgba(22,163,74,.14)}
.chip.warn{color:var(--amber-meta);background:rgba(217,119,6,.16)}
.chip.blue{color:var(--blue);background:rgba(44,92,229,.13)}
.chip.dim{color:#64748b;background:rgba(100,116,139,.14)}
/* v1.178.0: the payroll row now carries four short chips, so they wrap to the right rather
   than squeezing the name column. .pay-rung is the n/6 ladder position. */
.pay-tags{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:4px;flex-shrink:0;max-width:56%}
/* v1.219.0 (TWOCV-2026-08-25-tnp-019, F1/P0): the base rule above is UNCHANGED (>=768px
   keeps its exact pre-fix layout) - this query only fires <768px, where a uniform
   max-width percentage cannot free enough room for the name without overflowing the
   widest single chip (measured this session - see build script docstring). Letting tags
   wrap onto its own full-width line avoids that tradeoff entirely: the name (line 1,
   alongside checkbox/index/avatar) gets the row's full remaining width via its existing
   flex:1, and tags (line 2) gets the full row width to lay out its chips in, so even the
   widest realistic chip has room. padding-left:111px aligns the chip row under the name
   text (skipping the checkbox+index+avatar columns: 18+11+22+11+38+11=111, matching their
   own widths/gaps above) rather than flush against the row's left edge. */
@media (max-width:767px){
.pay-row{flex-wrap:wrap}
.pay-tags{flex-shrink:1;flex-basis:100%;max-width:100%;justify-content:flex-start;padding-left:111px}
}
.pay-tags .chip{font-size:10.5px;padding:4px 8px}
.pay-rung{font-style:normal;font-weight:800;opacity:.6;margin-left:5px;font-size:9.5px}
/* v1.182.0: the error dashboard. Operator UI - dense, monospaced where it is machine text,
   and deliberately plain: this card is read when something is wrong. */
.err-hd{display:flex;align-items:center;gap:8px;margin-bottom:8px}
.err-hd h3{font-size:15px;font-weight:800;color:var(--ink);margin:0;flex:1}
.err-tiles{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:10px}
.err-tile{background:rgba(118,131,151,.08);border-radius:12px;padding:9px 10px}
.err-tile .k{font-size:9.5px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--meta)}
.err-tile .v{font-size:19px;font-weight:800;color:var(--ink);line-height:1.15;margin-top:2px}
.err-spark{display:flex;align-items:flex-end;gap:2px;height:34px;margin-bottom:10px}
.err-spark i{flex:1;background:rgba(227,32,42,.55);border-radius:2px 2px 0 0;min-height:2px;display:block}
.err-spark i.zero{background:rgba(118,131,151,.18)}
.err-row{display:flex;gap:8px;align-items:flex-start;padding:8px 2px;border-bottom:1px solid rgba(118,131,151,.14);cursor:pointer}
.err-row .t{font-size:10.5px;color:var(--meta);font-weight:700;flex:0 0 52px;font-variant-numeric:tabular-nums}
.err-row .m{flex:1;min-width:0}
.err-row .a{font-size:12.5px;font-weight:800;color:var(--ink)}
.err-row .d{font-size:11.5px;color:var(--sub);word-break:break-word}
.err-row .w{font-size:10.5px;color:var(--meta);font-weight:600;margin-top:1px}
.err-trace{background:rgba(118,131,151,.08);border-radius:10px;padding:8px 10px;margin:4px 0 8px;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10.5px;line-height:1.5;
  color:var(--ink2);white-space:pre-wrap;word-break:break-word}
.err-rate{font-size:10.5px;font-weight:800;padding:2px 6px;border-radius:6px;white-space:nowrap}
.err-rate.bad{background:rgba(227,32,42,.14);color:#b3261e}
.err-rate.warn{background:rgba(217,119,6,.16);color:var(--amber-meta)}
.err-rate.dim{background:rgba(118,131,151,.14);color:var(--meta)}
.chip.err{color:#dc2626;background:rgba(220,38,38,.12)}
.chip.up{text-transform:uppercase;letter-spacing:.4px;padding:5px 11px}

.va-x{border:0;background:transparent;padding:6px;margin-left:2px;color:var(--meta);display:flex;align-items:center;cursor:pointer}
.va-x svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2.4;stroke-linecap:round}
/* ===== impersonation banner ===== */
#asBanner{display:none;background:linear-gradient(90deg,#0f2a52,#17356a);color:#fff;text-align:center;padding:7px 14px 8px;font-weight:800;font-size:11px;text-transform:uppercase;letter-spacing:.1em;cursor:pointer}
body.impersonating #asBanner{display:block}
body.impersonating .view{padding-top:calc(114px + var(--safe-top))}

/* ===== views ===== */
.view{position:fixed;top:0;left:0;right:0;height:var(--app-h,100dvh);padding:calc(84px + var(--safe-top)) 14px calc(172px + var(--safe-bot));overflow-y:auto;-webkit-overflow-scrolling:touch;display:none;z-index:10}
.view.active{display:block}
#vSites{padding:0}

/* ===== HOME ===== */
#topBand{position:fixed;top:0;left:0;right:0;z-index:1500;background:#E3202A;padding-top:var(--safe-top);box-shadow:0 8px 22px -12px rgba(150,12,22,.6)}
.tb-main{display:flex;align-items:center;justify-content:space-between;padding:10px 16px 13px;min-height:44px;gap:12px}
.tb-left{flex:1;min-width:0}
.tb-actions{display:flex;align-items:center;gap:10px;flex-shrink:0}
.tb-slot{display:none}
body[data-view=home] .tb-home{display:block}
body[data-view=sites] .tb-sites{display:flex;align-items:center}
body[data-view=sheets] .tb-sheets{display:flex;align-items:center;gap:10px}
.tb-title{font-size:22px;font-weight:800;color:#fff;letter-spacing:-.01em}
.greet .g1{font-size:13px;color:rgba(255,255,255,.78);font-weight:600;line-height:1}
.greet .g2{font-size:22px;color:#fff;font-weight:800;margin-top:4px}
.rnd-btn{position:relative;width:44px;height:44px;border-radius:50%;background:var(--glass-hi);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--glass-bd);display:flex;align-items:center;justify-content:center;box-shadow:0 2px 8px -3px rgba(15,25,45,.18);cursor:pointer}
.rnd-btn svg{width:20px;height:20px;stroke:var(--ink3);fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.rnd-btn.spin svg{animation:spin 1s linear infinite}
.avatar{width:44px;height:44px;border-radius:50%;background:linear-gradient(135deg,#2c5ce5,#1a3fb0);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:15px;box-shadow:0 6px 14px -4px rgba(44,92,229,.55);cursor:pointer}
.week{display:grid;grid-template-columns:repeat(7,1fr);gap:4px}
.wd{display:flex;flex-direction:column;align-items:center;gap:7px;padding:8px 0 6px;border-radius:14px}
.wd .dow{font-size:11px;font-weight:600;color:var(--meta)}
.wd .num{font-size:15px;font-weight:700;color:var(--ink)}
.wd .dot{width:5px;height:5px;border-radius:50%;background:transparent}
.wd.today{background:var(--blue)}
.wd.today .dow{color:rgba(255,255,255,.75)}
.wd.today .num{color:#fff}
.wd.today .dot{background:#fff}
.wd.worked .dot{background:var(--green)}
.wd.sched .dot{background:var(--blue)}
.wd.today.worked .dot,.wd.today.sched .dot{background:#fff}
/* v1.195.0: tappable days + week nav */
.wd{cursor:pointer}
.wd.sel{box-shadow:inset 0 0 0 2px var(--blue)}
.wd.today.sel{box-shadow:inset 0 0 0 2px rgba(255,255,255,.65)}
.cal-nv{width:24px;height:24px;border-radius:50%;border:1px solid var(--line);background:rgba(14,23,38,.03);color:var(--sub);font-size:15px;font-weight:800;line-height:1;display:flex;align-items:center;justify-content:center;cursor:pointer;padding:0 0 2px}
/* v1.198.0: calendar items - time on top of the hero, grouped pickups, per-worker actions */
.cal-grp{padding:10px 2px 12px;border-bottom:1px solid rgba(14,23,38,.06)}
.cal-grp:last-child{border-bottom:0}
.cal-tap{cursor:pointer}
.cal-time{display:flex;align-items:center;gap:6px;font-size:12px;font-weight:800;letter-spacing:.02em;color:var(--blue)}
.cal-car{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex:0 0 auto;vertical-align:-3px}
.cal-time .cal-car{stroke:var(--blue)}
.cal-hero{font-size:15px;font-weight:800;color:var(--ink);margin-top:3px;line-height:1.25}
.cal-meta{font-size:12px;font-weight:600;color:var(--meta);margin-top:2px}
.cal-body{display:flex;align-items:flex-start;gap:11px}
.cal-ic{width:38px;height:38px;flex:none}
.cal-ic .cal-car{width:22px;height:22px;stroke:var(--blue)}
.cal-main{flex:1;min-width:0}
/* v1.202.0 (Alex): "a line like the quote line on email replies and small hollow bullets
   on the line so we can see the separation of the pickupees" */
.cal-wlist{border-left:2px solid var(--line);margin:9px 0 2px 5px;padding-left:16px}
.cal-wrow{display:flex;align-items:center;gap:8px;margin-top:9px;position:relative}
.cal-wrow:first-child{margin-top:0}
.cal-wrow::before{content:'';position:absolute;left:-21px;top:50%;transform:translateY(-50%);width:8px;height:8px;border-radius:50%;border:2px solid var(--meta);background:#fff;box-sizing:border-box}
.cal-wmain{flex:1;min-width:0}
.cal-wskill{font-size:11px;font-weight:700;color:var(--meta);letter-spacing:.02em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* v1.201.0 (Alex): "Let's try not bolding the pickupees. It's melded into a blob." */
.cal-wname{font-size:14.5px;font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cal-act{width:34px;height:34px;border-radius:50%;background:rgba(118,131,151,.14);display:flex;align-items:center;justify-content:center;text-decoration:none;flex:0 0 auto}
.cal-act svg{width:16px;height:16px;stroke:var(--sub);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.asn-top{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
.asn-l{display:flex;gap:13px;min-width:0}
.asn-ic{width:44px;height:44px;border-radius:14px;background:var(--blue-bg);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.asn-ic svg{width:22px;height:22px;stroke:var(--blue);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.asn-site{font-size:16px;font-weight:800;color:var(--ink)}
.asn-sub{font-size:13px;color:var(--sub);font-weight:500;margin-top:2px}
.asn-meta{display:flex;gap:20px;margin-top:15px;padding-top:15px;border-top:1px solid var(--line)}
.asn-meta .m .k{font-size:11px;color:var(--meta);font-weight:600;margin-bottom:3px}
.asn-meta .m .v{font-size:14px;color:var(--ink);font-weight:700}
.asn-meta .m .v.ok{color:var(--green)}
.prog{height:10px;border-radius:999px;background:#eef1f6;overflow:hidden}
.prog i{display:block;height:100%;border-radius:999px;background:linear-gradient(90deg,#2c5ce5,#4f8ef0);transition:width .4s ease}
.stat3{display:flex;justify-content:space-between;margin-top:12px}
.stat3 .s{text-align:center}
.stat3 .n{font-size:17px;font-weight:800;color:var(--ink)}
.stat3 .c{font-size:11px;color:var(--meta);font-weight:600}
.tl-row{display:flex;align-items:center;gap:12px;padding:10px 2px}
.tl-row + .tl-row{border-top:1px solid var(--line)}
.tl-dot{width:10px;height:10px;border-radius:50%;flex:0 0 10px}
.tl-dot.in{background:var(--green)} .tl-dot.lo{background:var(--amber)} .tl-dot.li{background:var(--blue)} .tl-dot.out{background:var(--red)}
.tl-dot.open{background:var(--green);position:relative}
.tl-dot.open::after{content:'';position:absolute;inset:0;border-radius:50%;background:var(--green);animation:tnpulse 1.8s ease-out infinite}
.tl-lbl{font-size:14px;font-weight:600;color:var(--ink);flex:1}
.tl-t{font-size:13px;font-weight:700;color:var(--ink2);font-variant-numeric:tabular-nums}
.empty{padding:16px 4px;color:var(--meta);font-size:13px;font-weight:500;text-align:center}
.up-row{background:var(--glass);backdrop-filter:blur(20px) saturate(160%);-webkit-backdrop-filter:blur(20px) saturate(160%);border:1px solid var(--glass-bd);border-radius:18px;padding:14px 16px;box-shadow:0 3px 14px -6px rgba(15,25,45,.12);display:flex;align-items:center;gap:14px;margin-bottom:10px}
.up-date{width:46px;height:46px;border-radius:14px;display:flex;flex-direction:column;align-items:center;justify-content:center;flex-shrink:0}
.up-date .d1{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.4px}
.up-date .d2{font-size:16px;font-weight:800;line-height:1}
.up-b .s{font-size:14px;font-weight:700;color:var(--ink)}
.up-b .t{font-size:12px;color:var(--sub);font-weight:500;margin-top:2px}
.chev{width:18px;height:18px;stroke:var(--icon-dim);fill:none;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}

/* ===== SITES (map) ===== */
#map{position:absolute;inset:0;z-index:1;background:#dfe6ee}
.leaflet-container{background:#dfe6ee;font-family:'Plus Jakarta Sans',sans-serif}
.leaflet-control-attribution{background:rgba(255,255,255,.85)!important;color:rgba(15,23,42,.55)!important;font-size:9px!important}
.leaflet-control-attribution a{color:rgba(15,23,42,.75)!important}
.map-fade{position:absolute;inset:0;z-index:2;pointer-events:none;background:linear-gradient(180deg,rgba(223,230,238,.55) 0%,rgba(223,230,238,0) 18%,rgba(223,230,238,0) 55%,rgba(242,245,249,.35) 100%)}
.map-loc{width:42px;height:42px;border-radius:999px;background:radial-gradient(60% 42% at 30% 6%,rgba(255,255,255,.66),transparent 50%),rgba(255,255,255,.45);backdrop-filter:blur(36px) saturate(200%);-webkit-backdrop-filter:blur(36px) saturate(200%);border:1px solid rgba(255,255,255,.65);box-shadow:0 6px 18px -6px rgba(15,25,45,.28),inset 0 1px 0 rgba(255,255,255,.85);display:flex;align-items:center;justify-content:center;cursor:pointer;padding:0}
.map-ctrls{position:absolute;top:calc(78px + var(--safe-top));right:14px;z-index:16;display:flex;flex-direction:column;gap:10px}
/* v1.177.0: belt-and-braces on the button Alex actually held down */
.map-ctrls button{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;touch-action:manipulation}
body.impersonating .map-ctrls{top:calc(110px + var(--safe-top))}
.map-loc svg{width:20px;height:20px;stroke:var(--ink3);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.site-track{position:absolute;left:0;right:0;bottom:calc(146px + var(--safe-bot));display:flex;gap:10px;overflow-x:auto;scroll-snap-type:x mandatory;padding:0 10px;z-index:15;-webkit-overflow-scrolling:touch}
.site-track.single{justify-content:center}
.site-card{scroll-snap-align:center;flex:0 0 calc(100% - 20px);background:linear-gradient(135deg,rgba(255,255,255,.6) 0%,rgba(255,255,255,.14) 42%,transparent 68%),rgba(255,255,255,.42);backdrop-filter:blur(50px) saturate(205%) brightness(1.05);-webkit-backdrop-filter:blur(50px) saturate(205%) brightness(1.05);border:1px solid rgba(255,255,255,.7);border-radius:32px;padding:20px;box-shadow:0 16px 40px -12px rgba(15,25,45,.36),inset 0 1px 0 rgba(255,255,255,.85);cursor:pointer}
.sc-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
.sc-chips{display:flex;align-items:center;gap:8px}
.sc-date{font-size:12px;font-weight:600;color:var(--meta)}
.sc-nm{font-size:20px;font-weight:800;color:var(--ink)}
.sc-ad{font-size:13px;color:var(--sub);font-weight:500;margin-top:3px}
.sc-tiles{display:flex;gap:12px;margin-top:16px}
.sc-tiles .tile{flex:1}
.sc-hint{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:12px;font-size:12px;font-weight:600;color:var(--meta)}
.sc-hint svg{width:14px;height:14px;stroke:var(--meta);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;transition:transform .25s}
.sc-head{cursor:pointer}
.wk-row{display:flex;justify-content:center;gap:8px;margin-top:14px}
.wk{width:34px;height:34px;border-radius:50%;background:#eef1f6;color:#9aa4b2;font-size:12.5px;font-weight:800;display:flex;align-items:center;justify-content:center}
.wk.on{background:var(--blue);color:#fff}
.pill-row{display:flex;flex-wrap:wrap;margin-top:12px}
.req-pill{display:inline-block;padding:9px 15px;border-radius:999px;background:rgba(44,92,229,.13);color:var(--blue);font-size:13px;font-weight:800;margin:0 8px 8px 0}
.ppe-pill{display:inline-flex;align-items:center;gap:6px;padding:9px 14px;border-radius:999px;background:#fff;border:1px solid var(--line);color:var(--ink);font-size:13px;font-weight:700;margin:0 8px 8px 0}
.ppe-pill svg{width:15px;height:15px;stroke:var(--green);fill:none;stroke-width:2.6;stroke-linecap:round;stroke-linejoin:round;flex:0 0 15px}
.dots{position:absolute;left:0;right:0;bottom:calc(122px + var(--safe-bot));display:flex;align-items:center;justify-content:center;gap:7px;z-index:15}
.dots i{height:7px;width:7px;border-radius:999px;background:rgba(15,25,45,.28);transition:all .2s}
.dots i.on{width:20px;background:var(--blue)}
.today-track{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;cursor:grab}
.today-track.dragging{cursor:grabbing;scroll-snap-type:none}
.site-track.dragging{scroll-snap-type:none}
.today-track .card{scroll-snap-align:start;flex:0 0 100%;margin-bottom:0}
.today-dots{display:flex;align-items:center;justify-content:center;gap:6px;margin:8px 0 16px}
.today-dots i{height:6px;width:6px;border-radius:999px;background:rgba(15,25,45,.22);transition:all .2s}
.today-dots i.on{width:16px;background:var(--blue)}

/* ===== SHEETS ===== */
.sh-pill{display:flex;align-items:center;gap:6px;background:var(--glass-hi);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--glass-bd);padding:8px 12px;border-radius:999px;box-shadow:0 2px 8px -3px rgba(15,25,45,.15);font-size:13px;font-weight:700;color:var(--ink3)}
.hero{background:linear-gradient(135deg,#2c5ce5,#1a3fb0);border-radius:24px;padding:20px;box-shadow:0 12px 26px -10px rgba(44,92,229,.55);margin-bottom:20px;color:#fff;border:1px solid rgba(255,255,255,.25)}
.hero .p{font-size:12px;font-weight:600;color:#c5d5ff}
.hero .big{display:flex;align-items:flex-end;gap:6px;margin-top:6px}
.hero .big b{font-size:40px;font-weight:800;line-height:1;font-variant-numeric:tabular-nums}
.hero .big span{font-size:16px;font-weight:600;color:#c5d5ff;margin-bottom:4px}
.hero .row{display:flex;gap:26px;margin-top:18px}
.hero .row .c{font-size:12px;color:#b9c8e6;font-weight:600}
.hero .row .n{font-size:16px;font-weight:800;margin-top:2px}
.wk-head{display:flex;align-items:center;justify-content:space-between;margin:0 2px 10px}
.wk-head .r{font-size:13px;font-weight:800;color:var(--ink2)}
.wk-head .tt{font-size:13px;font-weight:800;color:var(--ink);font-variant-numeric:tabular-nums}
.wk-card{background:var(--glass);backdrop-filter:blur(20px) saturate(160%);-webkit-backdrop-filter:blur(20px) saturate(160%);border:1px solid var(--glass-bd);border-radius:20px;box-shadow:var(--card-sh);overflow:hidden;margin-bottom:20px}
.site-grp{border-top:1px solid rgba(120,134,156,.14)}
.site-grp:first-child{border-top:0}
.site-row{display:flex;align-items:center;gap:10px;padding:13px 16px;cursor:pointer;-webkit-tap-highlight-color:transparent}
.site-row .b{flex:1;min-width:0}
.site-row .s1{font-weight:700;font-size:14px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.site-row .s2{font-size:11.5px;color:var(--meta);font-weight:600}
.site-row .hrs-r{display:flex;align-items:center;gap:4px;font-weight:800;font-size:15px;color:var(--ink);flex:none}
.site-row .chev{width:16px;height:16px;fill:none;stroke:var(--meta);stroke-width:2.4;transition:transform .15s}
.site-grp.open .site-row .chev{transform:rotate(90deg)}
.site-days{display:none}
.site-grp.open .site-days{display:block}
.ts-sum{display:flex;align-items:center;gap:12px;padding:14px 16px}
.ts-sum .b{flex:1;min-width:0}
.ts-sum .s1{font-size:14px;font-weight:700;color:var(--ink)}
.ts-sum .s2{font-size:12px;color:var(--sub);font-weight:500;margin-top:2px}
.ent{display:flex;align-items:center;gap:14px;padding:15px 16px;border-bottom:1px solid var(--line);cursor:pointer}
.ent:last-child{border-bottom:none}
.ent .dcol{width:44px;text-align:center;flex-shrink:0}
.ent .dow{font-size:10px;font-weight:700;color:var(--meta);text-transform:uppercase}
.ent .num{font-size:18px;font-weight:800;color:var(--ink);line-height:1}
.ent .b{flex:1;min-width:0}
.ent .s1{font-size:14px;font-weight:700;color:var(--ink)}
.ent .s2{font-size:12px;color:var(--sub);font-weight:500;margin-top:2px;font-variant-numeric:tabular-nums}
.ent .r{text-align:right;display:flex;align-items:center;gap:8px}
.ent .hrs{font-size:15px;font-weight:800;color:var(--ink);font-variant-numeric:tabular-nums}
.ent .st{font-size:11px;font-weight:700}

/* ===== PILL NAV + PUNCH ===== */
.pill-wrap{position:fixed;top:calc(var(--app-h,100dvh) - 96px - var(--safe-bot));bottom:auto;left:50%;transform:translateX(-50%);z-index:2000}
.pill{position:relative;width:304px;height:72px;border-radius:999px;background:linear-gradient(135deg,rgba(255,255,255,.6) 0%,rgba(255,255,255,.15) 40%,transparent 65%),linear-gradient(180deg,rgba(255,255,255,.4),rgba(248,250,252,.5));backdrop-filter:blur(50px) saturate(205%) brightness(1.05);-webkit-backdrop-filter:blur(50px) saturate(205%) brightness(1.05);border:1px solid rgba(255,255,255,.65);box-shadow:0 16px 34px -10px rgba(15,25,45,.28),inset 0 1px 0 rgba(255,255,255,.85)}
.pill-side{position:absolute;top:50%;transform:translateY(-50%);display:flex;flex-direction:column;align-items:center;gap:3px;cursor:pointer;width:48px;background:none;border:none;padding:0;color:var(--nav-idle)}
.pill-side svg{width:24px;height:24px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.pill-side span{font-size:10px;font-weight:700}
.pill-side.l{left:30px} .pill-side.r{right:30px}
.pill-side.on{color:var(--blue)}
#punchBtn{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:110px;height:110px;border-radius:50%;border:5px solid #fff;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;cursor:pointer;padding:0 8px;transition:background .25s,box-shadow .25s}
#punchBtn:active{transform:translate(-50%,-50%) scale(.96)}
/* v1.192.0 (Alex): the button shows the punch happening - pulses while in flight */
#punchBtn.punching{animation:tnpunching .9s ease-in-out infinite}
/* v1.230.0 (TWOCV-2026-08-26-tnp-027 - Alex: "when punching in, looks like the punch
   button offsets a bit before recentering. It shouldn't."). The translate(-50%,-50%) this
   keyframe used to carry belongs to the ABSOLUTE regime two lines above, and that regime is
   unreachable: (min-width:768px) and (max-width:767px) partition every width and both set
   #punchBtn{position:static;transform:none} (measured: computed position `static`, transform
   `none`, at 390/744/767/768/1280/1440). So doPunch()'s `.punching` class was applying a
   half-box translate nothing was compensating for, and the puck sat 42px LEFT and 42px UP for
   the entire in-flight window - freshGeo() plus the api() round trip - then snapped back when
   the class came off. Measured excursion on v1.229.0: -42px at 390/744/767, -40px at
   768/1280/1440. Identical to the mistake slideMove() documents about itself in v1.97.0 ("NO
   -50% here ... the puck is a static flex child with no baseline transform"); this keyframe
   never got the same correction. The pulse is a pulse: scale only, no position.
   THE HAZARD THIS LEAVES, named rather than removed: the absolute rule above still centres
   with `transform`, and both overrides reset transform to none, which is the ONLY reason
   scale() alone is safe here. Re-enable that regime - or drop either override's
   `transform:none` - and this keyframe has to be revisited in the same edit. Centring the
   base rule by margin instead was built and MEASURED, and it moved the button 27.5px left at
   all six widths, because margin is the one property neither override resets; backed out. */
@keyframes tnpunching{0%,100%{transform:scale(1);filter:brightness(1)}50%{transform:scale(1.07);filter:brightness(1.3)}}
#punchBtn .site{display:none}
#punchBtn .w{font-size:15px;font-weight:800;line-height:1.1}
#punchBtn .w1{margin-top:3px}
#punchBtn .el{font-size:11px;font-weight:700;font-variant-numeric:tabular-nums;margin-top:2px;letter-spacing:.5px}
#punchBtn .el:empty{display:none}
.p-ready{background:#16a34a;box-shadow:0 14px 22px -10px rgba(22,163,74,.6);color:#fff}
.p-ready .site,.p-ready .el{color:rgba(255,255,255,.85)}
.p-in{background:#ef4444;box-shadow:0 14px 22px -10px rgba(239,68,68,.6);color:#fff}
.p-in .site,.p-in .el{color:rgba(255,255,255,.85)}
.p-lunch{background:#d97706;box-shadow:0 14px 22px -10px rgba(217,119,6,.6);color:#fff}
.p-lunch .site,.p-lunch .el{color:rgba(255,255,255,.9)}
.p-zone{background:#eab308;box-shadow:0 14px 22px -10px rgba(234,179,8,.55);color:#3d2f04}
.p-zone .site,.p-zone .el{color:rgba(61,47,4,.7)}
.p-off{background:#c3ccda;box-shadow:0 8px 18px -8px rgba(15,25,45,.3),inset 0 0 22px -2px rgba(227,32,42,.55),inset 0 0 6px rgba(227,32,42,.4);color:#4b5666}
.p-off .site,.p-off .el{color:#5f6b7d}

/* ===== punch target pill ===== */
.punch-target{position:fixed;left:50%;top:calc(var(--app-h,100dvh) - 121px - var(--safe-bot));bottom:auto;transform:translate(-50%,-100%);z-index:2001;max-width:80vw;display:none;align-items:center;gap:8px;padding:8px 15px;border-radius:999px;background:radial-gradient(75% 55% at 28% -14%,rgba(255,255,255,.62),transparent 45%),rgba(255,255,255,.44);backdrop-filter:blur(42px) saturate(200%);-webkit-backdrop-filter:blur(42px) saturate(200%);border:1px solid rgba(255,255,255,.65);box-shadow:0 12px 30px -10px rgba(15,25,45,.34),inset 0 1px 0 rgba(255,255,255,.88);white-space:nowrap;overflow:hidden}
.punch-target.show{display:flex}
.punch-target .pt-dot{width:8px;height:8px;border-radius:50%;flex:0 0 8px}
.punch-target .pt-lead{font-size:11px;font-weight:700;color:var(--meta);text-transform:uppercase;letter-spacing:.04em;flex:0 0 auto}
.punch-target .pt-site{font-size:13px;font-weight:800;color:var(--ink);overflow:hidden;text-overflow:ellipsis}

/* ===== sheets (bottom) ===== */
/* ===== tasks (v1.4.0) ===== */
.tk-row{display:flex;align-items:center;gap:12px;padding:11px 8px;border-bottom:1px solid var(--line);cursor:pointer}
.tk-row:last-child{border-bottom:none}
.tk-check{flex:0 0 22px;width:22px;height:22px;border-radius:7px;border:2px solid #c3ccda;background:#fff;cursor:pointer;position:relative;padding:0}
.tk-check.on{background:var(--green);border-color:var(--green)}
.tk-check.on::after{content:'';position:absolute;left:6px;top:2px;width:5px;height:10px;border:solid #fff;border-width:0 2px 2px 0;transform:rotate(45deg)}
.tk-main{flex:1;min-width:0}
.tk-sub{font-size:14px;font-weight:700;color:var(--ink);line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tk-meta{font-size:12px;color:var(--meta);font-weight:600;margin-top:2px;display:flex;align-items:center;gap:5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tk-dot{width:7px;height:7px;border-radius:50%;flex:0 0 7px}
.tk-due{color:var(--amber-meta)}
.tk-arrow{width:16px;height:16px;flex:0 0 16px;stroke:#c3ccda;fill:none;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.tk-note{width:100%;box-sizing:border-box;border:1.5px solid var(--line);border-radius:14px;padding:12px 14px;font-size:14px;font-family:inherit;color:var(--ink);background:#fff;resize:none;min-height:76px;font-weight:500}
.tk-note:focus{outline:none;border-color:var(--blue)}
.tk-photo-btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;box-sizing:border-box;padding:13px;border-radius:14px;border:1.5px dashed #c3ccda;background:#fff;color:var(--ink2);font-size:14px;font-weight:700;cursor:pointer;margin-top:10px}
.tk-photo-btn svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.tk-thumb{margin-top:10px;border-radius:14px;overflow:hidden;position:relative;display:none}
.tk-thumb img{width:100%;display:block;max-height:220px;object-fit:cover}
.tk-thumb .rm{position:absolute;top:8px;right:8px;width:30px;height:30px;border-radius:50%;background:rgba(10,16,26,.6);border:none;color:#fff;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:1}
/* v1.6.0 task thread */
.tk-thread{display:flex;flex-direction:column;gap:7px;padding:4px 0 2px;max-height:300px;overflow-y:auto;-webkit-overflow-scrolling:touch}
.tk-bub{max-width:82%;padding:9px 13px;border-radius:16px;font-size:13.5px;font-weight:500;line-height:1.45;word-wrap:break-word;white-space:pre-wrap}
.tk-bub.mine{align-self:flex-end;background:var(--blue,#2c5ce5);color:#fff;border-bottom-right-radius:5px}
.tk-bub.theirs{align-self:flex-start;background:rgba(255,255,255,.92);color:var(--ink);border:1px solid rgba(14,23,38,.08);border-bottom-left-radius:5px}
.tk-bub .who{display:block;font-size:10.5px;font-weight:800;letter-spacing:.02em;color:var(--sub);margin-bottom:2px}
.tk-bub.mine .who{color:rgba(255,255,255,.75)}
.tk-bub .when{display:block;font-size:10px;font-weight:600;opacity:.65;margin-top:3px}
.tk-bub.pending{opacity:.55}.tk-bub.failed{background:#b3261e;color:#fff}
.tk-empty{font-size:12.5px;color:var(--sub);font-weight:600;padding:6px 2px}

.tk-composer input{flex:1;border:1px solid rgba(14,23,38,.12);border-radius:14px;padding:11px 13px;font:inherit;font-size:14px;background:rgba(255,255,255,.92);color:var(--ink)}
.tk-composer button{border:0;border-radius:14px;background:var(--blue,#2c5ce5);color:#fff;font-weight:800;font-size:13.5px;padding:0 16px;min-width:64px}
.tk-composer button:disabled{opacity:.5}

.tk-mp svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:2}
.tk-mp:disabled{opacity:.5}
.tk-img{display:block;max-width:210px;max-height:250px;width:auto;border-radius:12px;margin:2px 0 1px;cursor:zoom-in;background:rgba(0,0,0,.06)}
.tk-bub.mine .tk-img{margin-left:auto}
.tk-imglink{display:inline-flex;align-items:center;gap:5px;font-size:12.5px;font-weight:700;color:var(--blue,#2c5ce5);text-decoration:underline;padding:6px 0}
/* v1.171.0: the master record is TALL (one section per worker), so the card scrolls its
   image and pins the actions to the footer - a lightbox that fits the whole sheet on a
   phone would render it unreadable. */
.mrec-ov{position:fixed;inset:0;z-index:4100;background:rgba(10,16,26,.55);display:flex;align-items:center;justify-content:center;padding:16px;animation:tnfade .2s ease}
@keyframes mrecpop{from{opacity:0;transform:translateY(14px) scale(.97)}to{opacity:1;transform:none}}
.mrec-card{width:min(560px,100%);max-height:calc(100dvh - 32px);max-height:calc(var(--vvh,100dvh) - 32px);display:flex;flex-direction:column;border-radius:26px;overflow:hidden;background:radial-gradient(55% 28% at 28% 0%,rgba(255,255,255,.62),transparent 42%),rgba(248,250,252,.72);backdrop-filter:blur(52px) saturate(200%) brightness(1.08);-webkit-backdrop-filter:blur(52px) saturate(200%) brightness(1.08);border:1px solid rgba(255,255,255,.6);box-shadow:0 22px 54px -18px rgba(15,25,45,.5);animation:mrecpop .22s cubic-bezier(.22,.61,.36,1)}
/* v1.190.0 (Alex): the record image owns the top/left/right edges; the standard X floats
   over it and everything else lives in the bottom section. */
.mrec-card{position:relative}
.mrec-card>.mrec-x{position:absolute;top:10px;right:12px;z-index:3}
.mrec-sub{font-size:11.5px;font-weight:700;color:var(--sub);margin-top:2px}
/* v1.187.0 (Alex): 'Why is the x not our standard UI button?' It is now - .sheet-x, the one
   recipe every card closes with. No bespoke close buttons again. */
.mrec-scroll{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:0;background:rgba(255,255,255,.35)}
.mrec-scroll img{display:block;width:100%}
.mrec-ft{padding:12px 16px calc(14px + var(--safe-bot));display:flex;flex-direction:column;gap:9px;border-top:1px solid rgba(255,255,255,.55)}
.mrec-st{font-size:11.5px;font-weight:700;color:var(--sub);min-height:15px}
.mrec-st.ok{color:#16a34a}.mrec-st.err{color:#dc2626}
.mrec-ax{display:flex;gap:8px}
/* ==== v1.187.0 UI GUIDELINE (Alex): THE SPLIT BUTTON. One primary action + a caret opening
   a menu of secondary actions - use .tn-split/.tn-menu wherever a button row would sprawl.
   The caret inherits the primary's colour classes; menu items are .tn-menu-it, with .danger
   for destructive/override actions. First use: the master record's Download. ==== */
.tn-split{position:relative;display:flex;flex:1;min-width:0}
.tn-split .tn-split-main{flex:1;border-radius:14px 0 0 14px}
/* v1.190.0 (Alex): the caret is a SEGMENT of the primary button - divider line, no gap */
.tn-split .tn-split-caret{flex:0 0 36px;border-radius:0 14px 14px 0;margin-left:0;border-left:1px solid rgba(255,255,255,.4);display:flex;align-items:center;justify-content:center;padding:0}
.tn-split .tn-split-caret svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.tn-menu{position:absolute;right:0;bottom:calc(100% + 6px);min-width:172px;background:var(--card);border:1px solid var(--line);border-radius:14px;box-shadow:0 14px 34px -12px rgba(15,25,45,.4);padding:6px;z-index:60;display:flex;flex-direction:column;gap:2px}
.tn-menu[hidden]{display:none}
.tn-menu-it{display:block;width:100%;text-align:left;background:none;border:0;border-radius:9px;padding:9px 11px;font-size:12.5px;font-weight:800;color:var(--ink);cursor:pointer;text-decoration:none;font-family:inherit}
.tn-menu-it:hover{background:rgba(15,25,45,.06)}
.tn-menu-it.danger{color:#C41A23}
.tn-menu-it:disabled{opacity:.5;cursor:default}
.mrec-ax .b{flex:1;text-align:center;text-decoration:none;border:0;cursor:pointer;border-radius:14px;padding:11px 12px;font-size:12.5px;font-weight:800}
.mrec-dl{background:#E3202A;color:#fff;box-shadow:0 10px 24px -12px rgba(227,32,42,.8)}
.mrec-sh{background:rgba(15,25,45,.07);color:var(--ink)}
.mrec-open{background:rgba(15,25,45,.07);color:var(--ink);display:none}
.tk-lb{position:fixed;inset:0;background:rgba(6,10,20,.92);display:flex;align-items:center;justify-content:center;z-index:4000;padding:16px}
.tk-lb img{max-width:100%;max-height:100%;border-radius:10px}
.tk-lb .x{position:absolute;top:calc(12px + env(safe-area-inset-top));right:16px;width:40px;height:40px;border-radius:20px;border:0;background:rgba(255,255,255,.16);color:#fff;font-size:22px;line-height:1}
.brk-sheet{position:fixed;left:0;right:0;bottom:0;z-index:3010;background:radial-gradient(55% 28% at 28% 0%,rgba(255,255,255,.6),transparent 42%),rgba(248,250,252,.5);backdrop-filter:blur(52px) saturate(200%) brightness(1.09);-webkit-backdrop-filter:blur(52px) saturate(200%) brightness(1.09);border-top:1px solid rgba(255,255,255,.6);border-radius:34px 34px 0 0;padding:10px 18px calc(24px + var(--safe-bot));max-height:calc(var(--vvh,100dvh) * .92);overflow-y:auto;box-shadow:0 -12px 44px rgba(10,16,26,.28),inset 0 1px 0 rgba(255,255,255,.8);animation:tnsheet .3s cubic-bezier(.22,.61,.36,1)}
.brk-grab{width:38px;height:5px;border-radius:3px;background:#c7d0dd;margin:0 auto 14px}
.brk-ttl{font-size:20px;font-weight:800;color:var(--ink);text-align:center}
.brk-sub{font-size:13px;color:var(--meta);font-weight:500;text-align:center;margin:5px 0 16px}
.brk-notes{width:100%;min-height:64px;resize:none;border:1px solid var(--line);border-radius:12px;padding:10px 12px;font:inherit;font-size:14px;background:var(--card);color:var(--ink);margin:2px 0 10px}
.brk-go{background:rgba(22,163,74,.14);color:var(--green);font-weight:800}
.brk-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.brk-opt{border:0;border-radius:15px;padding:16px;font-size:15px;font-weight:700;color:var(--ink);background:#fff;box-shadow:0 1px 4px rgba(10,16,26,.06);cursor:pointer}
.brk-opt[data-m='0']{grid-column:1 / -1;background:var(--blue-bg);color:var(--blue)}
.brk-cust{width:100%;margin-top:10px;background:var(--ink);color:#fff}
.tk-seg{display:inline-flex;gap:2px;background:#eef1f6;border-radius:10px;padding:3px;margin-left:auto}
.tk-seg-b{border:0;background:transparent;color:var(--meta);font-size:12px;font-weight:700;padding:5px 12px;border-radius:8px;cursor:pointer}
.tk-seg-b.active{background:#fff;color:var(--ink);box-shadow:0 1px 3px rgba(10,16,26,.1)}
.card-timer{font-variant-numeric:tabular-nums;font-weight:800;letter-spacing:.3px}
.tk-check.done{background:var(--green);border-color:var(--green)}
.tk-sticky{position:-webkit-sticky;position:sticky;bottom:calc(6px + var(--safe-bot));box-shadow:0 8px 22px rgba(10,16,26,.18)}
#taskSheet{z-index:2720}
.sheet-ov{position:fixed;inset:0;z-index:2700;background:rgba(10,16,26,.3);display:none;animation:tnfade .25s ease}
.sheet-ov.open{display:block}
.sheet{position:fixed;left:0;right:0;bottom:0;z-index:2710;height:90%;height:calc(var(--vvh,100dvh) * .9);border-radius:36px 36px 0 0;overflow:hidden;display:none;flex-direction:column;background:radial-gradient(55% 28% at 28% 0%,rgba(255,255,255,.62),transparent 42%),rgba(248,250,252,.46);backdrop-filter:blur(52px) saturate(200%) brightness(1.1);-webkit-backdrop-filter:blur(52px) saturate(200%) brightness(1.1);border-top:1px solid rgba(255,255,255,.55);box-shadow:0 -12px 44px rgba(10,16,26,.28),inset 0 1px 0 rgba(255,255,255,.78);animation:tnsheet .34s cubic-bezier(.22,.61,.36,1)}
.sheet.open{display:flex}
.sheet.auto{height:auto;max-height:calc(var(--vvh,100dvh) * .9)}
.pull-bar{flex:0 0 auto;position:relative;padding:12px 0 8px;display:flex;justify-content:center;cursor:grab;touch-action:none;z-index:45}
.pull-bar::before{content:'';width:42px;height:5px;border-radius:999px;background:#cdd5e1}
.sheet-x{position:absolute;right:16px;top:10px;width:34px;height:34px;border-radius:50%;background:radial-gradient(60% 42% at 30% 6%,rgba(255,255,255,.72),transparent 50%),rgba(255,255,255,.4);backdrop-filter:blur(24px) saturate(200%);-webkit-backdrop-filter:blur(24px) saturate(200%);box-shadow:0 4px 14px -4px rgba(15,25,45,.3),inset 0 1px 0 rgba(255,255,255,.8);display:flex;align-items:center;justify-content:center;cursor:pointer;border:1px solid rgba(255,255,255,.6);padding:0;z-index:46}
.sheet-x svg{width:17px;height:17px;stroke:var(--ink3);fill:none;stroke-width:2.4;stroke-linecap:round}
.sheet-body{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:4px 20px calc(28px + var(--safe-bot))}
.grp-h{font-size:14px;font-weight:800;color:var(--ink);margin:22px 2px 10px}
.pill-chip{font-size:13px;font-weight:700;color:var(--blue);background:var(--blue-bg);padding:8px 14px;border-radius:999px;display:inline-block}
.ppe-chip{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:700;color:var(--ink2);background:rgba(255,255,255,.85);border:1px solid #e6ebf2;padding:8px 13px;border-radius:999px}
.ppe-chip svg{width:14px;height:14px;stroke:var(--green);fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.chips{display:flex;flex-wrap:wrap;gap:8px}
.wcard{background:linear-gradient(135deg,rgba(255,255,255,.5) 0%,rgba(255,255,255,.11) 45%,transparent 72%),rgba(112,126,148,.27);backdrop-filter:blur(33px) saturate(200%) brightness(1.06);-webkit-backdrop-filter:blur(33px) saturate(200%) brightness(1.06);border-radius:24px;box-shadow:0 4px 16px -8px rgba(15,25,45,.14),inset 0 1px 0 rgba(255,255,255,.4)}
.cert-row{display:flex;align-items:center;gap:12px;padding:14px 0;border-bottom:1px solid var(--line)}
.cert-row:last-child{border-bottom:none}
.cert-ic{width:38px;height:38px;border-radius:11px;background:var(--blue-bg);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.cert-ic svg{width:19px;height:19px;stroke:var(--blue);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.cert-b{flex:1;min-width:0}
.cert-b .n{font-size:14px;font-weight:700;color:var(--ink)}
.cert-b .m{font-size:12px;font-weight:600;margin-top:2px}
.day7{display:flex;gap:7px}
.day7 .d{flex:1;text-align:center;padding:11px 0;border-radius:13px;font-size:12px;font-weight:800}
.big-btn{display:block;width:100%;padding:14px;border-radius:15px;border:none;font-size:15px;font-weight:700;cursor:pointer;margin-top:10px;text-align:center;color:#fff}
.big-btn.blue{background:var(--blue);box-shadow:0 8px 18px -8px rgba(44,92,229,.7)}
.big-btn.green{background:var(--green);box-shadow:0 8px 18px -8px rgba(22,163,74,.7)}
.big-btn.amber{background:var(--amber);box-shadow:0 8px 18px -8px rgba(217,119,6,.7)}
.big-btn.red{background:var(--red);box-shadow:0 8px 18px -8px rgba(239,68,68,.7)}
.big-btn.ghost{background:#e9edf3;color:var(--ink2);box-shadow:none}
.big-btn:disabled{opacity:.5}
.input{width:100%;padding:15px 16px;border-radius:15px;background:#fff;border:1.5px solid #e6ebf2;color:var(--ink);font-size:16px;font-weight:600;font-family:inherit;-webkit-appearance:none;outline:none}
.input:focus{border-color:var(--blue)}
.match-row{display:flex;align-items:center;gap:12px;padding:13px 4px;border-bottom:1px solid var(--line);cursor:pointer}
/* v1.176.0 (Alex): "The avatar bubbles should be the same style as the ones on the filter
   list." There was no bare `.av` rule at all - only this one, scoped to .match-row - so the
   message rows (.mc-row) rendered naked initials with no bubble. Same selector list, one
   definition, so the two cards cannot drift again. */
.match-row .av,.mc-row .av{width:38px;height:38px;border-radius:50%;background:linear-gradient(135deg,#0f2a52,#17356a);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:13px;flex:0 0 38px}
.warn-box{display:flex;gap:10px;align-items:flex-start;background:var(--amber-soft);border:1px solid var(--amber-bd);border-radius:14px;padding:12px 13px;margin-bottom:10px}
.warn-box i{width:7px;height:7px;border-radius:50%;background:var(--amber);margin-top:6px;flex:0 0 7px}
.warn-box span{font-size:13px;font-weight:600;color:var(--amber-ink);line-height:1.45}
.kv{display:flex;justify-content:space-between;gap:12px;padding:11px 2px;border-bottom:1px solid var(--line);font-size:13px}
.kv .k{color:var(--meta);font-weight:600}.kv .v{font-weight:700;color:var(--ink);text-align:right}

/* modal / day popup */
/* v1.96.0 punch time selector; v1.194.0: one native date/time row (adjust-sheet pattern) -
   the committed value is still a signed offset clamped to the window. */
.pt-lab{font-size:11px;font-weight:800;color:var(--meta);letter-spacing:.06em;margin:18px 0 8px}
.pt-row{display:flex;align-items:center;gap:10px}
.pt-row .pt-fi{flex:1 1 auto;font-size:17px;font-weight:800;font-variant-numeric:tabular-nums;text-align:center}
.pt-d{font-size:11px;font-weight:700;color:var(--meta);background:rgba(14,23,38,.06);border-radius:8px;padding:4px 9px;flex:0 0 auto}
.pt-hint{font-size:11.5px;font-weight:600;color:var(--meta);margin-top:7px;text-align:center}
.modal-ov{position:fixed;inset:0;z-index:3200;background:rgba(10,16,26,.5);display:none;align-items:center;justify-content:center;padding:24px;animation:tnfade .2s ease}
.modal-ov.open{display:flex}
.modal{width:100%;max-width:400px;background:radial-gradient(60% 35% at 28% -8%,rgba(255,255,255,.64),transparent 42%),rgba(255,255,255,.96);backdrop-filter:blur(46px) saturate(200%);-webkit-backdrop-filter:blur(46px) saturate(200%);border:1px solid rgba(255,255,255,.6);border-radius:32px;padding:24px 22px;box-shadow:0 24px 60px -18px rgba(10,16,26,.55),inset 0 1px 0 rgba(255,255,255,.88);animation:tnpop .26s cubic-bezier(.22,.61,.36,1)}
.modal .mic{width:54px;height:54px;border-radius:50%;background:var(--amber-bg);display:flex;align-items:center;justify-content:center;margin-bottom:16px}
.modal .mic svg{width:28px;height:28px;stroke:var(--amber);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.modal h3{font-size:19px;font-weight:800;color:var(--ink);margin:0}
.modal .msub{font-size:13px;color:var(--sub);font-weight:500;margin-top:4px}
.modal .btns{display:flex;gap:12px;margin-top:22px}
.modal .btns .big-btn{flex:1;margin-top:0}
.daycard{width:100%;max-width:400px;background:radial-gradient(60% 35% at 28% -8%,rgba(255,255,255,.64),transparent 42%),rgba(255,255,255,.96);backdrop-filter:blur(46px) saturate(200%);-webkit-backdrop-filter:blur(46px) saturate(200%);border:1px solid rgba(255,255,255,.55);border-radius:32px;overflow:hidden;box-shadow:0 24px 60px -18px rgba(10,16,26,.55),inset 0 1px 0 rgba(255,255,255,.88);animation:tnpop .26s cubic-bezier(.22,.61,.36,1)}
.daycard .hd{background:linear-gradient(135deg,#2c5ce5,#1a3fb0);padding:20px;color:#fff;position:relative}
.daycard .hd .x{position:absolute;right:14px;top:14px;width:30px;height:30px;border-radius:50%;background:rgba(255,255,255,.18);display:flex;align-items:center;justify-content:center;cursor:pointer}
.daycard .hd .x svg{width:15px;height:15px;stroke:#fff;fill:none;stroke-width:2.6;stroke-linecap:round}
.daycard .hd .d1{font-size:12px;font-weight:600;color:#c5d5ff}
.daycard .hd .d2{font-size:20px;font-weight:800;margin-top:3px}
.daycard .hd .d3{font-size:13px;color:#c5d5ff;font-weight:500;margin-top:2px}
.daycard .bd{padding:20px}
.io{display:flex;align-items:stretch}
.io .c{flex:1;text-align:center}
.io .k{font-size:11px;color:var(--meta);font-weight:700}
.io .v{font-size:22px;font-weight:800;margin-top:4px;font-variant-numeric:tabular-nums}
.io .sep{width:1px;background:#eef1f6}
.tile3{display:flex;gap:10px;margin-top:18px}
.tile3 .tile{flex:1;text-align:center;padding:12px}
.zone-note{display:flex;align-items:center;gap:10px;margin-top:14px;padding:12px 14px;border-radius:14px}
.zone-note svg{width:18px;height:18px;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}
.zone-note span{font-size:13px;font-weight:600}

#toast{position:fixed;left:50%;bottom:calc(172px + var(--safe-bot));transform:translateX(-50%) translateY(20px);z-index:3200;background:rgba(255,255,255,.95);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid var(--glass-bd);border-radius:999px;padding:11px 18px;font-size:12.5px;font-weight:700;color:var(--ink);opacity:0;pointer-events:none;transition:all .25s;box-shadow:0 12px 30px rgba(15,25,45,.22);max-width:86vw;text-align:center}
#toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
#toast.err{color:#dc2626}
#toast.ok{color:var(--green)}
.fo-inp{width:100%;box-sizing:border-box;border:1px solid var(--line);border-radius:14px;padding:12px 14px;font:inherit;font-size:15px;background:rgba(255,255,255,.72);color:var(--ink);margin-top:6px}
textarea.fo-inp{min-height:92px;resize:vertical}
.fo-lab{font-size:11px;font-weight:700;color:var(--meta);letter-spacing:.04em;margin-top:12px}
.tsi-btn,.at-btn{background:none;border:0;color:var(--blue);font:inherit;font-size:13px;font-weight:700;padding:0;cursor:pointer}
.big-btn.red{background:#E3202A;color:#fff;box-shadow:0 8px 18px -8px rgba(227,32,42,.7)}
body.modal-open .punch-target,body.modal-open .pill-wrap{opacity:0;pointer-events:none;transition:opacity .15s}
.fo-ttl{font-size:20px;font-weight:800;color:var(--ink);margin-bottom:4px}
.fo-sub{font-size:13px;color:var(--sub);font-weight:500;margin-bottom:8px}
.daycard .io .iodt{font-size:12px;font-weight:700;color:var(--meta);margin:3px 0 1px}
#railNav{display:none}
#homeGreet{display:none}
.gs-pin{background:#fff;border:1.5px solid rgba(15,25,45,.3);border-radius:10px;padding:3px 9px;font-size:11px;font-weight:700;color:#0e1726;box-shadow:0 4px 12px rgba(15,25,45,.3);white-space:nowrap;display:inline-block}
.gs-pin-person{border-radius:999px;background:#17356a;color:#fff;border-color:rgba(255,255,255,.5)}
.gs-pin-opp{opacity:.85;background:#eef2f8}
.gs-pin-acct{border-color:#E3202A}

/* ===== v1.23 responsive: collapsible icon rail (>=768px) ===== */
@keyframes tnsheetd{from{opacity:0;transform:translate(-50%,-47%)}to{opacity:1;transform:translate(-50%,-50%)}}
@media (min-width:768px){
  body{--railw:64px}
  body.railx{--railw:220px}
  /* rail chrome: icons stacked; header content hidden */
  #topBand{top:0;bottom:0;right:auto;width:var(--railw);display:flex;flex-direction:column;box-shadow:8px 0 22px -12px rgba(120,10,18,.55);transition:width .18s ease;background:#C41A23;z-index:2900}
  .tb-main{display:none}
  #railNav{display:flex;flex-direction:column;flex:1;min-height:0;padding:12px 10px calc(16px + var(--safe-bot));gap:6px;overflow:visible}
  #rnBrand{display:flex;align-items:center;justify-content:center;gap:9px;padding:8px 0 4px;min-height:34px}
  #rnBrand svg{width:24px;height:21px;flex:none;filter:drop-shadow(0 3px 8px rgba(0,0,0,.28))}
  #rnBrand .lbl{display:none;font-size:14px;font-weight:800;letter-spacing:.1em;color:#fff;white-space:nowrap}
  body.railx #rnBrand{justify-content:flex-start;padding-left:14px}
  #rnBrand{cursor:pointer}
  body.railx #rnBrand .lbl{display:block}
  .rn-top{display:flex;flex-direction:column;gap:6px;margin-top:4px}
  .rn-bot{display:flex;flex-direction:column;gap:6px;margin-top:auto}
  .rn-it{position:relative;display:flex;align-items:center;justify-content:center;gap:12px;border:none;background:none;color:rgba(255,255,255,.78);padding:12px 10px;border-radius:14px;cursor:pointer;font-family:inherit;min-height:46px}
  .rn-it svg{width:22px;height:22px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex:none}
  .rn-it .lbl{display:none;font-size:13px;font-weight:700}
  .rn-it.on{color:#fff;background:rgba(255,255,255,.2)}
  body.railx .rn-it{justify-content:flex-start;padding:12px 14px}
  body.railx .rn-it .lbl{display:block}
  #rnAvatar{width:34px;height:34px;border-radius:50%;background:rgba(255,255,255,.22);color:#fff;font-weight:800;font-size:13px;display:flex;align-items:center;justify-content:center;flex:none}
  #rnCollapse{position:absolute;top:50%;right:0;transform:translateY(-50%);width:24px;height:72px;min-height:0;padding:0;border:none;border-radius:12px 0 0 12px;background:transparent;color:rgba(255,255,255,.85);display:flex;align-items:center;justify-content:center;z-index:6}
  #rnCollapse svg{transition:transform .18s ease}
  body.railx #rnCollapse svg{transform:rotate(180deg)}
  /* view-as banner spans the content top */
  #asBanner{position:fixed;top:0;left:var(--railw);right:0;z-index:1600;transition:left .18s ease}
  /* content clears the rail */
  .view{left:var(--railw);padding-top:calc(26px + var(--safe-top));padding-bottom:calc(48px + var(--safe-bot));transition:left .18s ease}
  body.impersonating .view{padding-top:calc(62px + var(--safe-top))}
  /* v1.208.0 (Alex): "it's too wide. Same nominal width as the main page." #vPay joins
     the SAME rule rather than getting its own - a second set of numbers is how the two
     views drift apart on the next layout change. */
  #vHome,#vSheets,#vPay{padding-left:max(20px,calc(50vw - var(--railw)/2 - 380px));padding-right:max(20px,calc(50vw - var(--railw)/2 - 380px))}
  #homeGreet{display:block;margin:2px 2px 16px}
  #homeGreet .g1{font-size:13px;color:var(--sub);font-weight:600;line-height:1}
  #homeGreet .g2{font-size:24px;color:var(--ink);font-weight:800;margin-top:4px}
  .map-ctrls{top:calc(20px + var(--safe-top))}
  body.impersonating .map-ctrls{top:calc(56px + var(--safe-top))}
  /* punch: smaller, overlapping the rail edge; current-site chip directly right */
  .pill-wrap{left:calc(var(--railw) + (100vw - var(--railw))/2);right:auto;top:auto;bottom:calc(22px + var(--safe-bot));transform:translateX(-50%);width:auto;z-index:2100;transition:left .18s ease}
  .pill{width:auto;height:auto;border-radius:0;background:none;border:none;box-shadow:none;backdrop-filter:none;-webkit-backdrop-filter:none;display:block}
  .pill-side{display:none}
  #punchBtn{position:static;transform:none;width:80px;height:80px;border-width:4px}
  #punchBtn:active{transform:scale(.96)}
  #punchBtn .w{font-size:12.5px}
  #punchBtn .el{font-size:10px}
  .punch-target{left:calc(var(--railw) + (100vw - var(--railw))/2);right:auto;top:auto;bottom:calc(108px + var(--safe-bot));transform:translateX(-50%);max-width:44vw;transition:left .18s ease}
  #toast{left:calc(50% + var(--railw)/2);bottom:calc(56px + var(--safe-bot))}
  /* dialogs centered over the content region */
  .sheet{left:calc(50% + var(--railw)/2);right:auto;top:50%;bottom:auto;width:min(680px,calc(94vw - var(--railw)));height:auto;max-height:min(76vh,820px);border-radius:30px;border:1px solid rgba(255,255,255,.6);transform:translate(-50%,-50%);animation:tnsheetd .28s cubic-bezier(.22,.61,.36,1)}
  .sheet.auto{max-height:min(76vh,820px)}
  .brk-sheet{left:calc(50% + var(--railw)/2);right:auto;top:50%;bottom:auto;width:min(560px,calc(94vw - var(--railw)));border-radius:30px;transform:translate(-50%,-50%);animation:tnsheetd .28s cubic-bezier(.22,.61,.36,1);padding-bottom:24px}
  /* profile = popover right of the avatar button */
  #profSheet{left:calc(var(--railw) + 14px);right:auto;top:auto;bottom:calc(14px + var(--safe-bot));transform:none;width:min(420px,calc(96vw - var(--railw)));max-height:min(78vh,720px);border-radius:24px;animation:tnpop .22s cubic-bezier(.22,.61,.36,1)}
  /* carousels */
  .today-track{gap:12px}
  .today-track .card{flex:0 0 calc(50% - 6px)}
  .site-card{flex:0 0 430px}
  .site-track{padding:0 max(10px,calc(50% - 215px))}
  /* dock panels slide out from UNDER the rail (rail z is higher); no page dim; view resizes */
  #gsSheet,#acctPanel{position:fixed;left:var(--railw);right:auto;top:0;bottom:0;width:min(420px,58vw);height:auto;max-height:none;transform:translateX(-100%);transition:transform .22s cubic-bezier(.22,.61,.36,1),left .18s ease;z-index:2600;border-radius:0 26px 26px 0;border-left:none;display:flex;flex-direction:column;box-shadow:14px 0 34px -14px rgba(15,25,45,.4)}
  #gsSheet{display:flex!important}
  #gsSheet.dockopen,#acctPanel.dockopen{transform:translateX(0)}
  body.dockon .view{left:calc(var(--railw) + min(420px,58vw))}
  body.dockon #asBanner{left:calc(var(--railw) + min(420px,58vw))}
}
@media (max-width:767px){#acctPanel{display:none}}
@media (min-width:1100px){
  #vHome,#vPay{padding-left:max(24px,calc(50vw - var(--railw)/2 - 570px));padding-right:max(24px,calc(50vw - var(--railw)/2 - 570px))}
  .bd-img{height:250px}
  #vSheets{padding-left:max(24px,calc(50vw - var(--railw)/2 - 570px));padding-right:max(24px,calc(50vw - var(--railw)/2 - 570px))}
  #sheetList{column-count:2;column-gap:22px}
  #sheetList>*{break-inside:avoid;-webkit-column-break-inside:avoid}
  .sheet{width:min(720px,calc(90vw - var(--railw)))}
}
@media (hover:hover) and (pointer:fine){
  .rnd-btn:hover,.avatar:hover{filter:brightness(1.12)}
  .site-card:hover{transform:translateY(-2px);transition:transform .15s}
  .pill-side:hover{filter:brightness(1.06)}
  .card,.wk-card{transition:box-shadow .15s}
  button{cursor:pointer}
}
/* ===== v1.39.0 floating cards (XR port, desktop/tablet only) ===== */
@media (min-width:768px){
#psOv{pointer-events:none;background:transparent !important;backdrop-filter:none !important;-webkit-backdrop-filter:none !important}
.sheet.float{position:fixed;left:auto;right:auto;top:auto;bottom:auto;transform:none;animation:none;overflow:visible;max-height:min(78vh,860px);touch-action:none}
.sheet.float .sheet-body{overflow-y:auto;min-height:0;touch-action:pan-y}
.sheet.float input,.sheet.float textarea,.sheet.float select{touch-action:auto}
.sheet.float.window-dragging,.sheet.float.window-resizing{transition:none;user-select:none}
.sheet.float.window-dragging{box-shadow:0 24px 64px -12px rgba(10,16,26,.45)}
.win-handle{position:absolute;z-index:12}
.win-handle.corner{width:30px;height:30px}
.win-h-nw{top:-4px;left:-4px;cursor:nwse-resize}
.win-h-ne{top:-4px;right:-4px;cursor:nesw-resize}
.win-h-sw{bottom:-4px;left:-4px;cursor:nesw-resize}
.win-h-se{bottom:-4px;right:-4px;cursor:nwse-resize}
.win-handle.edge{}
.win-h-n{top:-4px;left:28px;right:28px;height:10px;cursor:ns-resize}
.win-h-s{bottom:-4px;left:28px;right:28px;height:10px;cursor:ns-resize}
.win-h-w{left:-4px;top:28px;bottom:28px;width:10px;cursor:ew-resize}
.win-h-e{right:-4px;top:28px;bottom:28px;width:10px;cursor:ew-resize}
.sheet.float .pull-bar{cursor:grab}
.sheet.float.window-dragging .pull-bar{cursor:grabbing}
}
@media (max-width:767px){.win-handle{display:none}}
/* ===== v1.39.3 punch-cluster hide (desktop/tablet only) ===== */
@media (min-width:768px){
#punchHide{position:absolute;top:50%;right:-46px;transform:translateY(-50%);width:34px;height:34px;border-radius:50%;border:1px solid rgba(255,255,255,.6);background:radial-gradient(60% 42% at 30% 6%,rgba(255,255,255,.72),transparent 50%),rgba(255,255,255,.4);backdrop-filter:blur(24px) saturate(200%);-webkit-backdrop-filter:blur(24px) saturate(200%);box-shadow:0 4px 14px -4px rgba(15,25,45,.3);display:flex;align-items:center;justify-content:center;cursor:pointer;padding:0;color:var(--sub)}
#punchHide svg,#punchShow svg{width:17px;height:17px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
#punchShow{display:none;position:fixed;right:18px;bottom:calc(22px + var(--safe-bot));z-index:2100;width:40px;height:40px;border-radius:50%;border:1px solid rgba(255,255,255,.6);background:radial-gradient(60% 42% at 30% 6%,rgba(255,255,255,.72),transparent 50%),rgba(255,255,255,.4);backdrop-filter:blur(24px) saturate(200%);-webkit-backdrop-filter:blur(24px) saturate(200%);box-shadow:0 6px 18px -6px rgba(15,25,45,.35);align-items:center;justify-content:center;cursor:pointer;padding:0;color:var(--sub)}
body.punch-hidden .pill-wrap,body.punch-hidden .punch-target,body.punch-hidden #siteTrack,body.punch-hidden #siteDots{display:none}
body.punch-hidden #punchShow{display:flex}
body.modal-open #punchShow{opacity:0;pointer-events:none}
}
@media (max-width:767px){#punchHide,#punchShow{display:none !important}}
/* ===== v1.40.0 map filter drawer (desktop/tablet only) ===== */
@media (min-width:768px){
#fltDrawer{position:fixed;top:14px;right:0;bottom:14px;width:min(420px,60vw);z-index:2705;transform:translateX(calc(100% - 15px));transition:transform .22s cubic-bezier(.22,.61,.36,1);background:none;border:0;box-shadow:none;padding:0;border-radius:0}
#fltDrawer.open{transform:translateX(0)}
#fltDrawer.tn-dwr-front{z-index:2706}
#fltList{flex:1;min-height:0;overflow-y:auto;margin-top:6px}
#fltDeep{max-height:40%;overflow-y:auto;flex-shrink:0}
}
/* v1.166.0 (Alex): "The filter slider should be sliding in from the right, also similar
   layout at the desktop view slid in from the right of the map page." It was
   display:none below 768px, so phones had no filter panel at all - same geometry as the
   message centre, mirrored. */
@media (max-width:767px){
#fltDrawer{position:fixed;top:var(--dwrTop);bottom:var(--dwrBot);right:0;left:auto;width:min(90vw,440px);z-index:2705;display:flex;align-items:stretch;transform:translateX(calc(100% - 15px));transition:transform .35s cubic-bezier(.22,1,.36,1);background:none;border:0;box-shadow:none;padding:0;border-radius:0}
#fltDrawer.open{transform:translateX(0)}
#fltDrawer.tn-dwr-front{z-index:2706}
#fltList{flex:1;min-height:0;overflow-y:auto;margin-top:6px}
#fltDeep{max-height:40%;overflow-y:auto;flex-shrink:0}
}
#fltResCard{position:fixed;top:14px;right:calc(var(--fltw,min(420px,60vw)) + 22px);width:min(460px,calc(94vw - var(--fltw,min(420px,60vw)) - 40px));max-height:calc(100% - 28px);z-index:2600;display:none;flex-direction:column;border-radius:26px;background:radial-gradient(55% 28% at 28% 0%,rgba(255,255,255,.62),transparent 42%),rgba(248,250,252,.6);backdrop-filter:blur(52px) saturate(200%) brightness(1.08);-webkit-backdrop-filter:blur(52px) saturate(200%) brightness(1.08);border:1px solid rgba(255,255,255,.55);box-shadow:0 18px 44px -18px rgba(15,25,45,.45);padding:16px 16px 12px;animation:tnfade .18s ease}
#fltResCard.open{display:flex}
@media (max-width:767px){#fltResCard{display:none !important}}
.sheet.mb-snap{transition:height .2s cubic-bezier(.22,.61,.36,1),max-height .2s cubic-bezier(.22,.61,.36,1)}
/* v1.98.0 (Alex): "Shouldn't have that background. Card should be seamless." At rest the header
   is exactly the card - no veil, no blur, no seam. The veil still exists, because the title has
   to stay readable over content scrolling under it, but it only fades in once the card is
   actually scrolled, which is never the state you see when a card opens. */
.sh-stick{position:sticky;top:0;z-index:30;margin:0 -20px 4px;padding:2px 96px 8px 20px;background:transparent;transition:background .18s linear}
.sh-stick.stuck{background:linear-gradient(rgba(248,250,252,.5),rgba(248,250,252,.22) 78%,rgba(248,250,252,0));backdrop-filter:blur(26px) saturate(190%);-webkit-backdrop-filter:blur(26px) saturate(190%)}
/* ===== v1.54.0 map UI: controls shift for the filter drawer; zoom-to-extent autozoom toggle ===== */
.map-ctrls{transition:transform .22s cubic-bezier(.22,.61,.36,1)}
@media (min-width:768px){body.flt-open .map-ctrls{transform:translateX(calc(-1 * (var(--fltw, min(420px,60vw)) + 16px)))}}
.map-loc.on{background:radial-gradient(60% 42% at 30% 6%,rgba(255,255,255,.72),transparent 50%),rgba(227,32,42,.18);border-color:rgba(227,32,42,.55)}
.map-loc.on svg{stroke:#E3202A}

/* ===== v1.45.0 Message Center ===== */
.rn-badge{position:absolute;top:50%;right:7px;transform:translateY(-50%);min-width:16px;height:16px;padding:0 4px;border-radius:8px;background:#E3202A;color:#fff;font-size:10px;font-weight:800;display:flex;align-items:center;justify-content:center;line-height:1}
/* v1.173.0: XR's header - the title and its count on the left, the scope pills top right
   on the SAME row, then the line filters under both. */
.mc-hd{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin:2px 0 8px}
.mc-hd-t{min-width:0;flex:1}
.mc-hd h2{font-size:19px;font-weight:800;margin:0}
.mc-hd-sub{font-size:11px;font-weight:700;color:var(--meta);margin-top:2px;text-transform:uppercase;letter-spacing:.02em}
.mc-scopes{display:flex;gap:5px;flex:0 0 auto;flex-wrap:wrap;justify-content:flex-end;max-width:62%}
.mc-chans{display:flex;gap:5px;flex-wrap:wrap;margin:0 0 9px}
#fltChips .ppe-pill{font-size:11px;font-weight:800;padding:4px 9px;margin:0;gap:4px}
.mc-chan{font-size:11px;font-weight:800;color:var(--sub);padding:4px 9px;border-radius:999px;background:rgba(118,131,151,.10);border:1px solid rgba(118,131,151,.22);cursor:pointer;white-space:nowrap}
.mc-chan.on{background:rgba(15,25,45,.86);border-color:rgba(15,25,45,.86);color:#fff}
.mc-chan b{font-weight:800;opacity:.72;margin-left:3px}
.mc-tab{font-size:12px;font-weight:700;color:var(--sub);padding:5px 11px;border-radius:999px;background:rgba(118,131,151,.12);cursor:pointer}
.mc-tab.on{background:#E3202A;color:#fff}
/* TWOCV-2026-08-26-tnp-025 P2-9 (Alex, third complaint: "remove the bevels from the
   buttons/tags. Use standard UI that aligns with rest of the platform."). .mc-tab is worn
   by <span> (payTabs/mcListTabs) AND by <button> (#prTabs), and sets neither `border` nor
   `appearance` -- so the span arm rendered flat and the button arm rendered the UA's
   `border: 2px outset rgb(0,0,0)`. Measured on both builds. Fixed on the BUTTON arm only,
   as an ADDED rule: .mc-tab's own declaration above is asserted byte-for-byte by
   tnp_probe.js v221 (F6-desktop), so it must not be touched. */
button.mc-tab{border:0;-webkit-appearance:none;appearance:none;font-family:inherit;line-height:1.35;margin:0}
/* TWOCV-2026-08-26-tnp-021 (G125 class - Alex, 2026-08-26 screenshot: "Why is the checkbox
   so big?"): v1.219.0's F6 fix ("gains min-height:44px + inline-flex centering")
   shipped .mc-tab VIEWPORT-UNSCOPED, so every desktop caller (payTabs/prTabs/
   mcListTabs all share this one class) inherited the same 44px thumb target built
   for its mobile use. Scoped <768px below; >=768px reverts to the rule above,
   byte for byte its pre-F6 (v1.218.0) text - no min-height/flex override there
   at all. */
@media (max-width:767px){
.mc-tab{min-height:44px;display:inline-flex;align-items:center;justify-content:center}
}
/* v1.175.0 (Alex): "The list should differentiate between the channel to which the messages
   belong to as per the same color coding." XR v6.3.80's `.row.line-*` border, hex for hex. */
.mc-row{display:flex;align-items:center;gap:11px;padding:11px 16px 11px 13px;border-bottom:1px solid rgba(118,131,151,.14);cursor:pointer;position:relative;border-left:3px solid rgba(148,163,184,.25)}
.mc-row.line-dispatch{border-left-color:#0a84ff}
.mc-row.line-dispatch2{border-left-color:#5e9eff}
.mc-row.line-dispatch3{border-left-color:#0a5bbf}
.mc-row.line-payroll{border-left-color:#34c759}
.mc-row.line-onboarding{border-left-color:#84a047}
.mc-row.line-none{border-left-color:rgba(148,163,184,.25)}
/* the header stays put; only this scrolls, and the search starts tucked above the fold */
/* v1.176.0 (Alex): "The color coding should be on the left edge of the screen." The stripe
   was on .mc-row, but the row sits inside the drawer body's 16px padding, so the colour
   floated in from the edge. Bleed the scroller out to the body's own edge and let the ROWS
   carry the inset - which is exactly how XR gets it there (`.row{padding:10px 20px}` in a
   list with no padding of its own). The search keeps its inset so it still lines up with
   the header above it. */
.mc-list-scroll{flex:1 1 auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;margin:0 -16px;padding:0}
#mcListSheet .sheet-body{display:flex;flex-direction:column;overflow:hidden}
#mcListQ{margin:0}
/* the composer is furniture, not content - the thread scrolls under it */
#mcSheet .sheet-body{display:flex;flex-direction:column;overflow:hidden;min-height:0}
#mcSheet .mc-pane{flex:1 1 auto;min-height:0}
#mcSheet .mc-thread{flex:1 1 auto;min-height:0;max-height:none}
.mc-name-row{display:flex;align-items:center;gap:7px;min-width:0}
.mc-name-row #mcName{font-size:17px;font-weight:800;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* v1.179.0 (Alex): "Open contact button beside the x mobile. Then the call button beside
   open contact." They used to sit in the sheet BODY on the row under the X, so the window
   had two right-hand control clusters stacked on each other. In the pull bar now, parked
   left of the X (16px edge + 34px button + 8px gap = 58px) and ordered call - contact so
   that left to right it reads call, contact, X. */
.mc-hd-acts{display:flex;align-items:center;gap:7px;flex-shrink:0}
#mcSheet .pull-bar .mc-hd-acts{position:absolute;right:58px;top:10px;z-index:47}
/* v1.180.0 (Alex): "Call and contact button should be our regular UI button as well, not a
   flat button." They were a flat grey disc, which read as foreign the moment v1.179.0 sat
   them next to the X. TNP's regular round button is the glass one - .sheet-x, .map-loc and
   #punchShow all share it - and this recipe is READ OUT OF .sheet-x by the build rather than
   restated here, so the four controls cannot drift apart. */
.mc-act{width:34px;height:34px;border-radius:50%;background:radial-gradient(60% 42% at 30% 6%,rgba(255,255,255,.72),transparent 50%),rgba(255,255,255,.4);backdrop-filter:blur(24px) saturate(200%);-webkit-backdrop-filter:blur(24px) saturate(200%);box-shadow:0 4px 14px -4px rgba(15,25,45,.3),inset 0 1px 0 rgba(255,255,255,.8);border:1px solid rgba(255,255,255,.6);display:flex;align-items:center;justify-content:center;cursor:pointer;padding:0}
.mc-act svg{width:17px;height:17px;stroke:var(--sub);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.mc-act:disabled{opacity:.35;cursor:default}
.mc-act-call svg{stroke:#16a34a}
/* v1.180.0: the green lives on the glyph, not on a flat fill - the .map-loc.on idiom. */
/* XR's send circle takes the colour of the line it will send from */
.mc-send-bar[data-line="dispatch"] .mc-send-btn:not(:disabled){background:#0a84ff}
.mc-send-bar[data-line="dispatch2"] .mc-send-btn:not(:disabled){background:#5e9eff}
.mc-send-bar[data-line="dispatch3"] .mc-send-btn:not(:disabled){background:#0a5bbf}
.mc-send-bar[data-line="payroll"] .mc-send-btn:not(:disabled){background:#34c759}
.mc-send-bar[data-line="onboarding"] .mc-send-btn:not(:disabled){background:#84a047}
/* unread + empty composer: the circle stays live and pulses, and a tap clears the unread */
.mc-send-btn.mark-read{opacity:1}
.mc-send-btn.mark-read svg{display:none}
.mc-send-btn.mark-read::after{content:"✓";color:#fff;font-size:17px;font-weight:800;line-height:1}
@keyframes mcReadPulse{0%,100%{box-shadow:0 0 0 0 rgba(255,255,255,0)}50%{box-shadow:0 0 0 5px rgba(255,255,255,.28)}}
.mc-send-btn.mark-read{animation:mcReadPulse 1.9s ease-in-out infinite}
.mc-line-wrap{position:relative;display:inline-flex}
.mc-line-pill.dispatch{background:rgba(10,132,255,.18);color:#0a5bbf;border-color:rgba(10,132,255,.35)}
.mc-line-pill.dispatch2{background:rgba(94,158,255,.18);color:#2f6fd0;border-color:rgba(94,158,255,.35)}
.mc-line-pill.dispatch3{background:rgba(10,91,191,.2);color:#08418a;border-color:rgba(10,91,191,.35)}
.mc-line-pill.payroll{background:rgba(52,199,89,.18);color:#1d7c3e;border-color:rgba(52,199,89,.35)}
.mc-line-pill.onboarding{background:rgba(132,160,71,.2);color:#5a7028;border-color:rgba(132,160,71,.38)}
.mc-row.unread .mc-row-name{font-weight:800}
.mc-row.unread::before{content:"";position:absolute;left:5px;top:50%;transform:translateY(-50%);width:6px;height:6px;border-radius:50%;background:#0a84ff}
.mc-row.unread.alert::before{background:#E3202A}
.mc-row-name{font-size:14px;font-weight:700;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mc-row-sub{font-size:12px;color:var(--meta);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* v1.176.0 (Alex): "The filter list buttons / tags should be the same size as the tags on
   the list. They shouldn't be different sizes tags on the two cards." There were THREE
   scales in play across two cards that sit side by side - .mc-badge 10px/2px 7px, .chip
   11px/5px 10px and .ppe-pill 13px/9px 14px. The row tags now share .chip's metrics and the
   filter's status buttons share .mc-chan's, so each kind of control is one size everywhere. */
.mc-badge{font-size:11px;font-weight:700;padding:5px 10px;border-radius:999px}
.mc-badge.red{background:rgba(227,32,42,.14);color:#c01824}.mc-badge.amber{background:rgba(217,119,6,.16);color:#b45309}.mc-badge.blue{background:var(--blue-bg);color:var(--blue)}.mc-badge.slate{background:rgba(118,131,151,.16);color:var(--sub)}
.mc-pane{display:flex;gap:0;align-items:stretch;min-height:0}
.mc-col-thread{flex:1 1 0;min-width:0;display:flex;flex-direction:column;min-height:0}
.mc-col-info{display:none}
.mc-thread{max-height:46vh;overflow-y:auto;display:flex;flex-direction:column;gap:6px;padding:4px 2px;min-height:0}
.mc-divider{display:none}
.mc-search-top{padding:2px 16px 8px}
.mc-search{width:100%;box-sizing:border-box;border:1px solid var(--line);border-radius:999px;background:var(--card);color:var(--ink);font:inherit;font-size:13px;padding:8px 14px;outline:none}
.mc-search:focus{border-color:var(--blue)}
.mc-list-items{min-height:calc(100% + 1px)}
.side-resize{position:absolute;top:0;bottom:0;width:12px;cursor:col-resize;z-index:6;touch-action:none;-webkit-user-select:none;user-select:none}
.side-resize-r{right:-3px}
.side-resize-l{left:-3px}
/* v1.49.0: Payroll module */
.pay-wknav{width:30px;height:30px;border-radius:10px;border:1px solid var(--line);background:var(--card);color:var(--ink);font-size:16px;font-weight:800;cursor:pointer;line-height:1}
.pay-cbw{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:18px;height:18px;margin-top:2px;cursor:pointer}
.pay-cbsp{display:block;width:18px;flex-shrink:0}
/* v1.219.0 (TWOCV-2026-08-25-tnp-019, F6): sub-44px tap-target cluster, all effective
   >=44x44. TWOCV-2026-08-26-tnp-021 (G125) then scoped them <768px, because all FOUR had
   shipped viewport-UNSCOPED and hit desktop too. >=768px still falls back to .pay-wknav's
   pre-F6 rule above (restored byte for byte) and, for the rules with no pre-F6 stylesheet
   entry at all, to nothing - .pay-cb's 18x18 inline style and #prReplySend's
   .big-btn/inline padding govern desktop exactly as they did before F6.
   TWOCV-2026-08-26-tnp-027 (Alex again, on the v1.229.0 phone build: "Why is the checkbox
   so big?"): 44px is the TOUCH FLOOR, not the drawing size, and on a native checkbox the
   two are the same number - the control has no separate ink. Measured on the real build at
   390 wide, painted extent scanned out of a screenshot rather than read off the stylesheet:
   border box 44x44, PAINTED INK 44x44, checked and unchecked. That is the "comedy-sized
   slab" one layer down from the one tnp-021 fixed - it was never a desktop-only problem,
   the phone rule was simply drawing the tap target.
   WHICH SEPARATION TECHNIQUE HOLDS IS DECIDED BY `appearance`, and F6's note above was the
   right measurement with the wrong conclusion drawn from it. Re-measured this session, same
   element, same engine, four candidates:
     - native appearance + padding:11px  -> computed padding 0px, hit box 22x22, 0/4 corner
       clicks land. F6's finding reproduces EXACTLY. That is why F6 resized the ink.
     - native appearance + an 11px transparent border -> computed border-width 0px too, and
       it renders byte-identically to the bug while LOOKING fixed in the stylesheet.
     - appearance:none + padding + background-clip:content-box -> padding IS honoured
       (computed 11px, not 0px), ink 22x22 checked... and 0x0 UNCHECKED. A select control
       you cannot see until it is already selected is not a fix.
     - a <label> wrapper holding a native 22x22 box -> ink 22x22 both states, hit box 44x44,
       4/4 real corner clicks toggle it and none opens the row sheet.  <== shipped
   The wrapper wins on more than the numbers: it changes no rendering at all. The control
   stays the platform checkbox (native check glyph, accent-color, focus ring, AT semantics)
   and the 44px region is a real <label>, whose click forwarding is native behaviour on every
   engine - which matters because this seat can only measure Chromium and the users are on
   iOS Safari. It is also the pattern this file already uses one line below for
   #payBulkAll's own label. .pay-cbsp is the ineligible-row spacer: it has to track the
   wrapper's width or eligible and ineligible rows start their content at different x. */
@media (max-width:767px){
.pay-wknav{width:44px;height:44px;border-radius:14px}
.pay-cb{width:22px!important;height:22px!important;margin:0!important}
.pay-cbw{width:44px;height:44px;margin-top:0}
.pay-cbsp{width:44px}
label:has(#payBulkAll){min-height:44px}
#prReplySend{min-height:44px!important;display:inline-flex!important;align-items:center!important;justify-content:center!important}
}
/* "Select all visible" is already <label><input/>text</label> - native label-for
   semantics already forward a click ANYWHERE in the label to the 13x13 checkbox (verified
   live this session: a real click at the label's far bottom-right corner, clear of the
   checkbox glyph itself, toggled it) - the real gap was vertical height alone, fixed here
   with zero markup change via :has(). */
/* v1.207.0: the payroll week arrow says WHERE the unreviewed contexts are - same red
   ring + stGlow pulse the site timesheet arrows have worn since v1.115.0. */
#payWkPrev.pay-wglow,#payWkNext.pay-wglow{border-color:#E3202A!important;color:#E3202A!important;animation:stGlow 1.6s ease-in-out infinite}
.pay-tile{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:10px 12px}
.pay-tile .k{font-size:10px;color:var(--meta);font-weight:700;text-transform:uppercase}
.pay-tile .v{font-size:20px;color:var(--ink);font-weight:800;margin-top:2px}
/* v1.219.0 (TWOCV-2026-08-25-tnp-019, F8): repeat(4,1fr) is really minmax(auto,1fr) - a
   track whose own content (an uppercase 2-word label) needs more than its fair 1/4 share
   resists shrinking, so columns end up unequal and the container overflows its own box
   (measured this session: computed track widths 89.9/69.9/89.4/81.3px summing to 330 -
   not 4x75 - inside a 324px box, scrollWidth 354). minmax(0,1fr) makes 1fr mean an EQUAL
   quarter unconditionally. The inline style attribute (unchanged, still repeat(4,1fr))
   beats a plain stylesheet rule for the SAME property, hence !important - matching
   #prReplySend's own use of the same technique above. Equal columns alone don't stop a
   2-word label wrapping to 2 lines at ~75px (measured: still wraps, and further shrinking
   + a smaller font just CLIPS the text instead - worse than wrapping, measured); a 2-up
   grid on phones gives every label >=130px, comfortably one line with room to spare
   (measured 132/144px unclipped, both tested widths). */
#payTiles{grid-template-columns:repeat(4,minmax(0,1fr))!important}
@media (max-width:767px){
#payTiles{grid-template-columns:repeat(2,minmax(0,1fr))!important}
}
/* v1.204.0: payroll dashboard - stats hero, side sites panel, inline tools */
.pd-hero{margin-bottom:14px}
.pd-tiles{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:8px}
@media(max-width:900px){.pd-tiles{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media(max-width:520px){.pd-tiles{grid-template-columns:repeat(2,minmax(0,1fr))}}
.pd-tile{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:11px 12px 10px;position:relative;overflow:hidden}
.pd-tile::before{content:"";position:absolute;top:0;bottom:0;left:0;width:3px;background:var(--pdrail,transparent)}
.pd-tile .k{font-size:10px;color:var(--meta);font-weight:800;letter-spacing:.06em;text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pd-tile .v{font-size:23px;font-weight:800;color:var(--ink);line-height:1.15;margin-top:2px}
.pd-tile .v small{font-size:12px;font-weight:700;color:var(--sub)}
.pd-tile .d{font-size:11px;font-weight:700;margin-top:1px;color:var(--meta)}
.pd-tile .d.up{color:#0e7a37}.pd-tile .d.dn{color:#B3261E}
.pd-charts{display:grid;grid-template-columns:minmax(0,1.55fr) minmax(0,1fr);gap:10px;margin-top:10px}
@media(max-width:900px){.pd-charts{grid-template-columns:minmax(0,1fr)}}
.pd-chart-card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:13px 15px 11px;min-width:0}
.pd-ch-hd{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:8px;flex-wrap:wrap}
.pd-ch-t{font-size:11px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--meta)}
.pd-leg{display:flex;gap:12px;font-size:11px;font-weight:700;color:var(--sub)}
.pd-leg i{display:inline-block;width:9px;height:9px;border-radius:3px;margin-right:5px;vertical-align:-1px}
.pd-bars{width:100%;height:auto;display:block}
.pd-bars .grid{stroke:rgba(15,23,42,.06);stroke-width:1}
.pd-bars .ax{fill:var(--meta);font-size:9.5px;font-weight:600;font-variant-numeric:tabular-nums}
.pd-bars .wk{fill:var(--sub);font-size:9.5px;font-weight:800}
.pd-col{cursor:pointer;outline:none}
.pd-col:hover rect.rg,.pd-col:hover path.rg,.pd-col:focus-visible rect.rg,.pd-col:focus-visible path.rg{filter:brightness(.92)}
.pd-pipe{display:flex;gap:2px;height:16px;border-radius:8px;overflow:hidden;margin:8px 0 10px}
.pd-pipe i{display:block;min-width:3px}
.pd-chips{display:flex;flex-wrap:wrap;gap:6px}
.pd-chips .chip{cursor:pointer}
.pd-tip{position:fixed;z-index:4000;background:#0e1726;color:#fff;border-radius:10px;padding:8px 11px;font-size:12px;font-weight:600;line-height:1.5;pointer-events:none;box-shadow:0 6px 20px -6px rgba(15,25,45,.45);max-width:240px;display:none}
.pd-tip b{font-weight:800}
.pd-tip .m{color:#b6c2d6;font-weight:600}
.pd-cols{display:grid;grid-template-columns:minmax(0,1fr);gap:14px;align-items:start}
.pd-side{order:2;min-width:0}.pd-main{order:1;min-width:0}
@media(min-width:1100px){
  .pd-cols{grid-template-columns:320px minmax(0,1fr)}
  .pd-side{order:1}.pd-main{order:2}
}
.pd-tools{padding:16px 18px}
.pd-sites{padding:16px 18px}
/* ===== TWOCV-2026-08-26-tnp-026 D (tnp-018 P3 + Alex's refinement): the Pending
   Timesheets row is TWO adjacent hit targets that tile the whole row. The name half opens
   the account card (exactly what the old whole-row handler did); the (N timesheets) tag is
   now a real button that expands the row IN PLACE to its N timesheet records. The row's own
   click handler is gone, so there is no third target underneath these two, and because the
   two buttons share an edge with zero gap there is no dead pixel between them -- the two
   ways a phone punishes a split row. */
.pd-prow{padding:0;gap:0;align-items:stretch;cursor:default}
.pd-hit{display:flex;align-items:center;gap:10px;background:none;border:0;font:inherit;color:inherit;text-align:left;cursor:pointer;min-height:56px;padding:10px 8px;touch-action:manipulation}
.pd-hit-name{flex:1 1 auto;min-width:0}
.pd-hit-exp{flex:0 0 auto;border-left:1px solid var(--line);padding:10px 12px;gap:6px}
.pd-hit:active{background:rgba(118,131,151,.10)}
.pd-nm{font-size:14px;font-weight:700;color:var(--ink);line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}
.pd-mt{font-size:12px;color:var(--meta);font-weight:600;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}
.pd-hit-name>span{flex:1 1 auto;min-width:0}
.pd-caret{width:14px;height:14px;flex:0 0 14px;stroke:#8894a8;fill:none;stroke-width:2.6;stroke-linecap:round;stroke-linejoin:round;transition:transform .15s ease}
.pd-prow.pd-open .pd-caret{transform:rotate(180deg)}
.pd-sub{border-bottom:1px solid var(--line);background:rgba(118,131,151,.05);padding:4px 0}
.pd-subrow{display:flex;align-items:center;gap:8px;width:100%;background:none;border:0;font:inherit;color:inherit;text-align:left;cursor:pointer;min-height:52px;padding:8px 10px 8px 18px;touch-action:manipulation}
.pd-subrow:active{background:rgba(118,131,151,.12)}
.pd-sr-main{flex:1 1 auto;min-width:0}
.pd-sr-nm{font-size:13.5px;font-weight:700;color:var(--ink);display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pd-sr-mt{font-size:11.5px;color:var(--meta);font-weight:600;display:block;margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pd-subnote{font-size:11.5px;color:var(--meta);font-weight:600;padding:8px 10px 8px 18px;line-height:1.35}
/* Splitting the row into two targets costs the name half ~140px, and at 390 that turned
   every site into "Turner Canadia..." -- measured in the render, which is the only place it
   was visible; every rect assertion was green while the text was unreadable. Under 768px the
   name and the record line WRAP (clamped at two lines) instead of ellipsing, which trades a
   ~16px taller row for a legible one. Above 768px they keep the single-line ellipsis, since
   the name half is wide enough there for the longest real account name. */
@media (max-width:767px){
.pd-nm,.pd-sr-nm{white-space:normal;overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.pd-mt,.pd-sr-mt{white-space:normal;overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.pd-hit-exp{padding:10px 10px;gap:5px}
}
/* E: the punch mark is its OWN line under the site's meta line, and never the (N timesheets)
   tag -- that tag is item D's expand target and expands to N records, so it has to keep
   meaning N in both filter modes. Its own line rather than inline because at 390 the name
   half is ~185px: measured, an inline chip broke "Earliest week Aug 17" across two lines and
   left the "17" stranded next to the chip. */
.pd-pchip{display:inline-block;margin-top:3px;font-size:11px;font-weight:800;color:var(--blue);background:rgba(44,92,229,.13);border-radius:999px;padding:1px 7px}
#pdFilt .mc-tab{flex:0 0 auto}
.pd-tools-hd{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:2px 0 4px;flex-wrap:wrap}
.pay-row{display:flex;align-items:center;gap:11px;padding:11px 6px;border-bottom:1px solid rgba(118,131,151,.14);cursor:pointer}
/* TWOCV-2026-08-26-tnp-025 P0-2: the two-column decision no longer rides an id-scoped
   container query, and no longer rides a container query at all.

   WHY the query had to GO rather than be re-scoped to a class. Two independent defects sat
   on top of each other. (a) `#prSheet { container-type: inline-size }` is id-scoped, and
   openPayThread/openPayCtx open the pane through mkInstance('prSheet'), which CLONES the node
   and renames it prSheet__iN -- so the element a user actually sees was never a query
   container and `@container (min-width:1080px)` could not match at ANY width. Re-scoping to a
   class fixes (a) and leaves (b) standing: (b) a container query can only compare against a
   CONSTANT, and the width that decides this layout is not constant. --pr-workw is written at
   runtime by prTableWSet (clamp 620..720) and the operator can drag the hours columns, so any
   fixed threshold is wrong for some column layout the operator can produce with a mouse.

   So the decision is computed in prLayoutSync() from the two measured inputs -- the pane's own
   content width, and --pr-workw as it currently stands -- and published as `.pr-2col` on the
   pane ROOT. classList survives cloneNode, so the template and every clone behave identically;
   that equivalence is what makes a harness result on either path meaningful.

   --pr-gap / --pr-evidmin / --pr-pad2 live here so the arithmetic and the CSS read the SAME
   numbers: prLayoutSync reads them off the computed style rather than carrying its own copy,
   which is what stops the threshold and the layout drifting apart the way 1080 and 1106 did. */
.pr-sheet{--pr-gap:20px;--pr-evidmin:360px;--pr-pad2:12px}
.pr-pane{display:flex;flex-direction:column;gap:14px;min-height:0}
/* TWOCV-2026-08-26-tnp-025 P1-6: the 62vh HEIGHT cap is gone. It limited the scan to
   368px of width on a 1366x768 panel -- narrower than the 390px phone the desktop pane
   exists to beat -- and it did so on the axis nobody was looking at. The scan is now
   bounded by its COLUMN, and the column is a scroller (see .pr-col-img), so a taller
   sheet scrolls at full width instead of shrinking to fit. */
.pr-col-img img{max-width:100%;object-fit:contain;display:block;border-radius:14px;border:1px solid var(--line);cursor:zoom-in;background:#eee}
.pr-col-evidence{display:flex;flex-direction:column;min-height:0;min-width:0}
/* stacked mode: the scan is FIRST and inline, and bounded so the thread beneath it is
   reachable without scrolling past a full page of timesheet (open item 2, stacked half). */
.pr-col-img{flex:0 0 auto;max-height:56vh;overflow:auto;overscroll-behavior:contain}
.pr-strip{flex:0 0 auto}
.pr-day{display:grid;grid-template-columns:var(--pr-cols,140px 150px 150px 64px 96px 30px);gap:6px;align-items:center;padding:7px 4px;border-bottom:1px solid rgba(118,131,151,.14);font-size:13px}
#prRows{overflow-x:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;-webkit-mask-image:linear-gradient(to right,#000,#000 calc(100% - 28px),transparent);mask-image:linear-gradient(to right,#000,#000 calc(100% - 28px),transparent)}
.pr-colh{position:absolute;top:0;right:-3px;width:10px;height:100%;cursor:col-resize;z-index:3;touch-action:none}
.pr-day input{border:1px solid var(--line);border-radius:9px;padding:6px 7px;font:inherit;font-size:12.5px;background:var(--card);color:var(--ink);width:100%;box-sizing:border-box}
.pr-day .conf{font-size:10px;font-weight:800;text-align:center;border-radius:8px;padding:3px 0}
.pr-day.rejected{opacity:.45;text-decoration:line-through}
/* TWOCV-2026-08-26-tnp-026 A/B (Alex on the live v1.228.0 phone build: "I can't edit hours
   in mobile ... Why a big Reject Row button?").
   tnp-023 fixed tnp-019's measured 678px-wide #prRows-inside-a-350px-window by rendering a
   SECOND, read-only summary card per row (the .pr-dc-* family) and CSS-picking one set per
   viewport. That killed the horizontal scroll and killed editing with it -- the comment
   that used to sit here said so itself ("full inline field editing on a 390px screen is NOT
   built in this batch"). A reviewer on a phone could read an extraction and not correct
   one, which is the job.
   The parallel card set is RETIRED. One row, one set of inputs, one source of truth, and
   under 768px the REAL row reflows into a card via grid-template-areas:
       [       DATE (spans)       ][x]     x = the row action, top-right, 44x44
       [ IN        ][ OUT        ][x]
       [ LUNCH     ][ HOURS+conf ][x]
       [ note (confidence reason / promoted-lock) spanning the foot ]
   Labels: the DATE/IN/OUT/LUNCH/HOURS header row is desktop-only (it carries the column
   resize handles), so each input is wrapped in a .pr-cell whose data-l is painted by
   .pr-cell::before under 768px ONLY -- a reflowed card with unlabelled time fields would
   be worse than the read-only card it replaces. The wrapper is invisible to every existing
   consumer: they all reach the inputs by descendant querySelector('[data-f=...]'), and
   prCheckRow's one upward walk (hh.parentNode -> the hours cell it outlines red) still
   lands on the same inner flex div, because .pr-c-h wraps that div rather than replacing
   it. */
.pr-cell{min-width:0}
.pr-cell::before{display:none}
.pr-dnote{display:none}
@media (max-width:767px){
/* no horizontal scroll left to fade: the row reflows instead of overflowing, so the
   mask-image affordance tnp-019 measured -- and the scroll it was apologising for -- both
   go. */
#prRows{overflow-x:visible;-webkit-mask-image:none;mask-image:none}
.pr-day.pr-dayhd{display:none}
.pr-day.pr-daytot{display:flex;justify-content:flex-end;align-items:center;gap:10px}
.pr-day.pr-daytot>div:empty{display:none}
.pr-day[data-i]{grid-template-columns:1fr 1fr 44px;grid-template-areas:"d d x" "s e x" "l h x" "n n n";gap:9px 8px;align-items:start;padding:12px 2px 13px}
.pr-cell::before{display:block;content:attr(data-l);font-size:10.5px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--meta);margin-bottom:4px;line-height:1}
.pr-c-d{grid-area:d}.pr-c-s{grid-area:s}.pr-c-e{grid-area:e}.pr-c-l{grid-area:l}.pr-c-h{grid-area:h}
.pr-c-x{grid-area:x;align-self:start;justify-self:end}
/* 16px is not a taste choice: iOS Safari zooms the viewport on focus for any input under
   16px, and on THIS pane that throws away the scan the reviewer is correcting against. */
.pr-day[data-i] input{min-height:44px;font-size:16px;padding:9px 10px}
.pr-day[data-i] .conf{min-height:44px;display:flex;align-items:center;justify-content:center;font-size:11px}
.pr-dnote{display:block;grid-area:n;font-size:11.5px;color:#b45309;font-weight:700;line-height:1.35}
.pr-dnote.dim{color:var(--meta);font-weight:600}
/* ITEM B: .pay-wknav is ALREADY 44x44 under 768px (the v1.225.0 F6 block above), so the
   touch floor is inherited, not re-invented. What the retired full-width reject slab was
   buying was READING, not target area -- so give the glyph the destructive reading and keep
   the corner. */
.pr-rowx{border-color:rgba(179,38,30,.35);background:rgba(179,38,30,.07);color:#B3261E;font-size:19px}
}
.pr-ts-card{border:1.5px solid var(--line);border-radius:14px;padding:12px;margin-bottom:8px;cursor:pointer}
.pr-ts-card.linked{border-color:var(--green)}
.pr-ts-card.warn{border-color:#d97706}
.ts-sf{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:9px;border:1px solid var(--line);color:var(--sub);flex-shrink:0;cursor:pointer;text-decoration:none}
.ts-sf svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.ts-sf:hover{color:var(--blue);border-color:var(--blue)}
/* ===== v1.217.0 (TWOCV-2026-08-25-tnp-018 P1): Timesheet Record Thread pane =====
   Reuse-only (DESIGN.md sec 13): every rule below composes existing tokens (.card/.chip/
   .mc-tab/--ink/--ink2/--meta/--line) - the only new class NAMES are structural. */
.pr-mtabs{display:flex;gap:6px;margin:2px 0 10px}
.pr-col-thread{min-width:0;position:relative;display:flex;flex-direction:column;min-height:0}
#prThreadBody{overflow-y:auto;overscroll-behavior:contain;min-height:0}
.pr-thread-rail{display:none}
.pr-thread-item{border:1px solid var(--line);border-radius:14px;padding:10px 12px;margin-bottom:8px;background:var(--card)}
.pr-thread-item.system{border:none;background:none;padding:2px 4px;margin-bottom:4px;font-size:12px;color:var(--meta)}
.pr-ti-hd{display:flex;align-items:center;gap:8px;cursor:pointer;font-size:12.5px;min-height:32px}
/* TWOCV-2026-08-26-tnp-023 D4 (F-4, P2): was unscoped 44px (pays mobile's touch
   budget on every desktop width -- 5 thread items x 44px = 220px of chrome for 5
   one-line rows at 1600x900). tnp-021's own rule ("44px scoped <768px, desktop back
   to compact") did not get applied to this rule; this extends it here, base declaration
   above now 32px, 44px only under the same <768px media query every other scoped
   control in this file already uses. */
@media (max-width:767px){.pr-ti-hd{min-height:44px}}
.pr-ti-who{font-weight:800;color:var(--ink);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pr-ti-snip{font-size:12px;color:var(--meta);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:2px;cursor:pointer}
.pr-ti-time{font-size:11px;color:var(--meta);font-weight:600;flex-shrink:0}
.pr-ti-chev{flex-shrink:0;transition:transform .15s;color:var(--meta)}
.pr-thread-item.open .pr-ti-chev{transform:rotate(180deg)}
.pr-ti-body{display:none;margin-top:8px;padding-top:8px;border-top:1px solid var(--line)}
.pr-thread-item.open .pr-ti-body{display:block}
.pr-ti-text{font-size:13.5px;line-height:1.5;white-space:pre-wrap;color:var(--ink2)}
.pr-anchor-slot:empty{display:none}
/* ==== TWOCV-2026-08-26-tnp-023: new pane components (DESIGN.md Sec 2/3/4.1) ==== */
/* header band top-right (R4): Apply rows / Clone hours, compact, always visible -- replaces
   the old full-width buttons that used to live at the bottom of the button stack. */
.pr-headwrap{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;flex-wrap:wrap}
.pr-headwrap #prHead{flex:1;min-width:0}
.pr-hacts{display:flex;gap:6px;flex-wrap:wrap;flex-shrink:0}
.pr-hbtn{border:0;background:transparent;border-radius:8px;height:32px;padding:0 9px;font:inherit;font-size:12px;font-weight:700;color:var(--sub);cursor:pointer;white-space:nowrap}.pr-hbtn:hover:not(:disabled){background:rgba(118,131,151,.12);color:var(--ink)}.pr-hbtn:disabled{opacity:.42;cursor:default}
.pr-hbtn[disabled]{opacity:.4;cursor:not-allowed}
/* R6 field list -- replaces the Summary__c prose paragraph. */
.pr-fl{display:grid;grid-template-columns:120px 1fr auto;gap:7px 10px;align-items:baseline;margin:0 0 16px}
.pr-fl dt{font-size:12px;font-weight:600;color:var(--meta)}
.pr-fl dd{margin:0;font-size:13px;font-weight:700;color:var(--ink);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pr-fl-mk{justify-self:end;white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}
.pr-fl-notes{grid-column:1/-1;font-size:12.5px;font-weight:500;color:var(--sub);white-space:pre-wrap;border-top:1px dashed var(--line);padding-top:8px;margin-top:2px}
@media (max-width:767px){
.pr-fl{grid-template-columns:1fr;gap:2px 0}
.pr-fl dt{grid-column:1;margin-top:8px;font-size:11px;font-weight:800;letter-spacing:.03em;text-transform:uppercase}
.pr-fl dt:first-child{margin-top:0}
.pr-fl dd{grid-column:1;white-space:normal;overflow:visible;text-overflow:clip}
.pr-fl-mk{grid-column:1;justify-self:start}
}
/* sticky action bar (R5): one primary + an overflow menu, replacing the 4-button slab. */
/* TWOCV-2026-08-26-tnp-025 open item 3: OPAQUE. A 0.97 alpha over a backdrop-filter is
   exactly the translucency complaint -- the hours rows showed through the bar that was
   supposed to be covering them. It now also sits as a flex sibling of the work column's
   scroller rather than relying on sticky inside a container that never scrolled. */
.pr-actbar{position:sticky;bottom:0;background:#fff;border-top:1px solid var(--line);padding:10px 2px 2px;display:flex;gap:8px;align-items:flex-start;flex-wrap:wrap;box-shadow:0 -6px 14px -10px rgba(16,24,40,.35);flex:0 0 auto}
/* R5, measured in the render: .pr-actwarn is flex-basis:100%, so it forced a wrap BETWEEN
   the primary and the overflow button and the bar resolved to 129px in three rows -- on a
   column whose whole scroller is ~480px. Ordering puts the primary and the overflow menu
   on one row and the warning under them, which is what "one clear primary" looks like
   and buys ~50px back for the hours table. */
/* TWOCV-2026-08-26-tnp-026 C (Alex, on the 390px render: "the promote area looks so
   off"): the warning moves ABOVE the primary so it reads as that button's context instead
   of an orphaned third stratum under it. This does NOT undo the v1.228.0 fix directly
   above -- that one stopped .pr-actwarn's flex-basis:100% wrapping BETWEEN the primary and
   the overflow button. Primary and overflow still share one row and the bar is the same
   three rows tall; only which row is first changed. */
.pr-actbar .pr-actwarn{order:0}
.pr-actbar .pr-primary{order:1}
.pr-actbar .pr-more-wrap{order:2}
.pr-actbar .pr-tagrow{order:4}
.pr-primary{flex:1 1 auto;min-width:140px;padding:13px;border:none;border-radius:13px;background:var(--blue);color:#fff;font:inherit;font-size:14.5px;font-weight:800;cursor:pointer}
.pr-primary[disabled]{background:rgba(118,131,151,.25);color:var(--meta);cursor:not-allowed}
.pr-actwarn{flex-basis:100%;font-size:12px;color:#b45309;font-weight:700}
.pr-more-wrap{position:relative;flex:0 0 auto}
.pr-more-btn{width:44px;height:44px;border:1px solid var(--line);border-radius:13px;background:#fff;font-size:18px;color:var(--sub);cursor:pointer}
.pr-more-menu{display:none;position:absolute;bottom:calc(100% + 6px);right:0;min-width:190px;background:var(--glass);backdrop-filter:blur(14px) saturate(160%);border:1px solid var(--line);border-radius:12px;padding:6px;box-shadow:0 12px 30px -12px rgba(15,25,45,.4);z-index:5}
.pr-more-menu.open{display:flex;flex-direction:column;gap:2px}
.pr-more-item{border:none;background:none;text-align:left;padding:10px 10px;border-radius:8px;font:inherit;font-size:13px;font-weight:700;color:var(--ink2);cursor:pointer;min-height:38px}
.pr-more-item:hover{background:rgba(118,131,151,.12)}
.pr-more-danger{color:#B3261E}
/* TWOCV-2026-08-26-tnp-026 C, the mobile half: at 390 this dock read as three unrelated
   strata -- a full-bleed blue primary, a detached overflow button, a three-line amber
   paragraph, then two tag pills flush to the sheet edge. Composed as ONE block: real side
   padding (nothing flush any more), the warning as a tinted bordered panel visibly
   attached to the primary below it, and the tag row demoted behind a hairline rule and a
   small caption. The chips reach the <768px 44px touch floor through PADDING, so they stay
   chip-sized and clearly secondary to the one blue verb above them rather than becoming a
   second pair of buttons. */
.pr-actwarn-dim{color:var(--meta)}
.pr-tagl{font-size:10.5px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--meta);flex:0 0 auto}
@media (max-width:767px){
.pr-actbar{padding:12px 12px 14px;gap:10px;border-radius:16px 16px 0 0;box-shadow:0 -10px 22px -14px rgba(16,24,40,.45)}
.pr-actwarn{background:rgba(217,119,6,.10);border:1px solid rgba(217,119,6,.28);border-radius:12px;padding:9px 11px;line-height:1.35;margin:0}
.pr-actwarn-dim{background:rgba(118,131,151,.10);border-color:rgba(118,131,151,.26)}
/* NOT touching the primary's height: the v1.225.0 F6 block already pins it with
   `.pr-actbar .pr-primary{min-height:44px}`, which outranks anything this block could say
   at one class of specificity -- measured in the 390 render, where a `.pr-primary
   {min-height:52px}` written here resolved to 44px and was dead text. 44 is also the right
   answer: it is the app-wide touch floor, and a 52px full-width blue slab is the oversized
   button Alex has now objected to five times.
   The tag chips sit at the base .chip's own 11px so BOTH fit on one row at 390 (measured:
   at 12px/14px padding they were 185+191 and wrapped to two rows with the caption stranded
   beside the first). Height still clears 44px, through padding. */
.pr-tagrow{margin-top:2px;padding-top:11px;border-top:1px solid var(--line);gap:8px;align-items:center}
.pr-tagrow .chip{min-height:44px;padding:0 12px;font-size:11px}
}
.pr-tagrow{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap;flex-basis:100%}
/* thread reply/forward, now icon buttons at the thread's own bottom-right (R2). */
.pr-thread-foot{display:flex;justify-content:flex-end;gap:6px;padding:8px 2px 0;flex:0 0 auto}
.pr-ibtn{width:32px;height:32px;border:1px solid var(--line);border-radius:9px;background:#fff;color:var(--sub);font-size:14px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}
@media (max-width:767px){.pr-ibtn{width:44px;height:44px}.pr-more-btn{width:44px}.pr-more-item{min-height:44px}}
/* evidence/work columns + mobile 3-tab visibility. Desktop (>=1180px pane) sizing lives
   in the @container block below; this part is viewport-driven (mobile tabs) so it stays
   media-query, matching every other viewport-only rule in this file (.pr-mtabs etc). */
.pr-col-work{display:flex;flex-direction:column;min-height:0;min-width:0}
.pr-work-scroll{flex:1 1 auto;overflow-y:auto;overscroll-behavior:contain;min-height:0;padding-right:2px}
/* TWOCV-2026-08-26-tnp-025 P0-4 + the producer's hard requirement ("there is NO width at
   which the scanned timesheet image is hidden behind a tab").

   v1.227.0's phone pane rendered EMPTY on the live wire: #prImgCol width 0, #prActionDock 0,
   #prRows 0x0 -- only the worker name, a chip and two bevelled pills. Mechanism: 3-tab CSS
   against 2-tab served markup, with mtab='review' matching neither pill, so every column was
   display:none. Re-syncing the two would fix that instance and leave the mechanism: a layout
   in which a class mismatch can empty the pane is the wrong mechanism regardless of which
   build ships.

   So the pills stop switching visibility. Everything is in ONE stacked scroll flow, in
   markup order -- image FIRST, thread under it, hours last -- and the pills scroll to a
   section (prTabScroll). The pr-tab-review/-sheet/-thread CLASSES stay on #prPane because
   they are the pane's "which section is current" state and tnp_probe v217->tnp-023 asserts
   them; they now only mark the active pill.

   The touch-target rules below are pane-scoped repairs of what the harness measured under
   768px: .pr-hbtn was 32px and the work column's .big-btn pair 36px, against tnp-019/021's
   own 44px floor. */
@media (max-width:767px){
.pr-col-evidence{display:flex;flex-direction:column}
.pr-col-img{max-height:52vh}
.pr-hbtn{height:44px}
.pr-work-scroll .big-btn{min-height:44px}
.pr-actbar .pr-primary{min-height:44px}
.pr-sheet .sheet-x{width:44px;height:44px;top:5px}
.pr-sheet #prSfBtn{margin-right:14px}
/* measured in the 390px render: #prHead and #prHeadActs share one flex row, the two header
   controls take ~250px of a 350px pane, and the worker name wrapped to FOUR lines. The name is
   the first thing a reviewer reads, so it takes the row; the controls wrap under it and
   margin-left:auto keeps them right-aligned (R4) instead of falling to flex-start, which is
   where a lone item on a space-between line would otherwise go. */
.pr-headwrap #prHead{flex:1 1 100%;min-width:100%}
.pr-hacts{margin-left:auto}
}
@media (max-width:767px){
/* TWOCV-2026-08-26-tnp-023 L1: mobile 3-tab visibility rules moved up next to the other
   new-component CSS (see apply_new_component_css) so this block only holds what still
   IS viewport/breakpoint logic: the old pr-tab-image/pr-tab-thread 2-tab pair is retired
   (superseded by pr-tab-review/pr-tab-sheet/pr-tab-thread, 3 tabs, DESIGN.md Sec 2.2). */
}
/* TWOCV-2026-08-26-tnp-025 P1-6: the pane is no longer capped, and the 768-1099 tier no
   longer caps it at 1020px -- which is BELOW the fit floor, so two columns were structurally
   impossible in that whole band at any window width. One formula now, matching the float-mode
   inline JS override in openPayCtx/openPayThread exactly, so the template path and the real
   (cloned, float) path lay out the same and a measurement on either means the same thing.
   The selector moves off the id for the same reason as everything else in this batch:
   mkInstance renames #prSheet to #prSheet__iN, so an id-scoped rule is a rule that only ever
   applies to the element no user opens. */
@media (min-width:768px){
.pr-sheet.open{width:calc(100vw - var(--railw) - 40px)}
}
/* TWOCV-2026-08-26-tnp-025 P0-2 / P1-5 / P1-6 / open items 2 and 3.
   Reviewer evidence: WIDTH_MATRIX.md sec 1 + min_twocol.json + measurements.json.

   P1-5, the threshold IS the arithmetic. Shipped v1.227.0 used @container (min-width:1080px),
   which is 26px BELOW the width at which these columns stop overflowing (684 + 20 + 360 + 42
   = 1106), so crossing it made the scan SMALLER rather than larger -- the evidence column
   snapped to its 360px floor. prLayoutSync() now requires
       pane content width >= --pr-workw + --pr-gap + --pr-evidmin
   with all three read off the live element, so the answer tracks a column drag.

   THE RAIL IS EXPANDED BY DEFAULT -- m6:5404, `if(_rs!=='0'){document.body.classList.add
   ('railx')}` -- so --railw is 220px, not 64px, for every user who has never touched it, and
   the pane formula subtracts it. That is the hidden variable behind two failed guesses: the
   prior harness pinned tnp_rail='0' and therefore measured the pane 156px WIDER than a default
   user ever sees. Every number below is at the DEFAULT (expanded) rail.
       pane width      = vw - 220 - 40
       X.clientWidth   = pane - 2 (the desktop .sheet 1px side borders)
       available       = X.clientWidth - 2 x --pr-pad2
       fit floor       = 684 + 20 + 360 = 1064
   -> two columns engage from a 1350px viewport at the default rail (1194 collapsed), so the
   two laptops Alex most plausibly uses -- 1366 and 1440 -- are two-column, which today they
   are not at ANY width. 1280 at the default rail leaves 994px of usable content and cannot
   fit two columns at any evidence floor above ~290px; it stacks image-first, which is the
   honest answer and is what the matrix's own 1280 row says happens.
   --pr-evidmin is the reviewer's own measured fit floor (360), not a number invented here.
   FLAGGED: at the very narrowest two-column pane the scan is ~360px wide, which is under
   WIDTH_MATRIX's U4 ("beats the 390px phone it replaced"); it clears 390 from ~1380 upward.
   Raising --pr-evidmin to 420 would restore U4 and push the engage point out to ~1410, which
   would leave 1366 stacked -- i.e. would trade the defect Alex is reporting for a cosmetic
   one. The engage point wins; U4 is named here so a human can overrule it in one number.

   --pr-pad2 (12px) is the pane's own side padding in two-column mode, down from .sheet-body's
   20px. It buys 16px of usable width and, more importantly, it is what makes the decision
   state-INDEPENDENT: prLayoutSync always asks "would two columns fit at the two-column
   padding?", so switching cannot change the answer to its own question and the layout cannot
   flap at the boundary.

   P1-6, nothing here caps the pane. The evidence column takes every pixel the work column
   does not need, so 1920 and 2560 spend their width on the document being audited instead of
   on margin.

   OPEN ITEM 3 -- the sticky bar was drawn ON TOP of the hours rows. `.pr-col-work
   {max-height:100%}` resolved against an auto-height `.pr-pane`, a percentage height against
   an indefinite parent is ignored, so `.pr-work-scroll{overflow-y:auto}` never had anything to
   scroll and `position:sticky` had no scroller to stick to. Fixed structurally: in two-column
   mode `.sheet-body` stops being the scroller, `#prPane` takes the remaining height, and each
   column owns a bounded scroller. The bar is also opaque now (see .pr-actbar).

   OPEN ITEM 2 -- the thread starts 207-278px BELOW the viewport at 1280x800 / 1600x900 /
   1920x1080 even on the working template path, because image-over-thread in ONE unbounded
   scroller means the thread is only reachable by scrolling past the scan. Chosen: BOUNDED
   SCROLLERS. The image and the thread each get their own scroller inside a height-bounded
   evidence column, so the thread is on screen at every laptop height AND the scan still
   renders at full column width. Rejected: defaulting the thread to its collapsed rail --
   that hides the correspondence which explains the numbers, which is the same class of defect
   as hiding the scan behind a tab, and it would have been a workaround for a height problem
   rather than a fix. Also rejected: shrinking the image to fit the column height, which is
   what the 62vh cap did and is why 1366x768 rendered a 368px scan. */
/* Height, and why it is not the 78vh/860px "comfortable opener" every other float sheet gets
   (v1.129.0): this is the only sheet with THREE independent scrollers, and at 78vh on a 900px
   window the work column resolved to 610px, of which the action bar took 129 and the field
   summary 318 -- leaving ~160px, about ONE visible row of the extracted-hours table, which is
   the primary work surface. Measured in the render, not reasoned about. 88vh/940px gives the
   hours table roughly three times that. openPayCtx/openPayThread re-run place()'s own
   bottom-of-viewport correction after the class lands, because the height changes AFTER
   __tnpWin.place() has already positioned the window. */
.pr-2col.sheet{height:min(88vh,940px);max-height:min(88vh,940px)}
.pr-2col .sheet-body{display:flex;flex-direction:column;overflow:hidden;padding-left:var(--pr-pad2,12px);padding-right:var(--pr-pad2,12px)}
.pr-2col .pr-pane{flex:1 1 auto;flex-direction:row;align-items:stretch;gap:var(--pr-gap,20px);min-height:0}
.pr-2col #prTabs{display:none}
.pr-2col .pr-col-evidence{flex:1 1 auto;min-width:var(--pr-evidmin,360px);display:flex;flex-direction:column;min-height:0}
.pr-2col .pr-col-work{flex:0 0 var(--pr-workw,684px);display:flex;flex-direction:column;min-height:0;max-height:none;position:static}
.pr-2col .pr-work-scroll{flex:1 1 auto;overflow-y:auto;min-height:0}
.pr-2col .pr-ti-text{max-width:72ch}
.pr-2col .pr-col-img{flex:1 1 58%;min-height:180px;max-height:none;display:block;overflow:auto;overscroll-behavior:contain}
.pr-2col .pr-col-thread{flex:1 1 42%;min-height:160px;min-width:0}
.pr-2col #prThreadBody{flex:1 1 auto;overflow-y:auto;min-height:0}
/* the rail-collapse FEATURE keeps v1.227.0's behaviour and values; what changes is WHICH half
   of it is two-column-only. The SIZING is direction-dependent (a column-direction flex-basis
   governs height, not width) so it stays scoped. The HIDING is not, and scoping it was the one
   thing that could put this pane in an unrecoverable state: tnp_prthreadcollapse is a GLOBAL
   localStorage preference by design, so a collapse set on a desktop arrives on a phone, where
   the rail was display:none and there was no way to expand again. The rail is now shown
   WHENEVER the thread is collapsed, at every width, which is what makes the state reversible;
   pending-link mode still refuses collapse and hides the toggle, and is listed last so it wins
   the same-specificity tie. */
.pr-2col .pr-thread-rail{display:flex;align-items:center;gap:8px;width:auto;height:auto;min-height:44px;position:static;border:1px solid var(--line);border-radius:10px;background:var(--card);cursor:pointer;color:var(--sub);font-size:13px;font-weight:800;padding:0 10px;margin-bottom:6px;justify-content:flex-start}
.pr-2col .pr-thread-rail .pr-rail-lbl{display:none}
.pr-2col .pr-pane.pr-thread-collapsed .pr-col-thread{flex:0 0 44px;min-height:44px;overflow:hidden}
.pr-2col .pr-pane.pr-thread-collapsed .pr-col-img{flex:1 1 auto}
.pr-pane.pr-thread-collapsed #prThreadBody,.pr-pane.pr-thread-collapsed #prReplyBar{display:none}
.pr-pane.pr-thread-collapsed .pr-col-thread>.pr-thread-rail{display:flex;align-items:center;gap:8px;width:auto;height:auto;min-height:44px;position:static;border:1px solid var(--line);border-radius:10px;background:var(--card);cursor:pointer;color:var(--sub);font-size:13px;font-weight:800;padding:0 10px;margin-bottom:6px}
.pr-pane.pr-thread-collapsed .pr-thread-rail{justify-content:center}
.pr-pane.pr-thread-collapsed .pr-thread-rail .pr-rail-ic{display:none}
.pr-pane.pr-thread-collapsed .pr-thread-rail .pr-rail-lbl{display:inline}
.pr-pane.pr-pending-link .pr-thread-rail{display:none}
/* v1.48.0: slider-card track deprecated — markers are the selection surface */
#siteTrack,#siteDots{display:none!important}
#gsAreaBtn{position:absolute;top:16px;left:50%;transform:translateX(-50%);z-index:1100;border:none;border-radius:999px;padding:9px 18px;background:#fff;color:var(--ink);font:inherit;font-size:13px;font-weight:800;box-shadow:0 6px 18px -6px rgba(15,25,45,.45);cursor:pointer}
#gsAreaBtn:active{transform:translateX(-50%) scale(.97)}
/* v1.177.0: text you might legitimately want to copy stays selectable. */
input,textarea,select,[contenteditable="true"],.mc-b,.mc-b-time,.bd-body,#bdBody,.tk-note,.note-body,.pr-sum,.sel-ok{-webkit-user-select:text;user-select:text;-webkit-touch-callout:default}
.mc-b{max-width:82%;padding:8px 12px;border-radius:16px;font-size:14px;line-height:1.35;word-break:break-word;white-space:pre-wrap}
.mc-b-out{align-self:flex-end;background:#0a84ff;color:#fff;border-bottom-right-radius:5px}
.mc-b-in{align-self:flex-start;background:rgba(118,131,151,.16);color:var(--ink);border-bottom-left-radius:5px}
.mc-b-time{font-size:10px;color:var(--meta);font-weight:600;margin-top:2px}
.mc-b-time.out{align-self:flex-end}
/* v1.179.0 (Alex): "TNFM XR's text input area and message thread extends more to the side
   of the window. The text input area extends towards the bottom with the radius along the
   bottom corners." Ported from XR verbatim - .thread-send-bar is 16px at rest and
   16px 16px 45px 45px on a phone, sitting in a card whose bottom padding drops to
   max(0px, safe-area - 19px). The 45px echoes the screen's own corner curvature, and the
   safe-area inset moves INSIDE the bar, which is what makes it taller as well as lower.
   TNP's side padding was 20px on the body plus 2px on the thread; XR gives the desktop
   card 18px and a phone less than that. */
@media (max-width:767px){
  #mcSheet .sheet-body{padding-left:10px;padding-right:10px;
    padding-bottom:max(0px,calc(var(--safe-bot) - 19px))}
  #mcSheet .mc-thread{padding-left:6px;padding-right:6px}
  /* v1.180.0 (Alex): "The send button and + button should be at the bottom of the card."
     v1.179.0 carried the safe-area inset INSIDE the bar to make it taller, but the bar is
     align-items:flex-end - which aligns to the bottom of the CONTENT box - so the circles
     ended up floating 44px above the bar's own bottom edge on a notched phone. That padding
     was not XR's: XR keeps padding:8px 10px and lets the sheet's padding-bottom carry the
     safe area, which is exactly why its send circle sits on the edge. The height comes from
     the textarea floor below instead, which is what XR's manual-drag floor does. */
  #mcSheet .mc-send-bar{margin-bottom:0;border-radius:16px 16px 45px 45px}
  #mcSheet .mc-send-bar textarea{min-height:64px}
  #mcSheet .mc-line-row{margin:6px 0 4px}
}
@media (min-width:768px){#mcSheet .sheet-body{padding-left:18px;padding-right:18px}}
.mc-line-row{margin:8px 0 6px;flex-shrink:0;display:flex;justify-content:center}
.mc-send-bar{flex-shrink:0}
.mc-line-pill{font-size:12px;font-weight:700;border:1px solid var(--line);border-radius:999px;padding:5px 12px;background:var(--card);color:var(--ink)}
/* v1.176.0 (Alex): "Same design of the text input area with the rounded bottom corners and
   send button inside the text input area." XR's .thread-send-bar is ONE bordered rounded box
   containing a transparent, border-less textarea and the send circle; TNP had two separate
   controls side by side, which is why it never read as the reference. */
/* v1.184.0 (Alex): "The send text entry should be on top of the buttons, not within it."
   Column: full-width textarea on top, the action row under it. */
.mc-send-bar{display:flex;flex-direction:column;align-items:stretch;gap:6px;padding:8px 10px;background:var(--card);border:1px solid var(--line);border-radius:16px;margin:3px 0 0}
.mc-act-row{display:flex;gap:8px;align-items:center}
.mc-act-row .mc-send-btn{margin-left:auto}
.mc-send-bar textarea{flex:1;min-height:36px;max-height:120px;resize:none;border:0;outline:0;border-radius:0;padding:8px 4px;font:inherit;font-size:14px;line-height:1.4;background:transparent;color:var(--ink)}
.mc-send-bar textarea::placeholder{color:var(--meta)}
/* the + and the send circle are the same control at two ends of the box, so they share every
   rule including the per-line colour below */
.mc-send-btn,.mc-photo-btn,.mc-ai-btn{width:34px;height:34px;flex-shrink:0;border-radius:50%;border:none;background:#0a84ff;display:flex;align-items:center;justify-content:center;cursor:pointer;padding:0}
/* v1.180.0 (Alex): "Make the + button a regular white button." The send circle keeps the
   line colour because that colour MEANS something - which TNL number the text leaves on -
   while + only opens a file picker, so it takes the same regular round button as call and
   contact. Same recipe, same source. */
.mc-photo-btn,.mc-ai-btn{background:radial-gradient(60% 42% at 30% 6%,rgba(255,255,255,.72),transparent 50%),rgba(255,255,255,.4);backdrop-filter:blur(24px) saturate(200%);-webkit-backdrop-filter:blur(24px) saturate(200%);box-shadow:0 4px 14px -4px rgba(15,25,45,.3),inset 0 1px 0 rgba(255,255,255,.8);border:1px solid rgba(255,255,255,.6)}
.mc-send-btn:disabled,.mc-photo-btn:disabled{opacity:.4;cursor:default}
.mc-send-btn svg,.mc-photo-btn svg{width:16px;height:16px;stroke:#fff;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.mc-photo-btn svg{stroke-width:2.4;stroke:var(--ink3)}
/* XR's AI bubble glyph: the cyan four-point star, filled not stroked. */
.mc-ai-btn svg{fill:#0891b2;stroke:none;width:15px;height:15px}
.mc-ai-btn.loading{animation:mcAiPulse 1.2s ease-in-out infinite}
@keyframes mcAiPulse{0%,100%{transform:scale(1)}50%{transform:scale(.88)}}
/* v1.184.0: the AI summary overlay - XR .ai-summary-overlay/.ai-summary-card, restated in
   TNP's own variables. Absolute over the message sheet, card pinned to the top. */
.mc-ai-ov{position:absolute;inset:0;z-index:60;background:rgba(248,250,252,.85);backdrop-filter:blur(12px) saturate(160%);-webkit-backdrop-filter:blur(12px) saturate(160%);border-radius:inherit;display:flex;align-items:flex-start;justify-content:center;padding:24px 16px;opacity:0;pointer-events:none;transition:opacity .18s ease}
.mc-ai-ov.open{opacity:1;pointer-events:auto}
.mc-ai-card{width:100%;max-width:560px;background:var(--card);border:1px solid var(--line);border-radius:18px;padding:16px 18px;box-shadow:0 12px 32px rgba(15,23,42,.18);display:flex;flex-direction:column;gap:12px;max-height:100%;overflow-y:auto}
.mc-ai-hd{display:flex;align-items:center;gap:8px;font-weight:800;font-size:15px;color:var(--ink)}
.mc-ai-hd .sheet-x{margin-left:auto}
.mc-ai-badge{background:#0891b2;color:#fff;font-size:10px;font-weight:800;padding:2px 7px;border-radius:999px;letter-spacing:.4px}
.mc-ai-sec h4{margin:0 0 4px;font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.5px;color:var(--meta)}
.mc-ai-sec div{font-size:13.5px;line-height:1.5;color:var(--ink)}
.mc-ai-sug{background:rgba(8,145,178,.08);border:1px solid rgba(8,145,178,.22);border-radius:12px;padding:10px 12px}
.mc-ai-load{font-size:13px;color:var(--sub);padding:6px 0 2px}
.mc-ai-acts{display:flex;gap:10px;justify-content:flex-end}
.mc-ai-acts .big-btn{width:auto;padding:10px 16px;margin:0}
.mc-day{align-self:center;font-size:10px;font-weight:700;color:var(--meta);background:rgba(118,131,151,.12);padding:2px 10px;border-radius:999px;margin:4px 0}
@media (min-width:768px){
#mcSheet.open{width:min(920px,calc(94vw - var(--railw)));height:min(87vh,860px)}
#mcSheet .sheet-body{display:flex;flex-direction:column;overflow:hidden}
#mcSheet #mcPane{flex:1;min-height:0}
.mc-col-info{display:block;flex:0 0 40%;min-width:0;padding-left:16px;overflow-y:auto;min-height:0}
.mc-thread{flex:1;max-height:none;min-height:0}
.mc-divider{display:block;flex:0 0 14px;position:relative;align-self:stretch;cursor:col-resize;touch-action:none;-webkit-user-select:none;user-select:none}
.mc-divider::before{content:"";position:absolute;left:50%;top:0;bottom:0;width:1px;background:rgba(118,131,151,.28);transform:translateX(-50%);transition:background .15s,width .15s}
.mc-divider:hover::before{background:rgba(118,131,151,.62);width:2px}
#mcPane.mc-resizing{cursor:col-resize}
#mcPane.mc-resizing .mc-divider::before{background:#60a5fa;width:2px}
}
/* ===== v1.39.1 viewport clamp (G60): no --app-h consumer may exceed the true dynamic viewport ===== */
@media (max-width:767px){
@supports (height:100dvh){
.view{height:min(var(--app-h,100dvh),100dvh)}
.pill-wrap{top:calc(min(var(--app-h,100dvh),100dvh) - 96px - var(--safe-bot))}
.punch-target{top:calc(min(var(--app-h,100dvh),100dvh) - 121px - var(--safe-bot))}
}
}

/* ===== v1.67.0 G-15 preview/editor + payroll home dashboard ===== */
.pr-strip{display:flex;gap:8px;margin-top:8px;overflow-x:auto;padding-bottom:4px}
.pr-thumb{position:relative;flex:0 0 64px;height:64px;border:2px solid var(--line);border-radius:10px;overflow:hidden;cursor:pointer;background:var(--card)}
.pr-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.pr-thumb.on{border-color:#2c5ce5}
.pr-thumb .b{position:absolute;left:0;right:0;bottom:0;font-size:9px;font-weight:800;color:#fff;background:rgba(15,23,42,.55);padding:1px 4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ph-tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(118px,1fr));gap:8px;margin-top:12px}
.ph-tile{cursor:pointer}
.ph-tile.mut .v{color:var(--meta)}
.ph-tile:active{transform:scale(.98)}
.pe-tools{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-bottom:8px}
.pe-tools .tbtn{border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:10px;padding:7px 11px;font-size:12.5px;font-weight:700;cursor:pointer;display:inline-flex;align-items:center;gap:5px}
.pe-tools .tbtn.on{border-color:#2c5ce5;color:#2c5ce5;background:rgba(44,92,229,.10)}
.pe-tools .cdot{width:20px;height:20px;border-radius:50%;border:2px solid var(--line);cursor:pointer;display:inline-block}
.pe-tools .cdot.on{border-color:var(--ink)}
.pe-wrap{position:relative;background:#0b1220;border:1px solid var(--line);border-radius:12px;overflow:auto;max-height:min(62vh,640px);display:flex;align-items:flex-start;justify-content:center}
#peCanvas,.pe-wrap canvas{max-width:100%;height:auto;display:block;touch-action:none;cursor:crosshair}
.pe-wrap img{max-width:100%}

/* v1.68.0 Time Punch ranges */
.tp-chip{border:0;cursor:pointer;font-weight:700;background:rgba(118,131,151,.12);color:var(--sub)}
.tp-chip.on{background:rgba(44,92,229,.14);color:#2c5ce5}
#tpRangeWrap{display:none}
#tpRangeWrap.on{display:flex}

/* v1.69.0 (no new rules — st card reuses wcard/chip) */

/* v1.70.0 master timesheet grid */
.st-scroll{overflow-x:auto;padding-bottom:4px}
.st-grid{display:grid;gap:2px 4px;align-items:center;min-width:540px}
.st-grid .st-hd{font-size:9.5px;font-weight:800;color:var(--meta);letter-spacing:.04em;padding:4px 2px;border-bottom:2px solid var(--line);align-self:stretch;display:flex;align-items:flex-end}
.st-grid .st-c{text-align:center;font-size:12px;font-weight:600;color:var(--ink2);font-variant-numeric:tabular-nums;padding:8px 1px}.st-grid .st-hd.st-c{font-size:9.5px;font-weight:800;color:var(--meta);padding:4px 2px;justify-content:center}.st-grid .st-cin{padding:3px 1px}
.st-grid .st-nm{font-size:12.5px;font-weight:700;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:8px 2px}
.st-grid .st-b{font-weight:800;color:var(--ink)}
.st-grid .st-row{cursor:pointer;border-bottom:1px solid rgba(118,131,151,.10)}
.st-grid .st-days{grid-column:1/-1;border-bottom:1px solid var(--line);padding:5px 2px;background:rgba(118,131,151,.05);border-radius:8px}
.st-dl{display:flex;justify-content:space-between;gap:8px;font-size:12px;font-weight:600;color:var(--ink2);padding:3px 4px;align-items:center}
/* v1.102.0 (Alex): "The timesheet can be wider." Bleed past the sheet body's 20px padding - 28px
   of extra grid on every screen, most of a day column on a phone. */
#stList,.stb-hd{margin-left:-14px;margin-right:-14px}
/* v1.102.0 (Alex): "the hours and edit needs to all be on one row... even on narrower screens it
   should scale down the font to fit". The rows used to flex-wrap, so on a phone the lunch field
   and its heading dropped to their own lines and one punch took three rows. nowrap + the two time
   inputs sharing the leftover space (min-width:0 is what actually lets a flex item shrink below
   its content) + clamp() type. The head repeats the row's proportions so the labels stay over
   their own columns at every width. */
.st-erow,.st-ehd{display:flex;flex-wrap:nowrap;align-items:center;gap:5px;padding:3px 2px}
.st-erow .st-dt,.st-ehd .st-dt{flex:0 0 auto;width:clamp(36px,12%,64px);font-size:clamp(9px,2.5vw,12px);font-weight:700;color:var(--ink);overflow:hidden;white-space:nowrap}
.st-erow .st-ri,.st-erow .st-ro2,.st-ehd .h-t{flex:1 1 0;min-width:0}
.st-erow .st-rl,.st-ehd .h-l{flex:0 0 auto;width:clamp(40px,15%,66px);min-width:0}
.st-erow input{margin:0;padding:5px 3px;font-size:clamp(9.5px,2.8vw,13px);text-align:center}
.st-erow .st-sep,.st-ehd .st-sep{flex:0 0 auto;color:var(--meta);font-weight:700;font-size:clamp(9px,2.4vw,12px)}
.st-ehd{font-size:clamp(7.5px,2.1vw,9.5px);font-weight:800;color:var(--meta);letter-spacing:.05em}
.st-ehd span{text-align:center;overflow:hidden;white-space:nowrap}
/* ===== v1.76 mobile bottom bar ===== */
#mbar{position:fixed;left:50%;right:auto;transform:translateX(-50%);bottom:calc(14px + var(--safe-bot));z-index:1999;display:none;gap:0;overflow:hidden;width:auto;max-width:calc(100vw - 24px);padding:6px 8px;border-radius:27px;background:rgba(255,255,255,.86);backdrop-filter:blur(40px) saturate(190%) brightness(1.04);-webkit-backdrop-filter:blur(40px) saturate(190%) brightness(1.04);border:1px solid rgba(255,255,255,.72);box-shadow:0 12px 32px -10px rgba(15,25,45,.32),0 2px 8px -3px rgba(15,25,45,.14)}
#mbar::-webkit-scrollbar{display:none}
/* v1.91.0: equal-width slots => uniform icon spacing by construction, and no scrolling. */
.mb-it{flex:1 1 64px;min-width:0;display:flex;flex-direction:column;align-items:center;gap:3px;padding:5px 2px 4px;border:none;background:none;cursor:pointer;color:var(--nav-idle);border-radius:14px}
.mb-gap{flex:0 0 92px}
/* v1.104.0: the badge anchors to the nav button, which had no positioning context of its own. */
.mb-it{position:relative}
.mb-bdg{position:absolute;top:3px;right:9px;min-width:15px;height:15px;padding:0 4px;border-radius:8px;background:#E3202A;color:#fff;font-size:9px;font-weight:800;display:flex;align-items:center;justify-content:center;line-height:1}
/* One panel per site, snapped, with the scrollbar hidden - it is a swipe surface, not a list. */
.ap-swipe{display:flex;gap:12px;overflow-x:auto;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.ap-swipe::-webkit-scrollbar{display:none}
.ap-pan{flex:0 0 100%;scroll-snap-align:center;min-width:0}
/* the clone keeps .sheet-body, whose padding and scroll belong to a sheet, not to a panel */
.ap-pan .sheet-body{padding:0;overflow:visible;flex:none}
/* v1.104.1: the v1.102.0 width bleed is sized against .sheet-body's 20px padding, which a panel
   does not have - left alone it hangs outside the panel box and .ap-swipe's overflow cuts the
   worker name off the left and the Approve button off the right. Inside a panel there is no sheet
   chrome to bleed into, so the bleed is zeroed and .ap-swipe reclaims the same 14px either side
   instead, which puts the grid back at the width it has in the standalone sheet. */
.ap-pan #stList,.ap-pan .stb-hd{margin-left:0;margin-right:0}
.ap-pan .sh-stick{margin-left:0;margin-right:0;padding-left:0;padding-right:0}
.ap-swipe{margin-left:-14px;margin-right:-14px}
.ap-pan{padding-left:14px;padding-right:14px}
.ap-dots{display:flex;gap:6px;justify-content:center;margin-top:10px}
.ap-dot{width:7px;height:7px;border-radius:50%;background:rgba(118,131,151,.32)}
.ap-dot.on{background:var(--blue)}
.mb-slot{flex:1 1 64px}
.mb-it span{max-width:100%;overflow:hidden;text-overflow:ellipsis}
.mb-it svg{width:22px;height:22px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.mb-it span{font-size:9.5px;font-weight:700;white-space:nowrap}
.mb-it.on{color:var(--blue)}
.wd .dotc{width:5px;height:5px;border-radius:50%;background:#16a34a;margin-top:2px}
.sup-w{font-size:12px;color:var(--sub);font-weight:700;margin-top:2px}
@media (max-width:767px){
#mbar{display:flex}
.pill-side{display:none}
.pill{width:auto;height:auto;background:none;border:none;box-shadow:none;backdrop-filter:none;-webkit-backdrop-filter:none}
.pill-wrap{left:50%;right:auto;transform:translateX(-50%);top:auto;bottom:calc(12px + var(--safe-bot))}
#punchBtn{position:static;transform:none;width:84px;height:84px;border-width:4px;padding:0 4px}
#punchBtn:active{transform:scale(.96)}
#punchBtn .w{font-size:11.5px}
#punchBtn .el{font-size:9.5px;margin-top:1px}
.punch-target{left:50%;right:auto;top:auto;bottom:calc(104px + var(--safe-bot));transform:translateX(-50%);max-width:80vw}
body.modal-open #mbar{opacity:0;pointer-events:none;transition:opacity .15s}
}
/* ===== v1.94.0 messages pull tab (left edge, XR-style) ===== */

body.modal-open .tn-dwr-tab,body.sliding .tn-dwr-tab{opacity:0;pointer-events:none}
/* ===== v1.93.0 nav overflow popover ===== */
#moreBd{position:fixed;left:0;top:0;right:0;bottom:0;z-index:2002;display:none;background:transparent}
#moreBd.on{display:block}
#morePop{position:fixed;right:12px;bottom:calc(96px + var(--safe-bot));z-index:2003;display:none;
  min-width:186px;max-width:min(68vw,260px);padding:6px;border-radius:16px;
  background:rgba(255,255,255,.97);backdrop-filter:blur(30px) saturate(180%);-webkit-backdrop-filter:blur(30px) saturate(180%);
  border:1px solid rgba(255,255,255,.75);box-shadow:0 18px 40px -12px rgba(15,25,45,.42),0 2px 8px -3px rgba(15,25,45,.16);
  transform-origin:100% 100%;animation:morePopIn .13s ease-out}
#morePop.on{display:block}
@keyframes morePopIn{from{opacity:0;transform:scale(.94) translateY(6px)}to{opacity:1;transform:none}}
.mb-more{display:flex;align-items:center;gap:10px;width:100%;padding:9px 10px;border:0;background:none;cursor:pointer;
  border-radius:11px;color:var(--ink);font-weight:700;font-size:13.5px;text-align:left}
.mb-more+.mb-more{margin-top:1px}
.mb-more:active{background:rgba(118,131,151,.14)}
/* the nav's icon rules are scoped to .mb-it — without an equivalent here every icon draws at its
   intrinsic size and solid-filled, which is what turned the More menu into black discs. */
.mb-more svg{width:20px;height:20px;flex:0 0 20px;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;color:var(--nav-idle)}
/* ===== v1.92.0 master timesheet: phone-fit view A + week-calendar view B ===== */
/* view B */
.stb-hd{display:grid;grid-template-columns:repeat(7,1fr);gap:3px;padding:0 2px 4px;border-bottom:2px solid var(--line)}
.stb-hd div{text-align:center;font-size:9.5px;font-weight:800;color:var(--meta);letter-spacing:.04em}
.stb-w{margin-top:10px;border:1px solid var(--line);border-radius:14px;overflow:hidden;background:var(--card)}
.stb-sub{display:flex;align-items:center;gap:8px;padding:8px 10px;background:rgba(118,131,151,.07);cursor:pointer}
.stb-nm{flex:1;min-width:0;font-size:13.5px;font-weight:800;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.stb-ro{font-size:10px;font-weight:800;color:var(--meta);font-variant-numeric:tabular-nums;white-space:nowrap}
.stb-ro b{color:var(--ink);font-size:11.5px}
.stb-ap{border:0;cursor:pointer;background:var(--green);color:#fff;font-weight:800;font-size:10.5px;padding:6px 12px;border-radius:999px;white-space:nowrap}
.stb-ap[disabled]{background:rgba(118,131,151,.18);color:var(--meta);cursor:default}
.stb-days{display:grid;grid-template-columns:repeat(7,1fr);gap:3px;padding:8px 6px;cursor:pointer}
.stb-d{text-align:center;font-size:12.5px;font-weight:700;color:var(--ink2);font-variant-numeric:tabular-nums;padding:5px 0;border-radius:9px;background:rgba(118,131,151,.06)}
/* v1.101.0 (Alex): "Empty days would still have the darker background with the dash in it." It
   was background:none - a hole in the row rather than a cell with nothing in it. Darker than a
   worked day, deliberately: the gap is the thing worth noticing. */
.stb-d.z{color:var(--sub);background:rgba(118,131,151,.14)}
/* the day letter sits above the number and keeps its own colour - inheriting the cell's would
   fade it out on exactly the empty days it most needs to label */
.stb-d .stb-dw{display:block;font-size:9px;font-weight:800;color:var(--meta);letter-spacing:.06em;line-height:1;margin-bottom:3px}
.stb-d.in{color:var(--green);font-weight:800}
.stb-d small{display:block;font-size:8px;font-weight:700;color:var(--meta)}
.stb-body{border-top:1px solid var(--line);padding:6px 8px 10px}
.stb-lk{font-size:10px;font-weight:800;color:var(--meta)}
@media (max-width:767px){
.st-grid{min-width:0}
.st-grid .st-nm{font-size:11.5px;padding:8px 1px}
.st-grid .st-c{font-size:11px;padding:8px 0}
.st-grid .st-hd{font-size:8.5px}
}
/* ===== v1.89.0 slide to punch ===== */
/* The rail is a fixed overlay sized to the capsule at hold time rather than a child of #mbar,
   so the nav's own children (and every probe assertion that walks them) stay untouched. */
#punchBtn{touch-action:none;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}
#mbar .mb-it{transition:opacity .16s}
#mbar.sliding{overflow:hidden;background:rgba(255,255,255,.94)}
#mbar.sliding .mb-it{opacity:0;pointer-events:none}
#slRail{position:fixed;left:0;top:0;width:0;height:0;z-index:1999;display:none;align-items:center;padding:0 12px;border-radius:27px;pointer-events:none}
#slRail.on{display:flex}
.sl-lead{display:none;font-size:10px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:var(--meta);white-space:nowrap}
.sl-t{position:absolute;top:50%;transform:translateY(-50%);font-size:11px;font-weight:800;white-space:nowrap;padding:6px 11px;border-radius:999px;background:rgba(118,131,151,.16);color:var(--sub);transition:background .14s,color .14s,transform .14s}
#slPunch{left:10px}
#slPunch.hot{background:#16a34a;color:#fff;transform:translateY(-50%) scale(1.07)}
#slCheck{right:10px}
#slCheck.hot{background:#2c5ce5;color:#fff;transform:translateY(-50%) scale(1.07)}
#slPunch.out.hot{background:#ef4444}
#punchBtn.sl-drag{box-shadow:0 18px 30px -12px rgba(15,25,45,.55)}
#punchBtn.sl-back{transition:transform .18s cubic-bezier(.2,.8,.3,1)}
#slideCard{position:fixed;left:12px;right:12px;bottom:calc(106px + var(--safe-bot));z-index:2002;display:none;padding:10px 14px;border-radius:16px;background:radial-gradient(75% 55% at 24% -12%,rgba(255,255,255,.6),transparent 46%),rgba(255,255,255,.58);backdrop-filter:blur(42px) saturate(200%);-webkit-backdrop-filter:blur(42px) saturate(200%);border:1px solid rgba(255,255,255,.7);box-shadow:0 14px 34px -12px rgba(15,25,45,.42)}
#slideCard.show{display:block}
#slideCard .sc-t{font-size:14px;font-weight:800;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#slideCard .sc-s{font-size:11.5px;font-weight:600;color:var(--meta);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
body.sliding .punch-target{display:none}
.p-pick{background:#768397;box-shadow:0 14px 22px -10px rgba(118,131,151,.6);color:#fff}
.p-pick .site,.p-pick .el{color:rgba(255,255,255,.85)}
/* ===== v1.77.2 payroll band glass ===== */
/* v1.113.0: editor reject + greyed blanks + week-nav pending dots */
.st-erow.st-off{opacity:.45}
.st-erow.st-rej{position:relative}
.st-erow.st-rej .input{text-decoration:line-through;color:#E3202A;opacity:.6}
.st-erow.st-rej:after{content:"";position:absolute;left:4px;right:30px;top:50%;border-top:1.5px solid rgba(227,32,42,.55);pointer-events:none}
.st-rx{width:22px;height:22px;border-radius:50%;border:1.5px solid var(--meta);background:none;color:var(--meta);font-size:13px;font-weight:800;line-height:1;flex:0 0 auto;cursor:pointer;padding:0}
.st-rx:hover{color:#E3202A;border-color:#E3202A}
/* v1.119.0: the REAL iOS fix - fr tracks floor at min-content, so the grid columns are minmax(0,)-capped
   inline; these rules make the inputs themselves slim enough to read at that width */
#dayAdj .fo-inp{min-width:0;width:100%;box-sizing:border-box;-webkit-appearance:none;appearance:none;padding:10px 8px;font-size:14px;border-radius:10px}
#dayAdj input::-webkit-date-and-time-value{text-align:left}
/* v1.118.0: editor rows that cannot save are dim; the check/lock glyphs are markers, not buttons */
.st-erow.st-lkd .input{opacity:.55}
.st-erow .st-rx.st-apx{border-color:#16a34a;color:#16a34a;cursor:default;display:inline-flex;align-items:center;justify-content:center}
.st-erow .st-rx.st-lkx{border:0;cursor:default;font-size:12px;display:inline-flex;align-items:center;justify-content:center}
/* ==== v1.188.0 entry notes: the editor row wraps to host the note line.
   MUST follow the shared .st-erow,.st-ehd nowrap rule - same specificity, order decides (G125). ==== */
.st-erow{flex-wrap:wrap}
.st-erow .st-wn{flex:1 1 100%;font-size:11.5px;color:var(--meta);font-style:italic;margin:2px 2px 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.st-erow .st-rn{flex:1 1 100%;text-align:left;font-size:12px;padding:6px 9px;margin-top:4px}
/* v1.115.0 (Alex): the arrow itself glows red when pending approvals live in that direction */
#stPrev.st-wglow,#stNext.st-wglow{border-color:#E3202A!important;animation:stGlow 1.6s ease-in-out infinite}
#stPrev.st-wglow svg,#stNext.st-wglow svg{stroke:#E3202A!important}
@keyframes stGlow{0%,100%{box-shadow:0 0 0 1.5px rgba(227,32,42,.35),0 0 8px 1px rgba(227,32,42,.3)}50%{box-shadow:0 0 0 1.5px rgba(227,32,42,.55),0 0 14px 4px rgba(227,32,42,.55)}}
/* ==== v1.123.0: left drawers, attention glows, assignment links ==== */
/* v1.185.0: this whole system sat inside a max-width:767px media query since v1.166.0 - on
   a desktop there was NO drawer CSS at all: #mcListSheet laid out as an unstyled static
   full-width block, its close X floated to the viewport corner (the second stacked X in
   Alex's screenshots), and the grab tab rendered as a bare button (the empty rectangle).
   v1.183.0's desktop sizing block was nested INSIDE it - (max-width) AND (min-width) is
   never true. Unwrapped: every rule below is width-aware on its own (the :root vars carry
   inner min-width overrides, #fltRz keeps its inner max-width hide). G125. */
  /* v1.166.0 (Alex): the message centre and the filter become XR-style edge drawers.
   Geometry lifted from XR v6.3.80 .map-drawer - inset top/bottom so the header and the
   tab bar stay visible, 90vw capped at 440px, 26px radius on the inner corners only. */
:root{--dwrTop:96px;--dwrBot:calc(84px + var(--safe-bot,0px))}
@media (min-width:768px){:root{--dwrTop:18px;--dwrBot:18px}}
/* XR v6.3.37 pull bar: a VERTICAL 4x75 bar on the drawer's outer edge, not the 42x5
   horizontal grab pill a bottom sheet uses. The alert pulse is XR's tnfmPullAlert. */
@keyframes tnfmPullAlert{0%,100%{background:rgba(227,32,42,.5);box-shadow:0 0 3px rgba(227,32,42,.25)}50%{background:#E3202A;box-shadow:0 0 14px rgba(227,32,42,.9)}}
#mcListSheet .pull-bar,#fltDrawer .tn-pull{position:absolute;top:50%;width:4px;height:75px;padding:0;border-radius:2px;background:rgba(120,130,145,.55);transform:translateY(-50%);pointer-events:none;z-index:46;display:block;transition:opacity .25s ease}
#mcListSheet .pull-bar::before{content:none}
#mcListSheet .pull-bar{right:7px;left:auto}
#fltDrawer .tn-pull{left:7px;right:auto}
#mcListSheet.unread .pull-bar,#fltDrawer.unread .tn-pull{animation:tnfmPullAlert 2.4s ease-in-out infinite}
/* v1.169.0 (Alex): "it retracts a bit then slides in again... should just slide into
   position from where I let go. Same inertia as TNFM XR." A keyframe cannot do that - it
   always replays from its `from`. XR parks the drawer with a 15px tab peeking and moves it
   with a TRANSITION; `.dragging` drops the transition so an inline transform can follow the
   finger, and clearing it on release lets the transition finish from wherever it is. */
/* v1.177.0 (Alex): "Message window should be on top of the message list and filter list
   panels." The drawers were minted at 2720 and .sheet is 2710, so every window opened FROM
   a drawer opened UNDER it. 2705 keeps them above the shared overlay #psOv (2700) - they
   have to be, or it eats their taps - and below every sheet. .tn-dwr-front is the
   most-recently-addressed drawer, so the two of them alternate rather than being decided
   by DOM order. */
.tn-dwr{position:fixed;top:var(--dwrTop);bottom:var(--dwrBot);width:min(90vw,440px);z-index:2705;display:flex;align-items:stretch;will-change:transform}
.tn-dwr.tn-dwr-front{z-index:2706}
.tn-dwr.left{left:0;right:auto;transform:translateX(calc(-100% + 15px));transition:transform .35s cubic-bezier(.22,1,.36,1)}
.tn-dwr.right{right:0;left:auto;transform:translateX(calc(100% - 15px));transition:transform .35s cubic-bezier(.22,1,.36,1)}
.tn-dwr.left.open,.tn-dwr.right.open{transform:translateX(0)}
.tn-dwr.dragging{transition:none}
/* v1.183.0: the drawer is now the ONLY message centre, so on a desktop it has to read as a
   primary panel rather than a phone artefact carried over. It clears the rail and takes the
   width the dock panel used to have. */
@media (min-width:768px){
  .tn-dwr.left{left:var(--railw);width:min(460px,52vw)}
  .tn-dwr.left.open{transform:translateX(0)}
  #mcListSheet .tn-dwr-body{padding:16px 18px calc(14px + var(--safe-bot,0px))}
}
#fltDrawer.dragging{transition:none}
.tn-dwr-inner{display:flex;width:100%;height:100%;align-items:stretch;min-width:0}
.tn-dwr.left .tn-dwr-inner{flex-direction:row-reverse}
.tn-dwr.right .tn-dwr-inner{flex-direction:row}
/* XR v6.3.38: a full-height grab COLUMN (big touch target) with the bar drawn at its
   vertical centre - same 15px closed peek. */
.tn-dwr-tab{flex:0 0 15px;width:15px;align-self:stretch;background:transparent;border:0;padding:0;display:flex;align-items:center;justify-content:center;cursor:grab;touch-action:none;user-select:none;-webkit-user-select:none;position:relative}
.tn-dwr-tab .pull-bar{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:4px;height:75px;padding:0;border-radius:2px;background:rgba(120,130,145,.55);pointer-events:none;display:block;transition:opacity .25s ease}
.tn-dwr-tab .pull-bar::before{content:none}
.tn-dwr-tab.glow-red .pull-bar{animation:tnfmPullAlert 2.4s ease-in-out infinite}
/* "Don't need grab bar or x when expanded" - XR hides the bar on touch once open; the
   swipe-away gesture replaces the X, so that goes too. */
@media (hover:none) and (pointer:coarse){
.tn-dwr.open .tn-dwr-tab .pull-bar{opacity:0;transition:opacity .2s ease .2s}
.tn-dwr.open .sheet-x{display:none}
}
.tn-dwr-body{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;overflow:hidden;background:radial-gradient(55% 28% at 28% 0%,rgba(255,255,255,.62),transparent 42%),rgba(248,250,252,.6);backdrop-filter:blur(52px) saturate(200%) brightness(1.08);-webkit-backdrop-filter:blur(52px) saturate(200%) brightness(1.08);border:1px solid rgba(255,255,255,.55);box-shadow:0 18px 44px -16px rgba(15,25,45,.42),inset 0 1px 0 rgba(255,255,255,.7);position:relative;padding:14px 16px calc(12px + var(--safe-bot,0px))}
.tn-dwr.left .tn-dwr-body{border-radius:0 26px 26px 0;border-left:0}
.tn-dwr.right .tn-dwr-body{border-radius:26px 0 0 26px;border-right:0}
.tn-dwr .sheet-body{flex:1 1 auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;touch-action:pan-y;padding:0}
/* v1.173.0: the tab is a real 15px column of the inner flex row again (v1.169.0 had to pin
   it absolutely because the drawer stacked its content in a COLUMN). #fltRz keeps the
   desktop resize edge, so it must sit above the body it overlays. */
#fltDrawer .tn-dwr-body{padding:18px 18px calc(14px + var(--safe-bot,0px))}
#fltDrawer #fltRz{z-index:4}
@media (max-width:767px){#fltRz{display:none}}
@keyframes tnsheetl{from{transform:translateX(-103%)}to{transform:translateX(0)}}
.st-y{box-shadow:inset 0 0 0 2px rgba(250,204,21,.7);background:rgba(250,204,21,.16)!important;border-radius:9px}
@keyframes stpulse{0%,100%{box-shadow:inset 0 0 0 2px rgba(250,204,21,.9),0 0 3px rgba(250,204,21,.3)}50%{box-shadow:inset 0 0 0 2px rgba(250,204,21,.35),0 0 13px rgba(250,204,21,.8)}}
.st-c.st-cin,.stb-d.in{animation:stpulse 2.6s ease-in-out infinite}
.st-asg{cursor:pointer;text-decoration:underline;text-decoration-color:rgba(44,92,229,.45);text-underline-offset:3px}
/* ==== v1.124.0: XR card stops (12.5 / 60 / 90 vh) + cascade ==== */
@media (max-width:767px){
  .sheet.snapcard{--snapH:calc(var(--vvh,100dvh)*.6);height:var(--snapH);max-height:none;transition:height .35s cubic-bezier(.22,1,.36,1)}
  .sheet.snapcard[data-snap="collapsed"]{--snapH:calc(var(--vvh,100dvh)*.125)}
  .sheet.snapcard[data-snap="expanded"]{--snapH:calc(var(--vvh,100dvh)*.9)}
  .sheet.snapcard.sheet-back{height:calc(var(--snapH) + 16px);filter:brightness(.96) saturate(.92)}
  .sheet.snapcard .pull-bar{touch-action:none;cursor:grab}
}
/* ==== v1.125.0: Site Board + weekly recurrence ==== */
.bd-add{border:1.5px solid var(--line,#e2e8f0);background:var(--card,#fff);color:var(--ink);border-radius:999px;padding:4px 12px;font-size:12px;font-weight:700;cursor:pointer}
.bd-card{min-width:86%;max-width:86%;padding:0;overflow:hidden;scroll-snap-align:start}
.bd-card:only-child{min-width:100%;max-width:100%}
#boardWrap{margin-bottom:14px}
.bd-cbody{padding:14px}
.bd-img{width:100%;height:150px;object-fit:cover;display:block}
#homeCols{display:flex;gap:22px;align-items:flex-start}
#studioSheet{height:min(90vh,1040px);max-height:none}
#artSheet.aeOn{height:min(94vh,1100px);max-height:none}
@media (min-width:768px){#artSheet.aeOn{width:min(880px,calc(94vw - var(--railw)))}}
@media (min-width:1100px){body.hasBoard #homeColR{padding-top:38px}}
#homeColL,#homeColR{flex:1 1 0;min-width:0}
.bd-t{font-size:15px;font-weight:800;color:var(--ink);margin-top:2px}
.bd-m{font-size:11.5px;color:var(--meta);font-weight:600;margin-top:4px}
.bd-ic{width:34px;height:34px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex:none}
.bd-ic svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.bd-ic.ev{background:#eef2ff;color:#4338ca}.bd-ic.nt{background:#fef3c7;color:#b45309}
.rec-d{width:34px;height:34px;border-radius:50%;border:1.5px solid var(--line,#e2e8f0);background:var(--card,#fff);color:var(--sub);font-size:12px;font-weight:800;cursor:pointer}
.rec-d.on{background:#E3202A;border-color:#E3202A;color:#fff}
.bc-lb{font-size:12px;color:var(--sub);font-weight:700;min-width:64px}
/* ==== v1.128.0: account + contact imagery ==== */
.acct-logo{width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block}
.ava-img{width:100%;height:100%;object-fit:cover;border-radius:50%;display:block}
.ct-ava{width:56px;height:56px;border-radius:50%;object-fit:cover;display:block;margin-bottom:8px}
.site-ph{width:132px;height:92px;border-radius:12px;object-fit:cover;flex:none;cursor:pointer}
#sitePhotos{gap:8px}
