
  :root{
    --navy:#194872;
    --blue:#4688c3;
    --grad:linear-gradient(225deg,#4688c3 35%,#194872);
    --paper:#f4f7fa;
    --card:#ffffff;
    --line:rgba(25,72,114,.08);
    --good:#2e7d54;
    --goodbg:#e2f0e8;
    --warn:#b3671f;
    --warnbg:#faeddd;
    --muted:#64748b;
    --shadow:0 1px 2px rgba(25,72,114,.06), 0 8px 24px rgba(25,72,114,.08);
    --btnshadow:0 2px 8px rgba(25,72,114,.20);
    --r-card:14px;
    --r-btn:10px;
    --font:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  }
  *{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
  html{background:#fff;overscroll-behavior:none;overflow-x:hidden}
  /* The root background shows in the home-indicator gap and on overscroll.
     Login and the admin picker are navy screens. While the app is open the
     top of the root is the same navy as the header, so a translucent phone
     status bar cannot pick up white and read as a second bar. The rest of
     the root stays white, so the gap under the copyright stays paper. */
  html:has(#login.on),html:has(#picker.on),body:has(#login.on),body:has(#picker.on){background:var(--navy)}
  /* Solid white, then the gradient as an image. A gradient shorthand leaves
     background-color transparent, and iOS paints theme-color (the header
     navy) or the web view's own navy in that gap: a dark line under the
     footer. The color has to be a real white. */
  /* Solid white. A navy gradient here was sampled into the home indicator,
     which is the header color under a white tab bar. The status bar is the
     fixed block below, not this background. */
  html:has(#app.on){background-color:#fff;background-image:none}
  body:has(#app.on){background:#fff;background-color:#fff}
  /* A known sub session is dark before body.subview exists. */
  html.sub-boot,html.sub-boot body{background:#06172d;background-color:#06172d}
  /* Fills only the status-bar inset. Same navy as .apphead, so that band
     is the header, not a gap and not a darker strip. */
  html:has(#app.on)::before{content:"";position:fixed;top:0;left:0;right:0;height:env(safe-area-inset-top);background:var(--navy);z-index:40;pointer-events:none}
  /* No overscroll-behavior on body. With overflow-x:hidden on html AND body,
     body becomes its own scroll box that never has anything to scroll; "none"
     there stopped a mouse wheel or trackpad from handing the scroll on to the
     page, so on a computer nothing moved (v176). html's "none" still stops
     the bounce. */
  body{font-family:var(--font);color:#22374d;line-height:1.5;overflow-x:hidden}
  :focus-visible{outline:2px solid var(--blue);outline-offset:2px}

  #frame{max-width:430px;margin:0 auto;background:var(--paper);min-height:100vh;position:relative}
  /* First paint, before the script. Signed out: the form. Signed in: a frame
     in place of a blank page, until the script marks the document ready. */
  #bootframe{display:none;min-height:100vh;min-height:100dvh;box-sizing:border-box;padding:28px 22px;background:var(--navy)}
  html.signed-in:not(.ready) #bootframe{display:block}
  html.client-in #bootframe{background:var(--paper)}
  /* First paint only, like the inline shell rule. Once the script is ready
     the form shows only as #login.on, so a session that began signed out
     can never leave it on screen above the app on desktop (v251, v252). */
  html:not(.signed-in):not(.ready) #login{display:block}
  html.signed-in:not(.ready) #login,
  html.signed-in:not(.ready) #picker,
  html.signed-in:not(.ready) #app{display:none!important}
  html.ready #bootframe{display:none!important}
  .bf-bar{height:16px;width:46%;max-width:240px;border-radius:8px;background:rgba(255,255,255,.18);margin:8px 0 22px}
  .bf-card{height:74px;max-width:560px;border-radius:14px;background:rgba(255,255,255,.08);margin:0 0 12px}
  .bf-card.short{height:46px;width:72%}
  html.client-in .bf-bar,html.client-in .bf-card{background:#e6eef5}
  .bootcard{min-height:88px;background:#eef3f8}
  @media (prefers-reduced-motion:no-preference){
    .bf-bar,.bf-card,.bootcard{animation:bfpulse 1.15s ease-in-out infinite}
  }
  @keyframes bfpulse{50%{opacity:.5}}

  .demo{display:none;background:#0f2c47;color:#9fc0dd;font-size:10.5px;letter-spacing:1.2px;text-transform:uppercase;text-align:center;padding:6px 10px;font-weight:500}

  /* ---------- LOGIN ---------- */
  #login{display:none;min-height:100vh;min-height:100dvh;background:#06182e;color:#fff}
  #login.on{display:block}
  .login-shell{display:grid;grid-template-columns:minmax(0,1.08fr) minmax(480px,.92fr);min-height:100vh;min-height:100dvh;background:linear-gradient(135deg,#06172d 0%,#092d57 54%,#071a31 100%)}
  .login-hero{position:relative;isolation:isolate;overflow:hidden;display:flex;flex-direction:column;justify-content:space-between;min-height:100dvh;padding:clamp(34px,4.6vw,68px)}
  .login-hero:before{content:"";position:absolute;z-index:-3;inset:0;background:radial-gradient(circle at 57% 32%,rgba(31,122,230,.36),transparent 38%),radial-gradient(circle at 12% 92%,rgba(70,136,195,.14),transparent 36%)}
  .login-hero:after{content:"";position:absolute;z-index:-2;left:8%;right:-16%;bottom:-34%;height:76%;background:radial-gradient(ellipse,rgba(2,13,27,.72),transparent 68%);transform:rotate(-6deg)}
  .hero-shield{position:absolute;z-index:-1;width:min(58vw,680px);height:auto;right:-10%;top:10%;color:#2f7dca;opacity:.16;filter:drop-shadow(0 30px 45px rgba(0,0,0,.32))}
  .login-logo-link{display:block;width:min(455px,82%);margin:0 auto;text-decoration:none}
  .login-logo{display:block;width:100%;height:auto;filter:drop-shadow(0 11px 21px rgba(0,0,0,.26))}
  .hero-copy{max-width:670px;margin:clamp(70px,11vh,130px) auto auto;text-align:center}
  .hero-kicker{display:flex;align-items:center;justify-content:center;gap:12px;color:#acd0f1;font-size:11.5px;font-weight:750;letter-spacing:.15em;text-transform:uppercase}
  .hero-kicker:before,.hero-kicker:after{content:"";width:42px;height:2px;background:linear-gradient(90deg,#d3d9df,#2685e5)}
  .hero-kicker:after{transform:scaleX(-1)}
  .hero-copy h1{max-width:630px;margin:19px auto 0;color:#fff;font-size:clamp(46px,5.4vw,76px);font-weight:600;line-height:.99;letter-spacing:-.052em}
  .hero-copy h1 span{color:#7ebaff}
  .hero-copy p{max-width:590px;margin:23px auto 0;color:rgba(233,241,249,.8);font-size:clamp(15.5px,1.35vw,18.5px);line-height:1.65}
  .hero-audience-line{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin-top:28px}
  .hero-audience-line span{padding:7px 11px;border:1px solid rgba(190,216,241,.18);border-radius:999px;background:rgba(255,255,255,.065);color:#d4e4f3;font-size:10.5px;font-weight:650;letter-spacing:.08em;text-transform:uppercase;backdrop-filter:blur(10px)}
  .hero-foot{display:flex;align-items:center;justify-content:space-between;gap:18px;margin-top:42px;color:rgba(216,229,241,.58);font-size:10.5px;font-weight:650;letter-spacing:.1em;text-transform:uppercase}
  .hero-rule{flex:1;max-width:180px;height:3px;background:linear-gradient(90deg,#c9d0d8 0 42%,transparent 42% 46%,#1675e8 46% 56%,transparent 56% 60%,#c9d0d8 60%);clip-path:polygon(5% 0,100% 0,95% 100%,0 100%)}
  .auth-zone{position:relative;display:flex;align-items:center;justify-content:center;min-width:0;padding:38px clamp(30px,4.5vw,72px) 26px;background:radial-gradient(circle at 90% 4%,rgba(70,136,195,.2),transparent 29%),linear-gradient(160deg,rgba(7,28,52,.92),rgba(5,20,38,.98))}
  .auth-zone:before{content:"";position:absolute;inset:0 auto 0 0;width:1px;background:linear-gradient(transparent,rgba(141,187,230,.28) 20% 80%,transparent)}
  .login-panel{width:100%;max-width:440px}
  .auth-card{width:100%;padding:31px 35px 27px;border:1px solid rgba(255,255,255,.17);border-radius:22px;background:rgba(255,255,255,.975);box-shadow:0 28px 80px rgba(0,0,0,.32),0 3px 12px rgba(0,0,0,.12)}
  .auth-kicker{color:var(--blue);font-size:10.5px;font-weight:800;letter-spacing:.14em;text-align:center;text-transform:uppercase}
  #login .auth-title{margin-top:7px;color:#153c62;font-size:30px;font-weight:650;line-height:1.12;letter-spacing:-.035em;text-align:center}
  #login .auth-sub{max-width:340px;margin:8px auto 0;color:#6d7f91;font-size:13.5px;line-height:1.5;text-align:center}
  #login form{margin-top:20px}
  .field-label-row{display:flex;align-items:center;justify-content:space-between;gap:16px;margin:16px 0 7px}
  #login label{display:block;color:#314a62;font-size:12px;font-weight:700;letter-spacing:.02em}
  .field-label-row a{color:var(--blue);font-size:12px;font-weight:650;text-decoration:none}
  .field-label-row a:hover{text-decoration:underline}
  #login input{width:100%;min-height:50px;padding:13px 14px;border:1px solid #cdd7e2;border-radius:11px;background:#fff;color:#18324c;font-size:15px;font-family:var(--font);transition:border-color .15s,box-shadow .15s}
  #login input::placeholder{color:#9aa9b8}
  #login input:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 4px rgba(70,136,195,.13)}
  .pwwrap{position:relative}
  .pwwrap input{padding-right:48px}
  #login button.pweye,button.pweye{position:absolute;right:5px;top:50%;transform:translateY(-50%);width:40px;height:40px;margin:0;padding:10px;background:none;border:none;box-shadow:none;color:#6d7f91;cursor:pointer;line-height:0}
  #login .auth-submit{display:flex;align-items:center;justify-content:center;gap:9px;width:100%;min-height:50px;margin-top:20px;padding:13px 16px;border:0;border-radius:11px;background:linear-gradient(135deg,#194872,#4688c3);color:#fff;font-size:14px;font-weight:750;letter-spacing:.02em;cursor:pointer;box-shadow:0 8px 18px rgba(25,72,114,.2);font-family:var(--font)}
  #login .auth-submit svg{width:17px;height:17px}
  #login .auth-submit:hover{filter:brightness(1.05)}
  #login .auth-submit:active{transform:translateY(1px)}
  #login .err{display:none;margin-top:14px;padding:11px 13px;border:1px solid #efd1ca;border-radius:10px;background:#fff2ef;color:#8a2f1f;font-size:12.5px;font-weight:600}
  #ssoWrap{display:none}
  #ssoWrap[hidden]{display:none !important}
  .sso-grid{display:grid;grid-template-columns:1fr;gap:9px;margin-top:11px}
  #login a.ssobtn{display:none;align-items:center;justify-content:center;gap:9px;min-height:47px;padding:11px 12px;border:1px solid #d5dee8;border-radius:11px;background:#fff;color:#17324d;font-size:13px;font-weight:650;text-decoration:none;box-shadow:0 2px 7px rgba(18,46,73,.04)}
  #login a.ssobtn:hover{border-color:#9db3c8;background:#f9fbfd}
  #login a.ssobtn:active{transform:translateY(1px)}
  .ssodiv{display:flex;align-items:center;gap:12px;margin-top:18px;color:#8999a9;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
  .ssodiv:before,.ssodiv:after{content:"";flex:1;height:1px;background:#e1e7ed}
  .create-row{display:flex;align-items:center;justify-content:center;gap:5px;margin-top:18px;color:#718293;font-size:12.5px}
  #login .account-link{margin:0;padding:3px 4px;border:0;background:transparent;color:#246da9;font-size:12.5px;font-weight:750;cursor:pointer;font-family:var(--font)}
  #login .account-link:hover{text-decoration:underline}
  .demo-panel{margin-top:16px;padding-top:14px;border-top:1px solid #e4e9ef;text-align:center}
  #login .demo-btn{width:100%;min-height:45px;margin:0;padding:11px 14px;border:1px solid #9ab5ce;border-radius:11px;background:linear-gradient(135deg,#f8fbfe,#eaf2f9);color:#194872;font-family:var(--font);font-size:13px;font-weight:750;cursor:pointer;box-shadow:0 3px 9px rgba(25,72,114,.08)}
  #login .demo-btn:hover{border-color:#6f97bb;background:linear-gradient(135deg,#f4f9fd,#e3eef7)}
  #login .demo-btn:active{transform:translateY(1px)}
  #login .demo-btn[disabled]{opacity:.55;cursor:wait}
  /* Demo role chooser: homeowners and subcontractors see different apps, so
     the demo asks which to show. Same light-card language as .demo-btn. */
  #login .demo-choice{display:block;width:100%;text-align:left;margin:0 0 9px 0;padding:12px 15px;border:1px solid #9ab5ce;border-radius:11px;background:linear-gradient(135deg,#f8fbfe,#eaf2f9);color:#194872;font-family:var(--font);cursor:pointer;box-shadow:0 3px 9px rgba(25,72,114,.08)}
  #login .demo-choice:hover{border-color:#6f97bb;background:linear-gradient(135deg,#f4f9fd,#e3eef7)}
  #login .demo-choice:active{transform:translateY(1px)}
  #login .demo-choice[disabled]{opacity:.55;cursor:wait}
  #login .demo-choice .dt{display:block;font-size:13.5px;font-weight:750}
  #login .demo-choice .dd{display:block;font-size:12px;font-weight:500;color:#5b7896;margin-top:2px;line-height:1.4}
  #login .demo-cancel{display:inline-block;margin-top:2px;font-size:12.5px;color:#5b7896;text-decoration:underline;cursor:pointer;background:none;border:none;font-family:var(--font)}
  #login .foot{margin-top:18px;color:rgba(213,227,240,.58);font-size:9.5px;text-align:center;letter-spacing:.05em;text-transform:uppercase}

  @media(max-width:899px){
    body:has(#login.on){height:100dvh;overflow:hidden;background:#06182e}
    #login.on{position:fixed;inset:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;touch-action:pan-y;background:#06182e}
    .login-shell{display:block;min-height:100dvh;background:linear-gradient(155deg,#06172d 0%,#0a3769 48%,#071a31 100%)}
    .login-hero{min-height:245px;padding:calc(16px + env(safe-area-inset-top)) 20px 54px;justify-content:flex-start}
    .login-logo-link{width:min(320px,88vw)}
    .hero-shield{width:330px;right:-112px;top:20px;opacity:.13}
    .hero-copy{margin:18px auto 0;max-width:350px;text-align:center}
    .hero-kicker{font-size:9.5px;letter-spacing:.13em}
    .hero-kicker:before,.hero-kicker:after{width:27px}
    .hero-copy h1{margin-top:10px;font-size:32px;line-height:.99}
    .hero-copy p{margin-top:8px;max-width:345px;font-size:11.8px;line-height:1.48}
    .hero-audience-line{display:none}
    .hero-foot{display:none}
    .auth-zone{display:block;margin-top:-46px;padding:0 14px calc(18px + env(safe-area-inset-bottom));background:transparent}
    .auth-zone:before{display:none}
    .login-panel{max-width:500px;margin:0 auto}
    .auth-card{padding:22px 19px 20px;border-radius:19px;box-shadow:0 22px 58px rgba(0,0,0,.3)}
    .auth-kicker{font-size:9.5px}
    #login .auth-title{font-size:26px}
    #login .auth-sub{font-size:12.5px}
    #login form{margin-top:17px}
    .field-label-row{margin:12px 0 6px}
    #login input{min-height:47px;padding:11px 13px;font-size:16px}
    #login .auth-submit{min-height:47px;margin-top:16px}
    #login a.ssobtn{min-height:44px;font-size:12px}
    .ssodiv{margin-top:15px}
    .create-row{margin-top:15px}
    .demo-panel{margin-top:14px;padding-top:13px}
    #login .foot{margin-top:14px;line-height:1.45}
  }
  /* A tablet held upright (Pixel Tablet 800 wide, iPad 768-834) takes the
     stacked sign-in above, which is sized for a phone: on a 1280-tall screen
     everything sat in the top half over a long empty navy tail. Here the
     stack is centered top to bottom and everything is a size up. */
  @media(min-width:600px) and (max-width:899px) and (min-height:900px){
    .login-shell{display:flex;flex-direction:column;justify-content:center;padding:calc(24px + env(safe-area-inset-top)) 0 calc(24px + env(safe-area-inset-bottom))}
    .login-hero{min-height:0;padding:0 32px 96px}
    .login-logo-link{width:min(440px,70vw)}
    .hero-shield{width:520px;right:-120px;top:-40px}
    .hero-copy{margin-top:44px;max-width:560px}
    .hero-kicker{font-size:11.5px}
    .hero-copy h1{margin-top:16px;font-size:50px}
    .hero-copy p{margin-top:16px;max-width:520px;font-size:15.5px;line-height:1.6}
    .hero-audience-line{display:flex;margin-top:26px}
    .auth-zone{margin-top:-56px;padding:0 32px}
    .login-panel{max-width:560px}
    .auth-card{padding:34px 38px 30px;border-radius:22px}
    #login .auth-title{font-size:30px}
    #login .auth-sub{font-size:14px;max-width:400px}
    #login form{margin-top:22px}
    .field-label-row{margin:16px 0 7px}
    #login input{min-height:54px}
    #login .auth-submit{min-height:54px;margin-top:22px;font-size:15px}
    #login a.ssobtn{min-height:50px;font-size:13.5px}
    .ssodiv{margin-top:20px}
    .create-row{margin-top:20px}
    .demo-panel{margin-top:18px;padding-top:17px}
    #login .demo-btn{min-height:50px;font-size:14px}
    #login .foot{margin-top:22px;font-size:10.5px}
  }
  /* Small copyright line shown at the bottom of every portal screen. */
  .copyline{padding:6px 16px 14px;text-align:center;font-size:11px;color:var(--muted);letter-spacing:.04em}
  .copyline.light{color:rgba(255,255,255,.65);padding-top:18px}

  /* ---------- ADMIN PROJECT PICKER ---------- */
  /* Do NOT add background-attachment:fixed here. It looks tempting on the long
     admin lists (the gradient otherwise stretches over thousands of pixels and
     reads as two flat blocks), but a fixed background on an element this tall
     composites over the content and blanks the page. */
  /* One solid navy (v204, Thomas: the light blue at the top of the admin
     dashboard goes; the whole page one color). */
  #picker{display:none;flex-direction:column;min-height:100vh;padding:52px 28px 40px;background:var(--navy);color:#fff}
  #picker.on{display:flex}
  /* Mobile: the admin dashboard is one scroll. The logo, the tabs, the
     list, Sign out and the copyright move together, so a long list uses the
     full screen instead of a short window above a pinned footer. */
  @media (max-width: 767px){
    body:has(#picker.on){overflow:hidden;height:100dvh;overscroll-behavior:none}
    /* html.ready matches the first-paint rule. That rule's padding (28px, no
       safe area) is more specific than #picker.on, so the logo sat under the
       status bar and the Dynamic Island. Same specificity, later in the
       cascade, and the inset is real because the viewport is viewport-fit=cover.
       The bottom padding is the only home-indicator inset, plus a small margin
       after the copyright. */
    html.ready #picker.on{position:fixed;inset:0;min-height:0;overflow-x:hidden;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;touch-action:pan-y;padding:calc(12px + env(safe-area-inset-top)) 18px calc(8px + env(safe-area-inset-bottom))}
    #picker.on > *{flex-shrink:0}
    /* Compact type so the project list is the focal point. */
    #picker.on h1{margin-top:12px;font-size:20px}
    #picker.on .sub2{margin-top:2px;font-size:12.5px}
    #picker.on .ptabs{margin:12px auto 10px}
    #picker.on .ptab{padding:8px;font-size:12.5px}
    #picker.on .plist{margin-top:10px;gap:10px}
    #picker.on .pcard{padding:14px 16px}
    #picker.on .signout{margin-top:8px;padding-top:0}
    #picker.on .signout button{padding:10px}
    #picker.on .copyline{padding:8px 16px 0;font-size:10px}
  }
  /* The iOS app draws edge to edge at every width. A wide iPad never enters
     the phone block above, and the first-paint padding would leave the logo
     under the status bar there too. */
  html.native.ready #picker.on{padding-top:calc(12px + env(safe-area-inset-top))}
  html.native.ready #picker.on{padding-bottom:calc(8px + env(safe-area-inset-bottom))}
  #picker .logo{width:210px;max-width:70%}
  #picker .tag{font-size:11.5px;color:rgba(255,255,255,.85);letter-spacing:.1em;margin-top:14px;font-weight:700;text-transform:uppercase}
  #picker h1{font-weight:600;font-size:26px;margin-top:34px;letter-spacing:-.02em}
  #picker .sub2{font-size:14px;color:rgba(255,255,255,.88);margin-top:6px}
  #picker .plist{margin-top:26px;display:flex;flex-direction:column;gap:12px}
  #picker .pcard{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;background:#fff;color:var(--navy);border:none;border-radius:var(--r-card);padding:18px;cursor:pointer;font-family:var(--font);text-align:left;box-shadow:var(--btnshadow)}
  #picker .pcard:active{transform:translateY(1px)}
  /* Reorder controls on the admin picker's Projects and Overviews cards: a
     drag grip for a mouse, plus explicit move-earlier/move-later buttons so
     the same reorder works with a tap - Thomas runs this app on his phone as
     much as his desk, and a phone has no drag-and-drop. */
  #picker .pcard.reorderable{cursor:pointer}
  #picker .pright{display:flex;align-items:center;gap:10px;flex:none}
  /* v238: the card's only control is a three-dot button at the far right;
     Move up, Move down and Delete live in the menu it opens. */
  #picker .pmenu{width:36px;height:36px;border-radius:999px;border:1px solid var(--line);background:#fff;color:var(--navy);
    display:flex;align-items:center;justify-content:center;padding:0;cursor:pointer}
  #picker .pmenu[aria-expanded="true"]{background:#eef3f8}
  .pmenu-pop{position:fixed;z-index:60;min-width:168px;padding:6px 0;background:#fff;color:var(--navy);border-radius:12px;box-shadow:0 10px 30px rgba(6,23,45,.35);border:1px solid var(--line)}
  .pmenu-pop button{display:block;width:100%;text-align:left;padding:11px 16px;background:none;border:0;font-family:var(--font);font-size:14px;font-weight:600;color:var(--navy);cursor:pointer}
  .pmenu-pop button:hover,.pmenu-pop button:focus-visible{background:#eef3f8;outline:0}
  .pmenu-pop button.del{color:#8a2f1f}
  .pmenu-pop button:disabled{opacity:.35;cursor:default;background:none}
  #picker .pcard .pn{font-size:16px;font-weight:700}
  #picker .pcard .ps{font-size:12.5px;color:var(--muted);margin-top:2px}
  #picker .pcard .chev{color:var(--blue);font-size:24px;font-weight:700;flex:none;line-height:1}
  /* Active job: a fresh green tint that reads "in progress". */
  #picker .pcard.active{background:#eaf7ef;border:1px solid #cbe7d6}
  #picker .pcard.active .chev{color:#1a8f4a}
  /* Archived = completed / $0 balance: light slate gray, still tappable. */
  /* Delete, on a project card. Opens a sheet that asks for the project name.
     The server hides that one id (config and Drive stay) and files a record
     in the Portal Archive folder in Drive. On Projects and Overviews alike. */
  #picker .pcard.archived{background:#eef1f4;border:1px solid #d8dfe6}
  #picker .pcard.archived .pn{color:#5b6b7c}
  #picker .pcard.archived .ps{color:#8794a3}
  #picker .pcard.archived .chev{color:#97a3b1}
  #picker .pcard .apill{display:inline-block;margin-top:6px;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;border-radius:999px;padding:2px 8px}
  #picker .pcard .apill.act{color:#1a7a3f;background:#cdeed9}
  #picker .pcard .apill.arch{color:#5b6b7c;background:#dde3e9}
  #picker .pcard.completed{background:#e8f1fb;border:1px solid #cddff2}
  #picker .pcard.completed .chev{color:#2f6fb0}
  #picker .pcard .apill.comp{color:#1c5a94;background:#d3e6f8}
  #picker .ptabs{display:flex;gap:8px;width:100%;max-width:560px;margin:24px auto 18px}
  #picker .ptab{flex:1;background:rgba(255,255,255,.12);color:#cfe0ef;border:1px solid rgba(255,255,255,.2);border-radius:999px;padding:10px;font-family:var(--font);font-weight:600;font-size:13px;cursor:pointer}
  #picker .ptab.active{background:#fff;color:var(--navy);border-color:#fff}
  #picker .usr{background:#fff;color:var(--navy);border-radius:14px;padding:15px 16px;margin-bottom:12px;box-shadow:var(--btnshadow)}
  /* Users on a computer: two columns, A to Z down each column's rows (v263).
     Open invitations keep the full width above them. */
  @media(min-width:768px){
    html:not(.native) #usersView{max-width:1000px!important}
    /* v271: one full grid, every card the same height (the tallest sets
       it), the buttons pinned to the foot so the rows line up. */
    html:not(.native) #usersList{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:1fr;gap:14px;align-items:stretch}
    html:not(.native) #usersList .usr{margin-bottom:0;display:flex;flex-direction:column}
    html:not(.native) #usersList .usr .uacc{flex:1;display:flex;flex-direction:column}
    html:not(.native) #usersList .usr .uacc .ubtns{margin-top:auto;padding-top:12px}
    html:not(.native) #usersList .usr .umuted{flex:1}
    html:not(.native) #usersList > .um,html:not(.native) #usersList > .inv,html:not(.native) #usersList > div[style*="height"]{grid-column:1 / -1}
  }
  #picker .usr .un{font-size:15px;font-weight:700;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
  /* The engagement score beside a name (v270), on Account and on Users. */
  .uscore{display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:700;letter-spacing:.02em;padding:2px 9px;border-radius:999px;background:linear-gradient(135deg,#f3b64a,#e58f1f);color:#2b1a03;white-space:nowrap;vertical-align:middle}
  .uscore:before{content:'\2605';font-size:11px}
  #picker .usr .ulast{font-size:12.5px;color:var(--muted);margin-top:4px}
  /* v272: a small search box under the header on Users. */
  #picker .usearch{margin:0 0 14px}
  #picker .usearch input{width:100%;max-width:360px;display:block;margin:0 auto;padding:10px 14px;border:1px solid rgba(255,255,255,.25);border-radius:999px;background:rgba(255,255,255,.12);color:#fff;font-family:var(--font);font-size:14px;outline:none;-webkit-appearance:none;appearance:none}
  #picker .usearch input::placeholder{color:rgba(255,255,255,.6)}
  #picker .usearch input:focus{border-color:#fff;background:rgba(255,255,255,.18)}
  #picker .usr.hid{display:none!important}
  #accScoreRow .v,#accSigninRow .v{display:flex;align-items:center;gap:8px;justify-content:flex-end}
  #picker .usr .um{font-size:12.5px;color:var(--muted);margin-top:3px;word-break:break-all}
  #picker .usr .ubadge{display:inline-block;font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;padding:2px 8px;border-radius:999px;margin-top:7px}
  #picker .usr .ubadge.admin{background:#0f2c47;color:#cfe0ef}
  #picker .usr .urolerow{margin-top:8px;display:flex;align-items:center;gap:8px}
  #picker .usr select.urole{font-family:var(--font);font-size:13px;font-weight:600;color:var(--navy);border:1px solid rgba(25,72,114,.3);border-radius:8px;padding:5px 8px;background:#fff}
  #picker .usr .uallrow{display:flex;align-items:center;gap:8px;font-size:13.5px;color:var(--navy);cursor:pointer;margin-bottom:2px}
  #picker .usr .uallrow input{width:16px;height:16px}
  #picker .usr .ubadge.client{background:#e7f0f8;color:var(--blue)}
  #picker .usr .ubadge.sub{background:#e9e2f6;color:#5b3fa6}
  #picker .usr .ubadge.closed{background:#f3e3e3;color:#9a2f2f}
  #picker .usr .ubadge.owner{background:#e3f1e7;color:#1d6b3a}
  #picker .usr .ucompany{font-size:13px;color:#4a5b70;margin-top:6px}
  #picker .inv{background:#fff;color:var(--navy);border-radius:14px;padding:12px 16px;margin-bottom:10px;box-shadow:var(--btnshadow);display:flex;justify-content:space-between;align-items:center;gap:10px}
  #picker .inv .d{font-size:12.5px;color:#4a5b70}
  .mjbody{max-height:60vh;overflow:auto;-webkit-overflow-scrolling:touch}
  .mjrow{display:flex;justify-content:space-between;align-items:center;gap:10px;padding:11px 0;border-bottom:1px solid #e6ecf3;font-size:15px}
  .mjrow:last-child{border-bottom:0}
  .mjrow .l{flex:1;min-width:0}
  .mjrow .l small{display:block;color:#55657a;font-size:12.5px;margin-top:2px}
  .mjrow .r{text-align:right;white-space:nowrap;font-weight:600}
  .mjpill{display:inline-block;font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;padding:2px 8px;border-radius:999px;background:#e7f0f8;color:var(--blue);margin-left:6px;vertical-align:middle}
  .mjpill.paid,.mjpill.done{background:#e3f1e7;color:#1d6b3a}
  .mjpill.due,.mjpill.now{background:#fff1dc;color:#9a5a00}
  .mjpill.scope{background:#f3e3e3;color:#9a2f2f}
  .mjpost{padding:12px 0;border-bottom:1px solid #e6ecf3}
  .mjpost:last-child{border-bottom:0}
  .mjpost .t{white-space:pre-wrap;line-height:1.5;font-size:15px}
  .mjpost .d{color:#55657a;font-size:12.5px;margin-top:5px}
  .mjtotal{display:flex;justify-content:space-between;padding:12px 0 2px;font-weight:700;font-size:15px}
  .mjtotal.soft{font-weight:500;color:#55657a}
  .mjcal{font-size:12.5px;font-weight:600;color:var(--blue);text-decoration:none;white-space:nowrap}
  .mthread{max-height:46vh;overflow:auto;display:flex;flex-direction:column;gap:8px;padding:4px 0 8px}
  .mbub{max-width:88%;padding:10px 13px;border-radius:14px;background:#eef4fa;align-self:flex-start}
  .mbub.me{align-self:flex-end;background:#194872;color:#fff}
  .mbub .t{white-space:pre-wrap;font-size:15px;line-height:1.45}
  .mbub .d{font-size:11.5px;opacity:.7;margin-top:4px}
  .mjbody .mcompose{display:flex;gap:8px;align-items:flex-end;margin-top:6px}
  .mjbody .mcompose textarea{flex:1 1 auto;min-width:0;height:48px;min-height:48px;max-height:120px;margin:0;border:1px solid #cfd9e4;border-radius:12px;padding:12px;font:inherit;font-size:15px;line-height:1.3;resize:none;box-sizing:border-box}
  .mjbody .mcompose .btn{flex:0 0 auto;width:auto;min-width:0;margin:0;padding:0 18px;height:48px;font-size:14px}
  #homeMessages .doc{margin:14px 0 0}
  #homeMessages .msgcard{margin:14px 0 0}
  #homeMessages .msgcard.unread{border-color:#194872;box-shadow:0 0 0 1px #194872 inset,var(--shadow)}
  #homeMessages .msgprev{display:flex;gap:12px;align-items:flex-start;margin:10px 0 14px;font-size:15px;line-height:1.45}
  #homeMessages .msgprev.none{display:block;color:#5a6f86}
  #homeMessages .msgprev .subav{width:40px;height:40px;border-radius:50%;background:linear-gradient(135deg,#194872,#4688c3);display:inline-flex;align-items:center;justify-content:center;font-weight:700;font-size:14px;flex:none;color:#fff}
  #homeMessages .mp{min-width:0;flex:1}
  #homeMessages .mw{display:flex;align-items:baseline;gap:8px}
  #homeMessages .mw b{font-weight:600;color:#194872}
  #homeMessages .mw small{font-size:12.5px;color:#5a6f86}
  #homeMessages .mt{color:#22374d;overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}
  #homeMessages .msgcard .btn{display:block;margin:0}
  .docbadge{display:inline-block;min-width:20px;padding:1px 7px;border-radius:999px;background:#d33;color:#fff;font-size:11.5px;font-weight:700;margin-left:8px;vertical-align:middle}
  .joinline{background:#eef4fa;border-radius:12px;padding:12px 14px;font-size:14px;line-height:1.45;color:var(--navy);margin:0 0 14px}
  .joinline b{display:block;font-size:15px;margin-bottom:2px}
  #picker .rq .ubadge.sub{background:#e9e2f6;color:#5b3fa6}
  #picker .rq .ubadge.client{background:#e7f0f8;color:var(--blue)}
  #picker .rq .ubadge{display:inline-block;font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;padding:2px 8px;border-radius:999px}
  #picker .usr .uacc{margin-top:11px;border-top:1px solid #eef1f5;padding-top:11px}
  #picker .usr label{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:500;margin:5px 0;cursor:pointer}
  #picker .usr label input{width:16px;height:16px}
  #picker .usr .ubtns{display:flex;gap:9px;margin-top:12px}
  #picker .usr .ubtns .btn{flex:1;border-radius:10px;padding:10px;font-family:var(--font);font-weight:600;font-size:13px;cursor:pointer;border:none}
  #picker .usr .ubtns .save{background:var(--blue);color:#fff}
  #picker .usr .ubtns .rm{background:#fde8e8;color:#b4232a}
  #picker .usr .umuted{font-size:12.5px;color:var(--muted);margin-top:11px;border-top:1px solid #eef1f5;padding-top:11px}
  #picker .rq{background:#fff;color:var(--navy);border-radius:14px;padding:15px 16px;margin-bottom:12px;box-shadow:var(--btnshadow)}
  #picker .rq .rqn{font-size:15px;font-weight:700}
  #picker .rq .rqm{font-size:12.5px;color:var(--muted);margin-top:3px;line-height:1.5}
  #picker .rq .rqrolerow{margin-top:8px;display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--muted)}
  #picker .rq select.rqrole{font-family:var(--font);font-size:13px;font-weight:600;color:var(--navy);border:1px solid rgba(25,72,114,.3);border-radius:8px;padding:5px 8px;background:#fff}
  #picker .rq .rqacc{margin-top:11px;border-top:1px solid #eef1f5;padding-top:11px}
  #picker .rq label{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:500;margin:5px 0;cursor:pointer}
  #picker .rq label input{width:16px;height:16px}
  #picker .rq .rqprojs{margin:4px 0 4px 6px;padding-left:10px;border-left:2px solid #eef1f5}
  #picker .rq .aprbtns{display:flex;gap:9px;margin-top:12px}
  #picker .rq .aprbtns .btn{flex:1;border-radius:10px;padding:10px;font-family:var(--font);font-weight:600;font-size:13px;cursor:pointer;border:none}
  #picker .rq .aprbtns .pri{background:var(--blue);color:#fff}
  #picker .rq .aprbtns .sec{background:#f0f3f7;color:var(--navy)}
  /* v240 (Thomas, 2026-10-13): the v238 locked footer is gone. Sign out and
     the copyright sit under the list again and scroll with it, exactly as
     before v238; a long project list no longer runs over the button. */
  /* v263 (Thomas, 2026-10-28): "I need to be able to sign myself out." A
     Sign out at the top right of the admin dashboard, on a computer and on
     a phone, so it never depends on scrolling past the project list. The
     one under the list stays. */
  #picker{position:relative}
  #picker .ptopout{position:absolute;top:calc(14px + env(safe-area-inset-top));right:18px;background:rgba(255,255,255,.14);color:#fff;border:1px solid rgba(255,255,255,.3);border-radius:999px;padding:7px 14px;font-family:var(--font);font-weight:600;font-size:12.5px;letter-spacing:.04em;cursor:pointer}
  #picker .ptopout:hover{background:rgba(255,255,255,.24)}
  @media(min-width:768px){ #picker .ptopout{top:20px;right:28px} }
  .embed #picker .ptopout{display:none!important}
  #picker .signout{margin-top:24px;padding-top:0}
  #picker .signout button{background:rgba(255,255,255,.14);color:#fff;border:1px solid rgba(255,255,255,.3);border-radius:var(--r-btn);padding:13px;width:100%;font-family:var(--font);font-weight:600;font-size:13px;letter-spacing:.04em;cursor:pointer}

  /* ---------- APP SHELL ---------- */
  #app{display:none;padding-bottom:88px}
  /* The admin's Overviews tab (v235): the same dark navy the subcontractors
     see, so the list and the view it opens are one color. body.ovdark is set
     only while that tab is up; Projects and Users keep the brand navy (v240
     tried the dark look on Projects for a day; Thomas asked for the lighter
     color back in v241). */
  html.ready body.ovdark #picker.on,body.ovdark #picker{background:#06172d !important;background-color:#06172d !important}
  html:has(body.ovdark #picker.on){background:#06172d !important;background-color:#06172d !important}
  body.ovdark #picker .pcard,body.ovdark #picker .pcard.active,body.ovdark #picker .pcard.completed,body.ovdark #picker .pcard.archived{background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.16);color:#fff;box-shadow:none}
  body.ovdark #picker .pcard .pn,body.ovdark #picker .pcard.archived .pn{color:#fff}
  body.ovdark #picker .pcard .ps,body.ovdark #picker .pcard.archived .ps{color:rgba(255,255,255,.75)}
  body.ovdark #picker .pcard .chev,body.ovdark #picker .pcard.active .chev,body.ovdark #picker .pcard.completed .chev{color:#8dc1f2}
  body.ovdark #picker .pcard .apill.act{color:#6fd69b;background:rgba(46,125,84,.34)}
  body.ovdark #picker .pcard .apill.comp{color:#9ccbf5;background:rgba(70,136,195,.34)}
  body.ovdark #picker .pcard .apill.arch{color:rgba(255,255,255,.75);background:rgba(255,255,255,.12)}
  body.ovdark #picker .pmenu{color:#fff;background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.25)}
  body.ovdark #picker .pcard.active .chev{color:#8dc1f2}
  /* ---- the subcontractor view (v201): the dark navy of the login page.
     Every rule is scoped to body.subview, which only a subcontractor's
     session sets, so the homeowner app keeps its light brand untouched. */
  body.subview,body.subview:has(#app.on),html:has(body.subview){background:#06172d !important;background-color:#06172d !important;color:#fff}
  /* The locked shell (v199/v200) paints every screen and the copyright line
     paper. The dark view is one flat navy, the same color as the header.
     A gradient here (v201) put a lighter blue under the header and again
     at the footer. */
  body.subview #app,body.subview #app.on{background:#06172d !important;background-image:none !important;min-height:100dvh;color:#fff}
  body.subview #app.on .screen,body.subview #app.on .screen.active,body.subview #app.on .copyline{background:transparent !important}
  body.subview .apphead,body.subview #adminbar{background:#06172d;background-color:#06172d;box-shadow:none}
  body.subview .apphead{border-bottom:0}
  body.subview #picker{background:#06172d}
  /* The sub's job list (v204 audit): the cards were white with the names
     turned white by the dark rules, so the job names vanished. Dark cards,
     white names, readable pills. */
  body.subview #picker .pcard,body.subview #picker .pcard.active,body.subview #picker .pcard.completed,body.subview #picker .pcard.archived{background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.16);color:#fff}
  body.subview #picker .pcard .pn,body.subview #picker .pcard.archived .pn{color:#fff}
  body.subview #picker .pcard .ps,body.subview #picker .pcard.archived .ps{color:rgba(255,255,255,.75)}
  body.subview #picker .pcard .chev,body.subview #picker .pcard.active .chev,body.subview #picker .pcard.completed .chev{color:#8dc1f2}
  body.subview #picker .pcard .apill.act{color:#6fd69b;background:rgba(46,125,84,.34)}
  body.subview #picker .pcard .apill.comp{color:#9ccbf5;background:rgba(70,136,195,.34)}
  body.subview #picker .pcard .apill.arch{color:rgba(255,255,255,.75);background:rgba(255,255,255,.12)}
  body.subview .screen{color:#fff;background:transparent}
  body.subview h2,body.subview .ccrumb b,body.subview .pcard .pn,body.subview .fxrn,body.subview .acctrow .v,body.subview .sheet h3,body.subview .mjrow,body.subview .mjpost .t,body.subview .mjtotal{color:#fff}
  body.subview .sub,body.subview .meta,body.subview .note,body.subview .label,body.subview .mjrow .l small,body.subview .mjpost .d,body.subview .acctrow .k,body.subview .pcard .pd,body.subview .fxrm,body.subview .ccrumb,body.subview .copyline,body.subview .cempty{color:rgba(255,255,255,.72)}
  body.subview .grp,body.subview .fxcat,body.subview .cback,body.subview .mjcal{color:#8dc1f2}
  body.subview .card,body.subview a.doc,body.subview #docsList a.doc,body.subview #docsContacts a.doc,body.subview .docnote,body.subview .scanbtn,body.subview .pcard,body.subview .cocard,body.subview .fxlist,body.subview .cempty,body.subview .subrow{background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.12);box-shadow:none;color:#fff}
  body.subview .docnote,body.subview .scanbtn{background:rgba(70,136,195,.28);border-color:rgba(141,193,242,.3)}
  body.subview a.doc .t,body.subview #docsList a.doc .t{color:#fff}
  body.subview a.doc:hover,body.subview #docsList a.doc:hover,body.subview #docsContacts a.doc:hover{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.2)}
  body.subview a.doc:active,body.subview #docsList a.doc:active{background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.2)}
  body.subview .fxlist .fxcard{border-top-color:rgba(255,255,255,.1)}
  body.subview .fxth{background:rgba(255,255,255,.1)}
  body.subview .fxrs span{color:#fff;background:rgba(255,255,255,.14)}
  body.subview .mjpill{background:rgba(255,255,255,.14);color:rgba(255,255,255,.82)}
  body.subview .mjpill.paid,body.subview .mjpill.done{background:rgba(46,125,84,.34);color:#6fd69b}
  body.subview .mjpill.now{background:rgba(70,136,195,.38);color:#9ccbf5}
  body.subview .mjpill.due{background:rgba(179,103,31,.36);color:#ffc46b}
  body.subview .mjpill.scope{background:rgba(154,47,47,.36);color:#ffb3b3}
  body.subview .docbadge{background:#e05252}
  /* Color audit (v204): every text on the sub view at 4.5:1 or better
     (3:1 for large or bold text), measured against what is really behind it. */
  body.subview .mjtotal.soft{color:rgba(255,255,255,.78)}
  body.subview .scal .dow div{color:rgba(255,255,255,.75)}
  body.subview .ccrumb i{opacity:1}
  body.subview a.cocard .cocount.none{color:rgba(255,255,255,.82)}
  body.subview .fxlink,body.subview .fxcard .ext{color:#8dc1f2}
  body.subview #contactModal .sub{display:none}
  body.subview #docsMyJob a.doc{justify-content:center;text-align:center;min-height:56px}
  body.subview #docsMyJob a.doc .t{display:inline-flex;align-items:center;gap:8px}
  body.subview .notiftoggle{color:#fff}
  body.subview .acctrow{border-bottom-color:rgba(255,255,255,.1)}
  body.subview .switch .slider{background:rgba(255,255,255,.22)}
  body.subview .switch input:checked + .slider{background:#3f9d6c}
  body.subview .notifrow .t{color:#fff}
  body.subview .sheet{background:#0b2240;color:#fff}
  body.subview .mjrow,body.subview .mjpost{border-color:rgba(255,255,255,.1)}
  body.subview .mbub{background:rgba(255,255,255,.12);color:#fff}
  body.subview .mbub.me{background:linear-gradient(135deg,#194872,#4688c3)}
  body.subview .mjbody .mcompose textarea,body.subview .acctinput,body.subview input[type=text],body.subview input[type=password],body.subview textarea{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.2);color:#fff}
  body.subview .mjbody .mcompose textarea::placeholder,body.subview .acctinput::placeholder{color:rgba(255,255,255,.5)}
  body.subview .btn.sec{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.28);color:#fff}
  body.subview .btn.pri{background:linear-gradient(135deg,#194872,#4688c3);box-shadow:0 8px 18px rgba(0,0,0,.25)}
  body.subview #nav{background:rgba(4,16,31,.86);border-top:0;box-shadow:none;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}
  body.subview #nav button{color:rgba(255,255,255,.55)}
  body.subview #nav button.active{color:#fff}
  body.subview #nav button[data-s="docs"] .lbl{font-size:inherit}
  body.subview #nav button[data-s="docs"] .lbl::after{content:none}
  body.subview .topnav button{color:rgba(255,255,255,.8)}
  body.subview .topnav button.active{background:#fff;color:#06172d}
  /* Home */
  body.subview .subactive{display:block;width:100%;text-align:left;border:1px solid rgba(141,193,242,.35);background:linear-gradient(135deg,rgba(70,136,195,.55),rgba(25,72,114,.65));border-radius:var(--r-card);padding:14px;margin-bottom:10px;font-family:var(--font);color:#fff;cursor:pointer}
  body.subview .subactive .dates{font-size:12.5px;color:rgba(255,255,255,.82);display:flex;align-items:center;gap:8px}
  body.subview .subactive .n{font-size:17px;font-weight:600;margin:6px 0 4px}
  body.subview .subactive .more{font-size:12.5px;color:rgba(255,255,255,.85)}
  body.subview a.doc .t2{display:flex;flex-direction:column;min-width:0;flex:1;text-align:left}
  body.subview a.doc .t2 b{font-weight:600;font-size:14.5px;color:#fff}
  body.subview a.doc .t2 small{color:rgba(255,255,255,.72);font-size:12.5px;margin-top:2px}
  body.subview .subpost{padding:12px 14px}
  body.subview .subpost .who{display:flex;justify-content:space-between;font-size:12.5px;color:rgba(255,255,255,.72);margin-bottom:6px}
  body.subview .subpost .who b{color:#fff}
  body.subview .subpost .tx{font-size:14px;line-height:1.45;white-space:pre-wrap}
  /* Schedule: the month */
  body.subview .scal{background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.12);border-radius:var(--r-card);padding:12px 10px 10px;margin-bottom:12px}
  body.subview .scal .mh{display:flex;justify-content:space-between;align-items:center;padding:0 2px 8px;font-size:14.5px;font-weight:600}
  body.subview .scal .mh button{background:none;border:0;color:rgba(255,255,255,.7);font-size:22px;line-height:1;width:40px;height:36px;cursor:pointer;font-family:var(--font)}
  body.subview .scal .dow,body.subview .scal .days{display:grid;grid-template-columns:repeat(7,minmax(0,1fr))}
  body.subview .scal .dow div{text-align:center;font-size:10.5px;color:rgba(255,255,255,.5);font-weight:600;padding-bottom:6px}
  body.subview .sday{font-size:12.5px;color:rgba(255,255,255,.72);margin:-4px 0 10px 4px}
  body.subview .subrow{display:flex;justify-content:space-between;align-items:center;gap:10px;width:100%;text-align:left;border-radius:var(--r-card);padding:12px 14px;margin-bottom:8px;font-family:var(--font);cursor:pointer}
  body.subview .subrow .l{flex:1;min-width:0;font-size:14.5px;font-weight:600;color:#fff}
  body.subview .subrow .l small{display:block;font-weight:400;font-size:12.5px;color:rgba(255,255,255,.72);margin-top:2px}
  body.subview .subrow .r{white-space:nowrap;display:flex;align-items:center;gap:8px}
  body.subview .subav{width:40px;height:40px;border-radius:50%;background:linear-gradient(135deg,#194872,#4688c3);display:inline-flex;align-items:center;justify-content:center;font-weight:700;font-size:14px;flex:none;color:#fff}
  body.subview .subav.co{background:rgba(255,255,255,.14)}
  body.subview .subav.bb{background:rgba(255,196,107,.22);color:#ffc46b}
  body.subview .subav img,.subav img{width:100%;height:100%;object-fit:cover;border-radius:50%;display:block}
  .mphotos{display:flex;gap:6px;flex-wrap:wrap;margin-top:6px}
  .mphotos img{width:96px;height:96px;object-fit:cover;border-radius:9px;display:block;background:rgba(255,255,255,.1)}
  .mpend{display:flex;gap:6px;flex-wrap:wrap;margin:6px 0 2px}
  .mpend span{position:relative;width:52px;height:52px;border-radius:8px;overflow:hidden;background:rgba(255,255,255,.1)}
  .mpend span img{width:100%;height:100%;object-fit:cover;display:block}
  .mpend span b{position:absolute;top:0;right:0;width:18px;height:18px;border-radius:0 0 0 8px;background:rgba(0,0,0,.55);color:#fff;font-size:12px;line-height:18px;text-align:center;cursor:pointer}
  .mcam{flex:0 0 auto;width:44px;height:48px;border:1px solid rgba(25,72,114,.25);border-radius:10px;background:#fff;color:var(--navy);display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
  .mcam svg{width:22px;height:22px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
  body.subview .mcam{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.25);color:#fff}
  .mbub .who{font-size:11.5px;font-weight:600;opacity:.8;margin-bottom:3px}
  body.subview .accphoto{display:flex;align-items:center;gap:14px;margin:4px 0 16px}
  body.subview .accphoto .big{width:68px;height:68px;border-radius:50%;background:linear-gradient(135deg,#194872,#4688c3);display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:700;overflow:hidden;flex:none}
  body.subview .accphoto .big img{width:100%;height:100%;object-fit:cover}
  /* ---- Messages between people on the app, iMessage-style (v215, 2026-10-08).
     Thomas: open messaging between subcontractors; "mimic the messaging
     platform I built on revvek.com ... attach, make reactions, and click
     reply to ... in the Torc brand voice". The thread, bubbles, quotes,
     reactions, tapback popover, reply bar, attachments, emoji drawer and
     lightbox are Revvek's, in the Portal's navy. Everything under body.subview. */
  body.subview .inboxtop{display:flex;align-items:center;gap:10px;margin:0 0 12px}
  body.subview .inboxtop h2{flex:1;margin:0}
  body.subview .composebtn{display:inline-flex;align-items:center;gap:8px;min-height:42px;padding:0 14px 0 12px;border-radius:13px;border:1px solid rgba(141,193,242,.35);background:linear-gradient(135deg,#194872,#4688c3);color:#fff;font:600 14px var(--font);cursor:pointer}
  body.subview .composebtn svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
  body.subview .ibhead{font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:rgba(255,255,255,.55);margin:16px 2px 8px}
  body.subview .subrow .l small.last{display:block;font-weight:400;font-size:12.5px;color:rgba(255,255,255,.62);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  body.subview .subrow.unread .l small.last{color:#fff;font-weight:600}
  body.subview .subrow .r .when{font-size:11.5px;color:rgba(255,255,255,.55)}
  body.subview .ppls{width:100%;box-sizing:border-box;min-height:44px;border-radius:12px;padding:0 14px;font:inherit;font-size:15px;margin:0 0 10px}
  body.subview .ppl .subrow{margin-bottom:6px}
  body.subview .ppl .note{color:rgba(255,255,255,.7)}
  /* One thread screen for everyone (v257). The colors are variables: a
     homeowner reads it in the light Torc brand, a subcontractor in the navy
     of the sub view. Nothing else about the screen changes between them. */
  body.inthread{--th-bg:#fff;--th-fg:#0f2d4f;--th-dim:rgba(15,45,79,.68);--th-faint:rgba(15,45,79,.5);--th-them:#eef3f8;--th-field:#f3f6fa;--th-line:rgba(25,72,114,.22);--th-accent:#194872;--th-pop:#0f2d4f;--th-me:linear-gradient(135deg,#194872,#4688c3)}
  body.subview.inthread{--th-bg:#06172d;--th-fg:#fff;--th-dim:rgba(255,255,255,.65);--th-faint:rgba(255,255,255,.5);--th-them:rgba(255,255,255,.12);--th-field:rgba(255,255,255,.1);--th-line:rgba(255,255,255,.2);--th-accent:#8dc1f2;--th-pop:#0f2d4f;--th-me:linear-gradient(135deg,#194872,#4688c3)}
  body.inthread .bub.me,body.inthread .bub.me .who,body.inthread .bub.me .quote,body.inthread .bub.me .quote b{color:#fff}
  body.inthread .bub.me .quote{border-left-color:rgba(255,255,255,.7);background:rgba(255,255,255,.14)}
  body.inthread .subav{width:40px;height:40px;border-radius:50%;background:linear-gradient(135deg,#194872,#4688c3);display:inline-flex;align-items:center;justify-content:center;font-weight:700;font-size:14px;flex:none;color:#fff;overflow:hidden}
  body.inthread .subav img{width:100%;height:100%;object-fit:cover;display:block}
  body:not(.subview).inthread{background:#fff}
  body:not(.subview).inthread #app{background:#fff}
  body.inthread #s-subthread{background:var(--th-bg)}
  body.inthread #app.on .copyline{display:none}
  body.inthread .bub .reacts{color:#fff}
  body.inthread .tcompose textarea{color:var(--th-fg)}
  /* the thread screen: its own header, the bubbles, the bar at the bottom */
  body.inthread .apphead,body.inthread #adminbar,body.inthread .topnav{display:none !important}
  body.inthread #app{padding-bottom:0}
  body.inthread #s-subthread,body.inthread #s-subthread.active{padding:0 !important}
  body.inthread #s-subthread.active{display:flex;flex-direction:column}
  html:not(.native) body.inthread #s-subthread.active{min-height:100dvh}
  html.native body.inthread #s-subthread.active{padding-bottom:0}
  body.inthread .thr{display:flex;align-items:center;gap:10px;padding:calc(6px + env(safe-area-inset-top)) 10px 8px 4px;position:sticky;top:0;z-index:5;background:var(--th-bg);border-bottom:1px solid var(--th-field)}
  body.inthread .thr .tback{width:44px;height:44px;border:0;background:none;color:var(--th-accent);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:0;flex:none}
  body.inthread .thr .tback svg{width:26px;height:26px;stroke:currentColor;fill:none;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
  body.inthread .thr .subav{width:40px;height:40px;font-size:14px}
  body.inthread .thr .tnm{flex:1;min-width:0;line-height:1.2}
  body.inthread .thr .tnm b{display:block;font-size:17px;font-weight:600;color:var(--th-fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  body.inthread .thr .tnm small{display:block;font-size:12.5px;color:var(--th-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  body.inthread .bubbles{flex:1;display:flex;flex-direction:column;gap:8px;padding:12px 12px 14px}
  body.inthread .bubbles .note,body.inthread .bubbles .empty{color:var(--th-dim);text-align:center;margin:30px 0}
  body.inthread .daysep{align-self:center;font-size:11.5px;color:var(--th-faint);margin:6px 0 2px}
  body.inthread .bub{position:relative;max-width:82%;padding:9px 13px;border-radius:18px;font-size:15.5px;line-height:1.4;white-space:pre-wrap;overflow-wrap:anywhere;color:var(--th-fg);touch-action:manipulation;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;transition:transform .15s ease}
  body.inthread .bub.them{align-self:flex-start;background:var(--th-them);border-bottom-left-radius:6px}
  body.inthread .bub.me{align-self:flex-end;background:var(--th-me);border-bottom-right-radius:6px}
  body.inthread .bub .who{font-size:11.5px;font-weight:600;color:var(--th-accent);margin-bottom:3px}
  body.inthread .bub time{display:block;font-size:10.5px;opacity:.6;margin-top:3px;text-align:right}
  body.inthread .bub.focus{transform:scale(1.04);z-index:61}
  body.inthread #tapback-ghost{transform:scale(1.04);box-sizing:border-box}
  body.inthread .bub.hasr{margin-bottom:12px}
  body.inthread .bub .quote{border-left:3px solid var(--th-accent);background:var(--th-field);border-radius:8px;padding:5px 9px;margin:0 0 7px;font-size:13px;line-height:1.35;color:var(--th-dim);white-space:normal}
  body.inthread .bub .quote b{display:block;font-size:10.5px;letter-spacing:.04em;text-transform:uppercase;color:var(--th-accent);margin-bottom:1px}
  body.inthread .bub .reacts{position:absolute;bottom:-13px;display:flex;gap:2px;padding:1px 7px;font-size:14px;line-height:1.4;background:var(--th-pop);border:1px solid var(--th-line);border-radius:999px;box-shadow:0 2px 8px rgba(0,0,0,.4);white-space:nowrap}
  body.inthread .bub.them .reacts{right:-4px}
  body.inthread .bub.me .reacts{left:-4px}
  body.inthread .bub .reacts i{font-style:normal}
  body.inthread .bub .reacts i.mine{filter:drop-shadow(0 0 3px var(--th-accent))}
  body.inthread .receipt{align-self:flex-end;font-size:11px;color:var(--th-faint);margin:-2px 4px 0 0}
  body.inthread .bub .atts{display:flex;flex-direction:column;gap:6px;margin-bottom:6px}
  body.inthread .bub.piconly{padding:3px;background:transparent}
  body.inthread .bub.piconly time{padding:0 10px 4px}
  body.inthread .att.pic img{display:block;max-width:100%;max-height:320px;border-radius:14px;object-fit:cover;background:var(--th-field)}
  body.inthread .att.doc{display:flex;align-items:center;gap:10px;padding:10px 12px;border:1px solid var(--th-line);border-radius:12px;background:var(--th-field);color:var(--th-fg);text-decoration:none;max-width:100%}
  body.inthread .att.doc svg{width:24px;height:24px;stroke:var(--th-accent);fill:none;stroke-width:2;flex:none}
  body.inthread .att.doc span{min-width:0}
  body.inthread .att.doc b{display:block;font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  body.inthread .att.doc small{color:var(--th-dim);font-size:12px}
  #tapback-bg{position:fixed;inset:0;z-index:60;background:rgba(0,0,0,.3);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}
  #tapback{position:fixed;z-index:62;display:flex;flex-direction:column;animation:tbin .16s ease-out}
  #tapback.me{align-items:flex-end}
  #tapback.them{align-items:flex-start}
  @keyframes tbin{from{opacity:0;transform:scale(.85) translateY(6px)}to{opacity:1;transform:none}}
  .tbrow{display:flex;gap:2px;padding:5px 6px;background:#0f2d4f;border:1px solid rgba(255,255,255,.2);border-radius:999px;box-shadow:0 8px 30px rgba(0,0,0,.5)}
  .tbrow button{width:42px;height:42px;border:1px solid transparent;background:transparent;font-size:24px;line-height:1;border-radius:50%;cursor:pointer;padding:0;color:#fff;font-family:var(--font)}
  .tbrow button:hover,.tbrow button:active{background:rgba(255,255,255,.12)}
  .tbrow button.on{background:rgba(70,136,195,.45);border-color:#8dc1f2}
  .tbmenu{margin-top:8px;min-width:190px;background:#0f2d4f;border:1px solid rgba(255,255,255,.2);border-radius:14px;overflow:hidden;box-shadow:0 8px 30px rgba(0,0,0,.5)}
  .tbmenu button{display:flex;align-items:center;justify-content:space-between;gap:14px;width:100%;min-height:48px;padding:0 16px;border:0;border-top:1px solid rgba(255,255,255,.1);background:none;color:#fff;font:500 16px var(--font);cursor:pointer;text-align:left}
  .tbmenu button:first-child{border-top:0}
  .tbmenu button:hover,.tbmenu button:active{background:rgba(255,255,255,.1)}
  .tbmenu svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
  body.inthread .tcompose{position:sticky;bottom:0;z-index:6;display:flex;flex-direction:column;padding:8px 10px calc(8px + env(safe-area-inset-bottom));background:var(--th-bg);border-top:1px solid var(--th-field)}
  body.inthread .tcompose .crow{display:flex;gap:6px;align-items:flex-end}
  body.inthread .tcompose .ta{flex:1;position:relative;min-width:0}
  body.inthread .tcompose textarea{width:100%;box-sizing:border-box;min-height:44px;max-height:150px;resize:none;border:1px solid var(--th-line);border-radius:22px;padding:11px 14px;font:inherit;font-size:16px;line-height:1.3;background:var(--th-field);color:var(--th-fg);display:block;margin:0}
  body.inthread .tcompose textarea:focus{outline:none;border-color:var(--th-accent)}
  body.inthread .tcompose textarea::placeholder{color:var(--th-faint)}
  body.inthread .tcompose .btn.pri{flex:0 0 auto;min-height:44px;height:44px;width:auto;min-width:0;padding:0 16px;margin:0;border-radius:22px;font-size:14px}
  body.inthread .tcompose [hidden],body.inthread .replybar[hidden],body.inthread .pending[hidden],body.inthread .emojipanel[hidden]{display:none !important}
  body.inthread .ebtn{width:40px;height:44px;flex:none;border:0;background:transparent;font-size:24px;line-height:1;padding:0;cursor:pointer;border-radius:12px;filter:grayscale(1);opacity:.75;color:var(--th-fg);font-family:var(--font)}
  body.inthread .ebtn.on,body.inthread .ebtn:hover{filter:none;opacity:1;background:var(--th-them)}
  body.inthread .ebtn.clip{filter:none;opacity:.8;color:var(--th-accent)}
  body.inthread .ebtn.clip svg{width:24px;height:24px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
  body.inthread .replybar{display:flex;align-items:center;gap:8px;margin-bottom:8px;padding:6px 6px 6px 12px;border-left:3px solid var(--th-accent);background:var(--th-field);border-radius:10px}
  body.inthread .replybar .rq{flex:1;min-width:0;font-size:13px;line-height:1.35}
  body.inthread .replybar .rq b{display:block;font-size:12px;color:var(--th-accent)}
  body.inthread .replybar .rq span{display:block;color:var(--th-dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  body.inthread .replybar .x{width:36px;height:36px;flex:none;border:0;border-radius:50%;background:var(--th-them);color:var(--th-fg);font-size:20px;cursor:pointer}
  body.inthread .pending{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px}
  body.inthread .pend{position:relative;display:inline-flex;align-items:center;gap:8px;max-width:100%;padding:8px 36px 8px 10px;border:1px solid var(--th-line);border-radius:12px;background:var(--th-field);font-size:13px;color:var(--th-fg)}
  body.inthread .pend.pic{padding:0;width:72px;height:72px;overflow:hidden}
  body.inthread .pend img{width:72px;height:72px;object-fit:cover;display:block}
  body.inthread .pend svg{width:20px;height:20px;stroke:var(--th-accent);fill:none;stroke-width:2;flex:none}
  body.inthread .pend b{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  body.inthread .pend.up{opacity:.6}
  body.inthread .pend .x{position:absolute;top:4px;right:4px;width:26px;height:26px;border:0;border-radius:50%;background:rgba(0,0,0,.65);color:var(--th-fg);font-size:16px;line-height:1;cursor:pointer;padding:0}
  body.inthread .emojipanel{max-height:240px;overflow:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;background:var(--th-field);border:1px solid var(--th-line);border-radius:14px;padding:4px 8px 10px;margin-bottom:8px}
  body.inthread .emojipanel .eh{font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--th-faint);padding:8px 4px 4px}
  body.inthread .emojipanel .eh.react{color:var(--th-accent);text-transform:none;letter-spacing:0;font-size:13px}
  body.inthread .egrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(40px,1fr));gap:2px}
  body.inthread .egrid button{height:42px;border:0;background:transparent;font-size:26px;line-height:1;border-radius:8px;cursor:pointer;padding:0}
  body.inthread .egrid button:active,body.inthread .egrid button:hover{background:var(--th-them)}
  #lightbox{position:fixed;inset:0;z-index:80;background:rgba(0,0,0,.94);display:flex;align-items:center;justify-content:center;padding:env(safe-area-inset-top) 0 env(safe-area-inset-bottom)}
  #lightbox img{max-width:100%;max-height:100%;object-fit:contain}
  #lightbox .x{position:absolute;top:calc(12px + env(safe-area-inset-top));right:12px;width:44px;height:44px;border:0;border-radius:50%;background:rgba(255,255,255,.15);color:#fff;font-size:26px;cursor:pointer}
  @media (min-width:900px){ body.inthread #s-subthread{max-width:860px;margin-left:auto;margin-right:auto;width:100%} }
  body.subview .accphoto .nm{font-size:19px;font-weight:600;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
  body.subview .accphoto .co{font-size:13px;color:rgba(255,255,255,.72);margin-top:2px}
  body.subview .accphoto button{margin-top:6px;background:none;border:0;color:#8dc1f2;font-weight:600;font-size:12.5px;padding:0;cursor:pointer;font-family:var(--font)}
  body.subview .acctrow.edit{cursor:pointer}
  body.subview .acctrow.edit .v:after{content:' ›';color:rgba(255,255,255,.5)}
  #viewAsSel{margin-left:6px;max-width:220px;font:inherit;font-size:12px;padding:3px 6px;border-radius:8px;border:1px solid rgba(255,255,255,.3);background:rgba(255,255,255,.12);color:#fff}
  /* v204: the month's day bars by kind, the day window, less text */
  body.subview .dayev.k-inspection .dk{background:#f5c84b}
  body.subview .dayev.k-pour .dk{background:#f07fb8}
  body.subview .dayev.k-work .dk{background:#5aa9f0}
  body.subview .dayev.k-meeting .dk{background:#e8eef5}
  body.subview .subactive{margin-bottom:12px}
  /* The month like Google Calendar (v210): week rows of equal height,
     bars across the days, each in its event's color. Structure here, the
     two color themes below. */
  .scal .weeks{display:block}
  .scal .wk{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));grid-template-rows:22px repeat(3,19px) 13px;position:relative;border-top:1px solid var(--scal-line,rgba(255,255,255,.12))}
  .scal .wk:first-child{border-top:0}
  .scal .wk .dy{grid-row:1/-1;position:relative;background:none;border:0;border-left:1px solid var(--scal-line,rgba(255,255,255,.12));color:inherit;font-family:var(--font);padding:0;cursor:pointer;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:flex-start}
  .scal .wk .dy:first-child{border-left:0}
  .scal .wk .dy>span{display:inline-flex;align-items:center;justify-content:center;height:18px;min-width:18px;padding:0 4px;margin-top:2px;border-radius:9px;font-size:11.5px;font-weight:600}
  .scal .wk .dy em.pay{position:absolute;top:5px;right:4px;width:6px;height:6px;border-radius:50%}
  .scal .wk .bar{pointer-events:none;align-self:center;height:17px;line-height:17px;margin:0 2px 0 1px;padding:0 5px;border-radius:4px;font-size:10.5px;font-weight:600;font-style:normal;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;z-index:1}
  .scal .wk .bar.cl{border-top-left-radius:0;border-bottom-left-radius:0;margin-left:0}
  .scal .wk .bar.cr{border-top-right-radius:0;border-bottom-right-radius:0;margin-right:0}
  /* A phase's own color beside its name (v256: the demo colors each phase). */
  .prog .step .tx .n .phc{display:inline-block;width:10px;height:10px;border-radius:3px;margin-right:7px;vertical-align:0}
  .scal .wk .more{pointer-events:none;grid-row:5;font-size:9.5px;font-weight:600;line-height:13px;text-align:center;overflow:hidden;white-space:nowrap}
  body.subview .scal{--scal-line:rgba(255,255,255,.12);padding:12px 6px 6px}
  body.subview .scal .wk .dy{color:#fff}
  body.subview .scal .wk .dy.o{color:rgba(255,255,255,.3)}
  body.subview .scal .wk .dy.td>span{background:#fff;color:#0a2746;font-weight:700}
  body.subview .scal .wk .dy em.pay{background:#6fd69b}
  body.subview .scal .wk .more{color:rgba(255,255,255,.7)}
  .lite .scal{--scal-line:rgba(25,72,114,.1);padding:12px 6px 6px}
  .lite .scal .wk .dy{color:#0f2c47}
  .lite .scal .wk .dy.o{color:#b6c2cf}
  .lite .scal .wk .dy.td>span{background:var(--navy);color:#fff;font-weight:700}
  .lite .scal .wk .dy em.pay{background:var(--good)}
  .lite .scal .wk .more{color:var(--muted)}
  /* v207: words on the month. Each day shows up to two short lines in the
     color of what happens (yellow inspection, pink pour, blue work, white
     meeting) so nobody has to tap a day to know what it is. */
  /* The progress scale (v207): how far along the job is, a bar with one
     block per phase, then every phase on one track: completed, the one
     under way, and what comes next. Not a list of statuses. */
  body.subview .prog{background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.12);border-radius:var(--r-card);padding:14px 14px 6px;margin-bottom:12px}
  body.subview .prog .ph{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
  body.subview .prog .ph b{font-size:17px;font-weight:600}
  body.subview .prog .ph span{font-size:12.5px;color:rgba(255,255,255,.75);white-space:nowrap}
  body.subview .prog .bar{display:flex;gap:3px;margin:10px 0 4px;height:8px}
  body.subview .prog .bar i{flex:1;border-radius:4px;background:rgba(255,255,255,.18);position:relative;overflow:hidden}
  body.subview .prog .bar i.ok{background:#4fc08a}
  body.subview .prog .bar i.now{background:rgba(255,255,255,.18)}
  body.subview .prog .bar i.now:after{content:'';position:absolute;left:0;top:0;bottom:0;width:var(--pct,50%);background:#6fb1ee;border-radius:4px}
  body.subview .prog .bl{display:flex;justify-content:space-between;font-size:11px;color:rgba(255,255,255,.6);margin-bottom:10px}
  body.subview .prog .step{display:flex;align-items:flex-start;gap:12px;width:100%;text-align:left;background:none;border:0;padding:0;font-family:var(--font);color:#fff;cursor:pointer;position:relative}
  body.subview .prog .step .dot{flex:none;width:22px;display:flex;flex-direction:column;align-items:center;align-self:stretch}
  body.subview .prog .step .dot i{flex:none;width:14px;height:14px;border-radius:50%;border:2px solid rgba(255,255,255,.4);background:none;box-sizing:border-box;margin-top:3px;display:flex;align-items:center;justify-content:center;font-style:normal;font-size:9px;font-weight:800;color:#06172d}
  body.subview .prog .step .dot:after{content:'';flex:1;width:2px;background:rgba(255,255,255,.18);margin:3px 0 0}
  body.subview .prog .step:last-child .dot:after{display:none}
  body.subview .prog .step.ok .dot i{background:#4fc08a;border-color:#4fc08a}
  body.subview .prog .step.ok .dot:after{background:#4fc08a}
  body.subview .prog .step.now .dot i{width:18px;height:18px;margin-top:1px;border-color:#fff;background:#6fb1ee;box-shadow:0 0 0 4px rgba(111,177,238,.3)}
  body.subview .prog .step .tx{flex:1;min-width:0;padding-bottom:14px}
  body.subview .prog .step .tx .n{font-size:14.5px;font-weight:600;line-height:1.3;color:rgba(255,255,255,.85)}
  body.subview .prog .step.ok .tx .n{color:rgba(255,255,255,.7)}
  body.subview .prog .step.now .tx .n{font-size:16px;color:#fff}
  body.subview .prog .step .tx small{display:block;font-size:12.5px;color:rgba(255,255,255,.65);margin-top:2px}
  body.subview .prog .step .tag{font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#8dc1f2;margin-bottom:2px}
  body.subview .prog .step.ok .tag{color:#4fc08a}
  body.subview .prog .step.now .tag{color:#cfe6fb}
  /* The homeowner's Schedule (v208): the same month and scale in the light
     Portal colors. Same day colors, same shapes; paper and navy instead of
     the dark shell. Nothing here touches body.subview. */
  .lite .scal{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:12px 10px 10px;margin-bottom:12px;box-shadow:var(--shadow)}
  .lite .scal .mh{display:flex;justify-content:space-between;align-items:center;padding:0 2px 8px;font-size:14.5px;font-weight:600;color:var(--navy)}
  .lite .scal .mh button{background:none;border:0;color:var(--muted);font-size:22px;line-height:1;width:40px;height:36px;cursor:pointer;font-family:var(--font)}
  .lite .scal .dow,.lite .scal .days{display:grid;grid-template-columns:repeat(7,minmax(0,1fr))}
  .lite .scal .dow div{text-align:center;font-size:10.5px;color:var(--muted);font-weight:600;padding-bottom:6px}
  .lite .prog{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:14px 14px 6px;margin-bottom:12px;box-shadow:var(--shadow);color:#0f2c47}
  .lite .prog .ph{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
  .lite .prog .ph b{font-size:17px;font-weight:600;color:var(--navy)}
  .lite .prog .ph span{font-size:12.5px;color:var(--muted);white-space:nowrap}
  .lite .prog .bar{display:flex;gap:3px;margin:10px 0 4px;height:8px}
  .lite .prog .bar i{flex:1;border-radius:4px;background:#e3eaf1;position:relative;overflow:hidden}
  .lite .prog .bar i.ok{background:var(--good)}
  .lite .prog .bar i.now:after{content:'';position:absolute;left:0;top:0;bottom:0;width:var(--pct,50%);background:var(--blue);border-radius:4px}
  .lite .prog .bl{display:flex;justify-content:space-between;font-size:11px;color:var(--muted);margin-bottom:10px}
  .lite .prog .step{display:flex;align-items:flex-start;gap:12px;width:100%;text-align:left;background:none;border:0;padding:0;font-family:var(--font);color:#0f2c47;cursor:pointer;position:relative}
  .lite .prog .step .dot{flex:none;width:22px;display:flex;flex-direction:column;align-items:center;align-self:stretch}
  .lite .prog .step .dot i{flex:none;width:14px;height:14px;border-radius:50%;border:2px solid #c5d0db;background:none;box-sizing:border-box;margin-top:3px;display:flex;align-items:center;justify-content:center;font-style:normal;font-size:9px;font-weight:800;color:#fff}
  .lite .prog .step .dot:after{content:'';flex:1;width:2px;background:#e3eaf1;margin:3px 0 0}
  .lite .prog .step:last-child .dot:after{display:none}
  .lite .prog .step.ok .dot i{background:var(--good);border-color:var(--good)}
  .lite .prog .step.ok .dot:after{background:var(--good)}
  .lite .prog .step.now .dot i{width:18px;height:18px;margin-top:1px;border-color:#fff;background:var(--blue);box-shadow:0 0 0 4px rgba(70,136,195,.25)}
  .lite .prog .step .tx{flex:1;min-width:0;padding-bottom:14px}
  .lite .prog .step .tx .n{font-size:14.5px;font-weight:600;line-height:1.3;color:#0f2c47}
  .lite .prog .step.ok .tx .n{color:var(--muted)}
  .lite .prog .step.now .tx .n{font-size:16px;color:var(--navy)}
  .lite .prog .step .tx small{display:block;font-size:12.5px;color:var(--muted);margin-top:2px}
  .lite .prog .step .tag{font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#2f6ba3;margin-bottom:2px}
  .lite .prog .step.ok .tag{color:var(--good)}
  .lite .prog .step.now .tag{color:var(--blue)}
  /* The day window on the light view (v208): the same rows as the dark one. */
  body:not(.subview) .dayev{padding:12px 0;border-bottom:1px solid var(--line)}
  body:not(.subview) .dayev:last-child{border-bottom:0}
  body:not(.subview) .dayev .dk{display:inline-block;font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#0a2746;padding:2px 9px;border-radius:999px;background:#5aa9f0}
  body:not(.subview) .dayev.k-inspection .dk{background:#f5c84b}
  body:not(.subview) .dayev.k-pour .dk{background:#f07fb8}
  body:not(.subview) .dayev.k-meeting .dk{background:#dfe7ef}
  body:not(.subview) .dayev.k-phase .dk{background:#e7f0f9;color:var(--navy)}
  body:not(.subview) .dayev.k-pay .dk{background:var(--goodbg);color:var(--good)}
  body:not(.subview) .dayev .dt{font-size:16px;font-weight:600;color:var(--navy);margin-top:6px}
  body:not(.subview) .dayev .dm{font-size:13px;color:var(--muted);margin-top:2px}
  body:not(.subview) .dayev .dn{font-size:13.5px;color:#0f2c47;margin-top:6px;white-space:pre-wrap}
  body.subview .selbtn{color:#cfe6fb;background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.25)}
  body.subview .subbull{display:block;width:100%;text-align:left;font-family:var(--font);color:#fff;cursor:pointer;margin:6px 0 30px;padding:14px;border-radius:var(--r-card);border:1px solid rgba(141,193,242,.35);background:linear-gradient(135deg,rgba(70,136,195,.45),rgba(25,72,114,.6))}
  body.subview .subbull .bh{display:flex;justify-content:space-between;align-items:center;font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#cfe6fb;margin-bottom:6px}
  body.subview .subbull .bh .chev{font-size:20px;color:#cfe6fb;letter-spacing:0}
  body.subview .subbull .bp{padding:6px 0}
  body.subview .subbull .bp+.bp{border-top:1px solid rgba(255,255,255,.15)}
  body.subview .subbull .bt{font-size:15px;font-weight:600;line-height:1.4;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
  body.subview .subbull .bm{font-size:12.5px;color:rgba(255,255,255,.8);margin-top:3px}
  /* The footer (v205): one solid dark navy to the very bottom of the phone.
     The page root painted the old navy-and-white gradient under the
     see-through tab bar and in the strip above the header.
     v212: that navy is #06172d, the same color as the header and the body.
     The footer keeps it. theme-color in the page head is the lighter brand
     navy (#194872); iOS paints that in any gap the shell does not cover,
     which is the light blue line at the home indicator and, on a scroll,
     at the status bar. These bars are the shell color, and the tab bar
     has no border and no shadow that could draw another line. */
  html:has(body.subview),html:has(body.subview):has(#app.on),html:has(body.subview):has(#picker.on){background:#06172d !important;background-color:#06172d !important}
  html:has(body.subview)::before,html:has(body.subview):has(#app.on)::before{background:#06172d !important;background-color:#06172d !important}
  /* Inside the viewport, over the home-indicator inset. Fixed on the root so
     the job list (no tab bar) and a project (tab bar) both cover the strip
     iOS would otherwise fill with theme-color. */
  html:has(body.subview)::after{content:"";position:fixed;left:0;right:0;bottom:0;z-index:5;height:env(safe-area-inset-bottom);background:#06172d;pointer-events:none}
  /* Homeowner and admin-as-client: white through the home indicator, and a
     white shadow past the edge, so a gap under the web view cannot show the
     header navy. The status bar is the top block, not this color. */
  html:has(#app.on):not(:has(body.subview))::after{content:"";position:fixed;left:0;right:0;bottom:0;z-index:4;height:env(safe-area-inset-bottom);background:#fff;box-shadow:0 48px 0 48px #fff;pointer-events:none}
  body.subview #nav{background:#06172d !important;background-color:#06172d !important;border-top:0 !important;-webkit-backdrop-filter:none !important;backdrop-filter:none !important;padding-bottom:max(env(safe-area-inset-bottom),6px);box-shadow:0 max(env(safe-area-inset-bottom),40px) 0 0 #06172d !important}
  body.subview #nav::after{background:#06172d !important;background-color:#06172d !important}
  body.subview .copyline{display:none}
  body.subview .dayev{padding:12px 0;border-bottom:1px solid rgba(255,255,255,.1)}
  body.subview .dayev:last-child{border-bottom:0}
  body.subview .dayev .dk{display:inline-block;font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#0a2746;padding:2px 9px;border-radius:999px;background:#5aa9f0}
  body.subview .dayev.k-phase .dk{background:rgba(255,255,255,.18);color:#fff}
  body.subview .dayev.k-pay .dk{background:#6fd69b}
  body.subview .dayev .dt{font-size:16px;font-weight:600;color:#fff;margin-top:6px}
  body.subview .dayev .dm{font-size:13px;color:rgba(255,255,255,.75);margin-top:2px}
  body.subview .dayev .dn{font-size:13.5px;color:rgba(255,255,255,.85);margin-top:6px;white-space:pre-wrap}
  body.subview .dayev .mjcal{display:inline-block;margin-top:6px}
  body.subview #s-account > p.sub,body.subview #s-account .card > p.note,body.subview #contactsList > p.sub{display:none}
  /* Contacts on the dark view (v204): names and numbers in white */
  body.subview a.cocard .con,body.subview .pline .num,body.subview .cxmain .num,body.subview .cform .cft{color:#fff}
  body.subview a.cocard .cot,body.subview a.cocard .com,body.subview .cxmain .lbl{color:rgba(255,255,255,.75)}
  body.subview a.cocard .cocount{background:rgba(141,193,242,.2);color:#cfe6fb}
  body.subview a.cocard .cocount.none{background:rgba(255,255,255,.1);color:rgba(255,255,255,.6)}
  body.subview a.cocard:active{background:rgba(255,255,255,.14)}
  body.subview .pline,body.subview .pfoot,body.subview a.psave,body.subview .pfoot .prm{border-color:rgba(255,255,255,.1)}
  body.subview .pline svg{color:#8dc1f2}
  body.subview .pact a{background:rgba(255,255,255,.14);color:#fff}
  body.subview a.psave,body.subview .pfoot .prm{background:rgba(255,255,255,.06);color:#8dc1f2}
  body.subview .csearch{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.2);color:#fff}
  body.subview .csearch::placeholder{color:rgba(255,255,255,.5)}
  body.subview .cadd{color:#8dc1f2;border-color:rgba(141,193,242,.5)}
  /* Fixtures and finishes on the dark view (v204): headings you can read */
  body.subview .fxarea{color:#fff;font-size:18px;letter-spacing:.03em;margin-top:26px}
  body.subview .fxarea b{color:rgba(255,255,255,.6)}
  body.subview .fxarea::after{background:rgba(255,255,255,.35);height:2px}
  body.subview .fxcat{color:#fff;font-size:13px}
  body.subview .fxlist{background:rgba(255,255,255,.07)}
  body.subview .fxlist .fxcard:active{background:rgba(255,255,255,.1)}
  /* The product window (v204): dead center on the phone, inside the notch
     and the home bar; the photo whole (all four sides) and first; the
     details under it. */

  @media (min-width:900px){ body.subview #s-subhome,body.subview #s-subsched,body.subview #s-submsgs{max-width:760px;margin-left:auto;margin-right:auto} }
  /* The document window on the dark view (v221): the same navy as the
     overview behind it, never a white page in a dark room. Folder rows,
     document tiles, a sheet's cards and tabs all go navy with the light
     blue accents; a PDF page itself stays white, because it is paper. */
  body.subview .vframe{background:#06172d;border-color:#4688c3}
  body.subview .vbody,body.subview .vsheet{background:#06172d}
  body.subview .vbody.photo{background:#06172d}
  body.subview .vloading,body.subview .verr,body.subview .fxwait{color:rgba(255,255,255,.72)}
  body.subview .vrow{background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.12);box-shadow:none}
  body.subview .vrow:hover{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.2)}
  body.subview .vrow:active{background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.2)}
  body.subview .vrow .vn{color:#fff}
  body.subview .vrow .vsubn{color:rgba(255,255,255,.72)}
  body.subview .vrow .vico,body.subview .vrow .vc{color:#8dc1f2}
  body.subview .vrow .vshare{background:rgba(255,255,255,.1);border-color:rgba(141,193,242,.4);color:#8dc1f2}
  body.subview .pcell{border-color:rgba(255,255,255,.14)}
  body.subview .pcell.doc{background:rgba(255,255,255,.07)}
  body.subview .pcell.doc .pthumb{background:rgba(255,255,255,.06)}
  body.subview .pcell.doc .pcap{background:rgba(70,136,195,.38);color:#fff}
  body.subview .vsrc{background:rgba(70,136,195,.38);border-color:rgba(141,193,242,.3);box-shadow:none}
  body.subview .vtabs button{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.18);color:#fff}
  body.subview .vtabs button.on{background:#4688c3;border-color:#4688c3}
  body.subview .fxsearch input{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.2);color:#fff}
  body.subview .fxsearch input::placeholder{color:rgba(255,255,255,.5)}
  body.subview .fxsearch>svg,body.subview .fxsearch .fxclear{color:rgba(255,255,255,.6)}
  body.subview .scard{background:rgba(255,255,255,.07);border-color:rgba(255,255,255,.12);box-shadow:none}
  body.subview .scard .st,body.subview .scard .sv,body.subview .ssum b,body.subview .sgrp1{color:#fff}
  body.subview .scard .sk,body.subview .ssum{color:rgba(255,255,255,.72)}
  body.subview .scard .sr{border-top-color:rgba(255,255,255,.1)}
  body.subview .sgrp2{color:#8dc1f2;border-left-color:#8dc1f2}
  body.subview .fxt td{color:#fff;border-top-color:rgba(255,255,255,.1)}
  body.subview .fxt td.it small{color:rgba(255,255,255,.72)}
  body.subview .fxth{color:#8dc1f2}
  /* The fixtures list painted inside the window (v221): the same rows as
     the Fixtures screen, with the screen's side padding. */
  .vfx{padding:4px 16px 24px}
  .vfx>.fxarea:first-child{margin-top:12px}

  #app.on{display:block}
  /* Mobile app shell: header and bottom nav are locked; ONLY the screen content
     scrolls (its own scroller, bounce contained) — no more blue peeking past
     the header/footer while scrolling a project dashboard. */
  @media (max-width: 767px){
    /* While the shell is up, the document itself can never scroll or bounce —
       gestures anywhere outside the screen content (header, nav, short pages)
       must not drag the app. Belt (overflow/height), suspenders (touch-action),
       and a JS touchmove guard below for iOS rubber-banding. */
    body:has(#app.on){overflow:hidden;height:100dvh;overscroll-behavior:none}
    html.ready #app.on{display:flex;flex-direction:column;position:fixed;inset:0;overflow:hidden;padding-bottom:0;background:var(--paper);touch-action:none}
    #app.on .apphead{position:relative;top:auto;flex:none}
    /* Move the row down. The mark stays 22px (20px once the header compacts). */
    #app.on .apphead,#app.on .apphead.compact{padding-top:calc(12px + env(safe-area-inset-top))}
    #app.on #adminbar{flex:none}
    /* The header is only the logo row and the admin strip. The tools are the
       locked bar at the bottom, so the screen keeps room under the last card. */
    #app.on .screen.active{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;background:var(--paper);padding-bottom:110px;touch-action:pan-y}
    /* The copyright follows the page. It is not a pinned bar, so it does not
       shrink the list into a window. The home-indicator inset is the padding
       under that line. The tab bar, when it is on, is the footer instead. */
    #app.on .copyline{flex:none;margin-top:0;background:var(--paper);padding-bottom:calc(8px + env(safe-area-inset-bottom))}
    #app.on:has(#nav[style*="display: block"]) .copyline,
    #app.on:has(#nav[style*="display:block"]) .copyline{display:none}
    /* An overview has no tab bar. The shell is the one scroller, and the
       copyright sits after the files. A subcontractor thread keeps the screen
       as its scroller (body.subview), and so does a homeowner's (body.inthread, v257). */
    body:not(.subview):not(.inthread) #app.on:not(:has(#nav[style*="display: block"])):not(:has(#nav[style*="display:block"])){overflow-x:hidden;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;touch-action:pan-y}
    body:not(.subview):not(.inthread) #app.on:not(:has(#nav[style*="display: block"])):not(:has(#nav[style*="display:block"])) .screen.active{flex:none;min-height:0;overflow:visible;padding-bottom:16px;touch-action:auto}
  }
  /* One row: the logo, the job's address beside it, the button on the right
     (Thomas, 2026-10-02: "The header needs to be condensed"). The address used
     to be a second line under the logo. */
  .apphead{background:var(--navy);color:#fff;padding:4px 14px 4px;padding-top:calc(4px + env(safe-area-inset-top));position:sticky;top:0;z-index:20;box-shadow:0 2px 12px rgba(15,44,71,.25);transition:padding .15s ease}
  .apphead .row{display:flex;justify-content:space-between;align-items:center;gap:12px}
  .apphead img{height:22px;width:auto;max-width:44%;object-fit:contain;object-position:left center;flex:none;transition:height .15s ease}
  .apphead .proj{flex:1;min-width:0;font-size:12px;font-weight:600;color:rgba(255,255,255,.92);text-align:left;letter-spacing:.02em;line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-left:12px;border-left:1px solid rgba(255,255,255,.35)}
  .apphead .proj:empty{visibility:hidden}
  .apphead .out{background:rgba(255,255,255,.92);border:none;color:var(--navy);font-size:11px;letter-spacing:.04em;font-weight:600;padding:6px 12px;border-radius:var(--r-btn);cursor:pointer;font-family:var(--font);flex:none}
  .apphead.compact{padding:4px 14px;padding-top:calc(4px + env(safe-area-inset-top))}
  .apphead.compact img{height:20px}
  /* One header. The shadow sits under whichever band is last, so the admin
     strip does not cast a seam across the logo row. */
  #app:has(#adminbar[style*="display: flex"]) .apphead,
  #app:has(#adminbar[style*="display:flex"]) .apphead{box-shadow:none}

  #adminbar{display:none;background:var(--navy);color:rgba(255,255,255,.92);font-size:12px;padding:4px 14px;margin-top:-1px;align-items:center;gap:6px 8px;flex-wrap:wrap;box-shadow:0 2px 12px rgba(15,44,71,.25)}
  #adminbar::-webkit-scrollbar{display:none}
  #adminbar > *{flex:none;white-space:nowrap}
  #adminbar select{border-radius:8px;border:none;height:28px;padding:0 8px;max-width:42vw;font-family:var(--font);font-size:12px;color:var(--navy)}
  #adminbar .ro{display:inline-flex;align-items:center;height:22px;background:rgba(255,255,255,.12);border-radius:999px;padding:0 10px;font-size:10.5px;letter-spacing:.06em;text-transform:uppercase}
  #adminbar .abk{display:inline-flex;align-items:center;height:28px;background:rgba(255,255,255,.14);color:#eaf2fb;border:1px solid rgba(255,255,255,.25);border-radius:999px;padding:0 10px;font-size:12px;font-weight:600;font-family:var(--font);cursor:pointer;letter-spacing:.02em}
  #adminbar .abk:hover{background:rgba(255,255,255,.22)}
  #adminbar .abk:active{transform:translateY(1px)}
  /* A subcontractor on several jobs gets this bar with the back button alone. */
  #adminbar.subbar b,#adminbar.subbar #adminbarCtx,#adminbar.subbar .ro,#adminbar.subbar select{display:none!important}

  .screen{display:none;padding:18px 16px 20px}
  .screen.active{display:block}
  h2{font-weight:600;font-size:24px;color:var(--navy);margin-bottom:3px;letter-spacing:-0.02em}
  .sub{color:var(--muted);font-size:13.5px;margin-bottom:6px}
  .meta{color:var(--muted);font-size:12.5px;margin-bottom:16px}
  /* Home only: a tighter stack. Progress leads, the four figures share one
     grid with the labels and the amounts on the same lines, and the notes
     sit at the bottom of equal-height cards. Nothing on Home is removed. */
  #s-home .sub{margin-bottom:2px}
  #s-home .meta{margin-bottom:10px}
  #s-home .progcard{margin-bottom:10px;padding:12px 14px;gap:12px}
  #s-home .grid2{gap:8px;margin-bottom:10px;align-items:stretch}
  #s-home .grid2 .card{display:flex;flex-direction:column;padding:12px}
  #s-home .grid2 .label{letter-spacing:.06em}
  #s-home .grid2 .value{margin-top:2px;line-height:1.15}
  #s-home .grid2 .note{margin-top:auto;padding-top:4px}
  #s-home > .card,#homeSide > .card,#s-home .alert,#s-home a.doc{margin-bottom:10px}
  @media(max-width:767px){
    #s-home.screen{padding:12px 14px 8px}
    #s-home h2{font-size:22px;line-height:1.15;margin-bottom:2px}
  }

  .card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:16px;margin-bottom:12px;box-shadow:var(--shadow)}
  .label{font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:600}
  .value{font-size:23px;margin-top:4px;font-weight:700;color:var(--navy);letter-spacing:-0.01em;overflow-wrap:anywhere}
  .note{font-size:12.5px;color:var(--muted);margin-top:4px;overflow-wrap:anywhere}
  .grid2{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:10px;margin-bottom:12px}
  .grid2 .card{margin-bottom:0;min-width:0}
  .grid2 .value{font-size:18px}
  .kpicard{cursor:pointer;-webkit-user-select:none;user-select:none;min-height:44px}
  .kpicard:active{background:#f6f9fc}
  @media(hover:hover){.kpicard:hover{border-color:rgba(70,136,195,.38)}}
  @media(max-width:389px){.grid2{grid-template-columns:minmax(0,1fr)}}
  .screen{overflow-x:hidden}

  /* action-needed accent: amber, reserved exclusively for "you need to act" */
  .alert{display:none;background:#fff;border:1px solid var(--line);border-left:4px solid var(--warn);border-radius:var(--r-card);padding:16px;margin-bottom:12px;cursor:pointer;color:#22374d;box-shadow:var(--shadow)}
  .alert .t{font-weight:700;font-size:15px;color:var(--navy)}
  .alert .d{font-size:13px;color:var(--muted);margin-top:3px}
  .alert .go{display:inline-block;font-size:11.5px;background:var(--navy);color:#fff;font-weight:600;margin-top:12px;letter-spacing:.06em;text-transform:uppercase;padding:9px 16px;border-radius:var(--r-btn);box-shadow:var(--btnshadow)}
  .alert.ok{border-left-color:#2e9e5b;cursor:default}
  .alert.ok .t{color:#1f7a45}
  .progcard{background:#fff;border:1px solid var(--line);border-radius:var(--r-card);padding:14px 16px;margin-bottom:12px;box-shadow:var(--shadow);display:flex;align-items:center;gap:16px}
  .progring{position:relative;width:68px;height:68px;flex:none}
  .progring svg{width:68px;height:68px;transform:rotate(-90deg)}
  .progring .ptrack{fill:none;stroke:#e8f0f8;stroke-width:9}
  .progring .parc{fill:none;stroke:#1c6fb0;stroke-width:9;stroke-linecap:round;transition:stroke-dasharray .6s ease}
  .progring .ppct{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:15px;font-weight:700;color:var(--navy)}
  .progtxt .protop{font-size:12.5px;font-weight:600;color:var(--muted);letter-spacing:.04em;text-transform:uppercase}
  .progtxt .prosub{font-size:16px;font-weight:700;color:var(--navy);margin-top:3px}
  .progtxt .pronote{font-size:12.5px;color:var(--muted);margin-top:2px}

  .finmeta{font-size:11.5px;color:var(--muted);margin-top:6px}
  .pill{display:inline-block;font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;padding:4px 11px;border-radius:999px;font-weight:700;white-space:nowrap}
  .pill.done{background:var(--goodbg);color:var(--good)}
  .pill.now{background:#fff;color:var(--navy)}
  .pill.due{background:var(--warnbg);color:var(--warn)}
  .pill.wait{background:#e8edf2;color:var(--muted)}
  .pill.up{background:#e7f0f9;color:#2f6ba3}

  /* schedule */
  .nowcard{background:var(--navy);color:#fff;border-radius:var(--r-card);padding:16px;margin-bottom:14px;box-shadow:var(--shadow)}
  .nowcard .dates{font-size:12.5px;color:rgba(255,255,255,.75);font-weight:500}
  .nowcard .desc{font-size:16px;font-weight:600;margin-top:2px}
  .nowcard .why{font-size:13px;color:rgba(255,255,255,.85);margin-top:8px}
  .nowcard .pill{margin-left:8px;vertical-align:middle}
  .phase{display:flex;gap:12px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:11px 14px;margin-bottom:8px;align-items:center;box-shadow:var(--shadow)}
  .phase .dates{min-width:96px;font-size:12px;color:var(--muted);font-weight:500}
  .phase .desc{flex:1;font-size:13.5px}
  .phase.donerow{box-shadow:none;background:transparent;border-color:transparent;padding:7px 14px}
  .phase.donerow .desc,.phase.donerow .dates{color:#93a3b3;font-size:12.5px}
  .phase .ck{flex:none;width:18px;height:18px;color:var(--good)}
  /* v196: every timeline row is a button that opens the phase's scope. */
  button.phase,button.nowcard{width:100%;text-align:left;font:inherit;color:inherit;cursor:pointer;-webkit-appearance:none;appearance:none}
  button.nowcard{display:block;border:0;color:#fff}
  .phase .main{flex:1;min-width:0}
  .phase .main .dates{min-width:0;margin-top:2px}
  .phase .desc{font-weight:600;color:var(--navy);font-size:14.5px}
  .phase.donerow .desc{font-weight:500}
  .phase .ext,.nowcard .more .ext{flex:none;width:16px;height:16px;color:#9fb0c1}
  .nowcard .more{display:flex;align-items:center;gap:4px;font-size:12.5px;color:rgba(255,255,255,.85);margin-top:10px}
  .nowcard .more .ext{color:rgba(255,255,255,.7)}
  .phase:active{background:#f3f6f9}
  .toggledone{display:block;width:100%;background:none;border:none;color:var(--navy);font-family:var(--font);font-size:13px;font-weight:600;padding:10px;cursor:pointer;text-align:center}

  /* Three fixed columns, not space-between: the amount column and the status
     column start at the same x on every row, so the numbers read as one
     right-aligned stack no matter how long the description runs.
     v236: the row is a plain box the full width of the list (the same width
     as the Material Allowances card under it) and the columns live on an
     inner .pin wrapper. A button used to carry the grid itself, and WebKit
     sized the button to its text (a short "Deposit" row came out half as
     wide as a long one) and did not lay its children out as grid columns. */
  .payrow{display:block;width:100%;max-width:100%;min-width:0;box-sizing:border-box;margin:0 0 8px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:12px 14px;box-shadow:var(--shadow)}
  .payrow .pin{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;column-gap:12px;width:100%}
  .payrow .pin>div:first-child{min-width:0}
  .payrow .t{font-size:13.5px;font-weight:600;color:#22374d;line-height:1.35;overflow-wrap:anywhere;text-align:left}
  .payrow .d{font-size:12px;color:var(--muted);margin-top:3px;text-align:left}
  .payrow .pd{font-size:12px;color:#1f7a4d;margin-top:3px;line-height:1.4;overflow-wrap:anywhere;text-align:left}
  .apr .amt{font-size:14.5px;font-weight:700;white-space:nowrap;font-variant-numeric:tabular-nums;color:var(--navy)}
  .cototal{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:4px 16px 0;margin:-2px 0 12px;font-size:14px;font-weight:700;color:var(--navy)}
  .cototal span:last-child{font-variant-numeric:tabular-nums;white-space:nowrap}
  .payrow .amt{font-size:14.5px;font-weight:700;white-space:nowrap;font-variant-numeric:tabular-nums;color:var(--navy);text-align:right}
  /* Sized to its own text, not to a guessed width, so "NOT DUE" is never
     clipped; min-width keeps the short labels ("DUE", "PAID") in one column. */
  .payrow .pill{justify-self:end;padding:4px 10px;min-width:54px;text-align:center}
  /* v164: every row opens its own window, so it is a button with a chevron.
     v236: the class is "go", not "tap": since v225 the shell paints any
     .tap element pressed (darker, nudged down) and strips the class on
     pointerup, so the rows sat grey and lost their columns after one tap. */
  .payrow.go{text-align:left;font-family:var(--font);cursor:pointer;-webkit-tap-highlight-color:transparent;-webkit-appearance:none;appearance:none;color:inherit;font-size:inherit}
  .payrow.go .pin{grid-template-columns:minmax(0,1fr) auto auto auto}
  .payrow.go .ext{color:#b8c5d3;width:18px;height:18px;flex:none}
  .payrow.go:hover{border-color:var(--blue)}
  .payrow.go:active{transform:translateY(1px);background:#f7fafd}
  #payList{display:flex;flex-direction:column;align-items:stretch}
  /* One job sold in phases: three figures on one surface, then the rows. */
  .phasefigs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));margin:0 0 12px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card)}
  .phasefigs div{padding:10px 12px;min-width:0}
  .phasefigs div+div{border-left:1px solid var(--line)}
  .phasefigs span{display:block;font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}
  .phasefigs b{display:block;margin-top:3px;font-size:14px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--navy);letter-spacing:-0.02em;overflow-wrap:anywhere}
  @media (max-width:380px){
    .phasefigs{grid-template-columns:1fr}
    .phasefigs div+div{border-left:0;border-top:1px solid var(--line)}
  }
  .payatt{display:flex;gap:12px;align-items:center;width:100%;text-align:left;background:#f7fafd;border:1px solid #e3eaf1;border-radius:12px;padding:10px 12px;margin-top:8px;font-family:var(--font);cursor:pointer;-webkit-tap-highlight-color:transparent}
  .payatt:active{transform:translateY(1px)}
  .payatt img{width:58px;height:74px;object-fit:cover;object-position:top;border-radius:6px;border:1px solid #dfe6ee;background:#fff;flex:none}
  .payatt .t{font-weight:700;color:#194872;font-size:14px}
  .payatt .d{font-size:12px;color:#6b7c8e;margin-top:2px}
  .payatt .o{display:inline-flex;align-items:center;gap:4px;font-size:12px;color:var(--blue);font-weight:700;margin-top:6px}
  .payatt .o .ext{width:14px;height:14px;color:var(--blue)}
  /* Narrow screens keep the same three columns, just tighter. Stacking the
     status pill onto a second row left a dead gap and made rows uneven. */
  @media(max-width:430px){
    .payrow{padding:11px 12px}
    .payrow .pin{column-gap:8px}
    .payrow .t{font-size:13px}
    .payrow .amt{font-size:13.5px}
    .payrow .pill{min-width:auto;font-size:9px;padding:3px 8px;letter-spacing:.04em}
  }
  .paybtn{display:block;width:100%;text-align:center;background:var(--grad);color:#fff;border:none;border-radius:var(--r-btn);padding:15px;font-size:13.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;text-decoration:none;cursor:pointer;margin:4px 0 10px;box-shadow:var(--btnshadow)}
  .paybtn:active{transform:translateY(1px)}
  .qbnote{font-size:11.5px;color:var(--muted);text-align:center;margin:0 0 16px}

  /* documents: whole row clickable */
  a.doc{display:flex;justify-content:space-between;gap:12px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:13px 14px;margin-bottom:8px;align-items:center;box-shadow:var(--shadow);text-decoration:none;color:inherit}
  a.doc:hover{background:rgba(70,136,195,.16);border-color:var(--blue)}
  a.doc:active{background:rgba(70,136,195,.28);border-color:var(--blue)}
  a.doc .t{font-size:14px;font-weight:600;color:#22374d}
  a.doc .right{display:flex;align-items:center;gap:8px;flex:none}
  /* Documents tab: real links get a solid blue fill and a trailing arrow so
     they read as tappable buttons, not inert cards. A file that has no link
     yet (rare: div.doc, not a.doc) stays a plain muted row on purpose, so
     "tap this" and "nothing to open yet" stay visually distinct. */
  #docsList a.doc{display:flex;justify-content:center;align-items:center;gap:8px;text-align:center;background:var(--blue);border-color:var(--blue)}
  #docsList a.doc:hover{background:#3a76ab;border-color:#3a76ab}
  #docsList a.doc:active{background:#2f6293;border-color:#2f6293}
  #docsList a.doc .t{display:inline-flex;align-items:center;gap:8px;color:#fff}
  #docsList a.doc .t .ext{width:15px;height:15px;color:#fff}
  #docsList div.doc{display:flex;justify-content:center;align-items:center;text-align:center;background:#f2f5f8;border-color:var(--line)}
  #docsList div.doc .t{color:var(--muted)}
  /* Per-document share button (email/text the Drive link) */
  a.doc{position:relative}
  .dsh{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;padding:9px;color:#fff;cursor:pointer;line-height:0;border-radius:10px}
  .dsh:active{background:rgba(255,255,255,.22)}
  /* Every title centers on the same axis (Thomas, 2026-09-24): the share
     button is absolute at the right, so every row, with or without one,
     keeps the same gutter on both sides. */
  #docsList a.doc{padding-left:44px;padding-right:44px}
  .shopt{display:flex;align-items:center;gap:12px;width:100%;background:#fff;border:1px solid var(--line);border-radius:var(--r-card);padding:14px;margin-bottom:10px;font-family:var(--font);font-size:14.5px;font-weight:600;color:var(--navy);cursor:pointer;text-decoration:none;box-shadow:var(--shadow)}
  /* Standing job-site facts on an overview (a gate code, a lockbox). Same
     blue button as every file row on this screen, on purpose: Thomas wants it
     read as part of the same set. It opens nothing, so it is a div, carries no
     hover/active states and no arrow, and is not in the tab order. */
  .docnote{display:flex;justify-content:center;align-items:center;gap:8px;text-align:center;
    background:var(--blue);border:1px solid var(--blue);border-radius:var(--r-card);
    padding:13px 14px;margin-bottom:8px;box-shadow:var(--shadow);
    font-size:14px;font-weight:600;color:#fff;cursor:default;-webkit-user-select:text;user-select:text}
  /* The Contacts folder. On an overview it is the first row of the file
     list and is drawn exactly like the folders under it - the same blue
     button - because to the sub it IS a folder: Contacts, then a company,
     then its people. */
  #docsContacts a.doc{display:flex;justify-content:center;align-items:center;gap:8px;text-align:center;background:var(--blue);border-color:var(--blue)}
  #docsContacts a.doc:hover{background:#3a76ab;border-color:#3a76ab}
  #docsContacts a.doc:active{background:#2f6293;border-color:#2f6293}
  #docsContacts a.doc .t{display:inline-flex;align-items:center;gap:8px;color:#fff}
  #docsContacts a.doc .t .ext{width:15px;height:15px;color:#fff}
  .cbackrow{display:flex;align-items:center;gap:8px;margin-bottom:10px}
  .cback{display:inline-flex;align-items:center;gap:6px;background:none;border:0;padding:6px 2px;font-family:var(--font);
    font-size:14px;font-weight:600;color:var(--blue);cursor:pointer}
  /* The crumb under the title: Ivy St / Contacts / LCL Construction. */
  .ccrumb{display:flex;flex-wrap:wrap;align-items:center;gap:6px;font-size:12px;color:var(--muted);margin:-2px 0 14px}
  .ccrumb b{color:var(--navy);font-weight:600}
  .ccrumb i{font-style:normal;opacity:.6}
  .csearch{display:block;width:100%;box-sizing:border-box;font-family:var(--font);font-size:14px;color:var(--navy);
    background:#fff;border:1px solid rgba(25,72,114,.18);border-radius:12px;padding:11px 14px;margin:0 0 12px;outline:none}
  .csearch:focus{border-color:var(--blue);box-shadow:0 0 0 3px rgba(70,136,195,.15)}
  /* One company per card on the first level. The name is the thing you are
     choosing between, so it is the largest text; the trade and the headcount
     say what you will find inside before you tap. */
  a.cocard{display:flex;justify-content:space-between;align-items:center;gap:12px;background:var(--card);border:1px solid var(--line);
    border-radius:var(--r-card);padding:14px 15px;margin-bottom:8px;box-shadow:var(--shadow);text-decoration:none;color:inherit}
  a.cocard:active{background:#f7fafc}
  a.cocard .coi{display:block;min-width:0;flex:1}
  a.cocard .con{display:block;font-size:15px;font-weight:650;color:var(--navy);line-height:1.3}
  a.cocard .cot{display:block;font-size:12.5px;color:var(--muted);margin-top:2px}
  a.cocard .com{display:block;font-size:12.5px;color:var(--navy);margin-top:5px;font-variant-numeric:tabular-nums}
  a.cocard .cor{display:inline-flex;align-items:center;gap:8px;flex:none}
  a.cocard .cocount{font-size:11.5px;font-weight:600;color:var(--blue);background:#e8f0f8;padding:4px 9px;border-radius:999px;white-space:nowrap}
  a.cocard .cocount.none{color:var(--muted);background:#f0f3f7}
  /* The company's own header on the second level, then one card per person
     with every number on its own line under the name (Thomas: "phone numbers
     all listed under each employee's name"). Call and Text sit beside the
     number they act on; Save at the foot adds the whole person to the phone. */
  .cxhead{background:var(--navy);color:#fff;border-radius:var(--r-card);padding:4px 16px;margin:6px 0 12px;box-shadow:var(--shadow)}
  .cxhead .cxmain{display:flex;align-items:center;gap:10px;padding:12px 0}
  .cxhead .cxmain+.cxmain{border-top:1px solid rgba(255,255,255,.18)}
  .cxhead .cxmain .lbl{font-size:11px;letter-spacing:.06em;text-transform:uppercase;opacity:.7;flex:none}
  .cxhead .cxmain .num{flex:1;font-size:14.5px;font-weight:600;font-variant-numeric:tabular-nums;color:#fff;text-decoration:none;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .cxhead .cxmain .pact a{background:rgba(255,255,255,.14);color:#fff}
  .cxhead .cxmain .pact a:active{background:rgba(255,255,255,.28)}
  .pcard{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);margin-bottom:10px;box-shadow:var(--shadow);overflow:hidden}
  .pcard .phead{padding:13px 14px 11px}
  .pcard .pn{display:block;font-size:15px;font-weight:650;color:var(--navy);line-height:1.3}
  .pcard .pd{display:block;font-size:12.5px;color:var(--muted);margin-top:2px}
  .pline{display:flex;align-items:center;gap:10px;padding:10px 14px;border-top:1px solid var(--line)}
  .pline svg{width:16px;height:16px;color:var(--blue);flex:none}
  .pline .num{flex:1;min-width:0;font-size:14.5px;font-weight:600;color:var(--navy);text-decoration:none;font-variant-numeric:tabular-nums;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .pline .num.em{font-weight:500;font-size:13.5px}
  .pact{display:inline-flex;gap:6px;flex:none}
  .pact a{display:inline-flex;align-items:center;justify-content:center;min-width:44px;padding:7px 11px;border-radius:999px;background:#eef3f8;color:var(--navy);font-size:12px;font-weight:600;text-decoration:none}
  .pact a:active{background:#dfe8f1}
  a.psave{display:flex;align-items:center;justify-content:center;gap:6px;padding:11px;border-top:1px solid var(--line);background:#f7fafc;color:var(--blue);font-size:12.5px;font-weight:600;text-decoration:none}
  a.psave:active{background:#eef3f8}
  a.psave svg{width:15px;height:15px}
  .cempty{background:#f2f6fa;border:1px solid var(--line);border-radius:var(--r-card);padding:16px;font-size:13.5px;color:var(--muted);line-height:1.55}
  /* Thomas's own controls on the Contacts folder. Quiet by design: a subtle
     text button on each card and one dashed "Add" row, so the screen the
     subs see and the screen he edits are the same screen. */
  .pfoot{display:flex;border-top:1px solid var(--line)}
  .pfoot a.psave{flex:1;border-top:0}
  .pfoot .prm{flex:none;display:inline-flex;align-items:center;gap:5px;padding:11px 14px;border-left:1px solid var(--line);background:#f7fafc;
    font-family:var(--font);font-size:12.5px;font-weight:600;color:#8a2f1f;border:0;border-left:1px solid var(--line);cursor:pointer}
  .pfoot .prm.arm{background:#8a2f1f;color:#fff}
  .pfoot .prm.edit{color:var(--blue)}
  .pfoot .prm svg{width:14px;height:14px}
  .cadd{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;box-sizing:border-box;padding:13px;margin:4px 0 10px;
    background:transparent;border:1.5px dashed rgba(70,136,195,.5);border-radius:var(--r-card);font-family:var(--font);font-size:13.5px;font-weight:600;color:var(--blue);cursor:pointer}
  .cadd svg{width:16px;height:16px}
  .cform{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:14px;margin:4px 0 12px;box-shadow:var(--shadow)}
  .cform .cft{font-size:13px;font-weight:700;color:var(--navy);margin-bottom:10px}
  .cform input{display:block;width:100%;box-sizing:border-box;font-family:var(--font);font-size:14px;color:var(--navy);
    background:#fff;border:1px solid rgba(25,72,114,.18);border-radius:10px;padding:10px 12px;margin-bottom:8px;outline:none}
  .cform input:focus{border-color:var(--blue);box-shadow:0 0 0 3px rgba(70,136,195,.15)}
  .cform .cfb{display:flex;gap:8px;margin-top:4px}
  .cform .cfb .btn{flex:1;padding:11px}
  .cform .cfnote{font-size:12px;color:var(--muted);margin-top:8px;line-height:1.45}
  .chid{margin-top:22px}
  .chid .chidh{display:flex;align-items:center;justify-content:space-between;font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:700;margin-bottom:8px}
  .chidrow{display:flex;align-items:center;gap:10px;background:#f2f6fa;border:1px solid var(--line);border-radius:var(--r-card);padding:10px 14px;margin-bottom:6px}
  .chidrow .n{flex:1;min-width:0;font-size:13.5px;color:var(--navy)}
  .chidrow .n small{display:block;font-size:12px;color:var(--muted)}
  .chidrow button{flex:none;font-family:var(--font);font-size:12px;font-weight:600;color:var(--blue);background:#fff;border:1px solid rgba(70,136,195,.35);border-radius:999px;padding:6px 11px;cursor:pointer}
  .plocal{display:inline-block;font-size:10.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--blue);background:#e8f0f8;padding:2px 7px;border-radius:999px;margin-left:8px;vertical-align:middle}
  .shopt svg{width:20px;height:20px;color:var(--blue);flex:none}
  .ftype{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;font-weight:600;color:var(--navy);background:#e8f0f8;padding:4px 10px;border-radius:999px}
  .ext{width:16px;height:16px;color:var(--muted);flex:none}
  .grp{margin:22px 0 8px;font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--blue);font-weight:700}
  /* Fixtures and finishes (v142, ADR 0009): the job's sheet as a viewer, not
     a wall of cards. A bar with the count, a search and the areas as chips
     stays at the top; on a phone each item is one compact row (photo, name,
     make, the three specs that matter) that opens a detail sheet with
     everything; from a tablet up every area is a real table with only the
     columns this job fills in. A spreadsheet is never a sideways scroll
     (torc-app-standards). It is reached from a folder row at the top of
     Documents (v143), never from a tab of its own, so the client project
     and a sub's overview open it the same way. */
  .fxsearch{position:relative}
  .fxsearch>svg{position:absolute;left:12px;top:50%;transform:translateY(-50%);width:16px;height:16px;color:var(--muted);pointer-events:none}
  .fxsearch input{width:100%;box-sizing:border-box;min-height:44px;padding:10px 40px 10px 38px;border:1px solid rgba(25,72,114,.18);border-radius:12px;background:#fff;font-family:var(--font);font-size:15px;color:#22374d;-webkit-appearance:none;appearance:none}
  .fxsearch input:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 3px rgba(70,136,195,.18)}
  .fxsearch .fxclear{position:absolute;right:6px;top:50%;transform:translateY(-50%);width:32px;height:32px;border:none;background:none;color:var(--muted);font-size:22px;line-height:1;cursor:pointer;border-radius:50%;display:none}
  .fxsearch.has .fxclear{display:block}
  .fxarea{display:flex;align-items:center;gap:10px;margin:22px 0 8px;font-size:15px;font-weight:800;color:var(--navy);letter-spacing:.02em;text-transform:uppercase;overflow-wrap:anywhere}
  .fxarea b{font-weight:600;color:var(--muted);font-size:12.5px;letter-spacing:0;text-transform:none}
  .fxarea::after{content:"";flex:1;height:3px;border-radius:2px;background:var(--grad);opacity:.85}
  #fxList>.fxarea:first-child{margin-top:18px}
  .fxcat{margin:12px 0 6px;font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--blue);font-weight:700}
  /* Phone: one compact row per item inside a grouped list. */
  .fxlist{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);box-shadow:var(--shadow);overflow:hidden}
  .fxlist .fxcard{display:flex;align-items:center;gap:12px;padding:10px 10px 10px 12px;border-top:1px solid var(--line);cursor:pointer;min-height:64px;-webkit-tap-highlight-color:transparent}
  .fxlist .fxcard:first-child{border-top:none}
  .fxlist .fxcard:active{background:rgba(70,136,195,.10)}
  .fxlist .fxcard .ext{color:#b8c5d3;flex:none}
  .fxth{flex:none;width:52px;height:52px;border-radius:10px;background:#eef2f6;overflow:hidden;display:flex;align-items:center;justify-content:center;color:var(--blue)}
  .fxth img{width:100%;height:100%;object-fit:cover;display:block}
  .fxth svg{width:22px;height:22px;opacity:.5}
  .fxrb{flex:1;min-width:0}
  .fxrn{font-size:15px;font-weight:700;color:#22374d;line-height:1.25;overflow-wrap:anywhere}
  .fxrm{font-size:12.5px;color:var(--muted);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .fxrs{display:flex;flex-wrap:wrap;gap:4px 6px;margin-top:6px}
  .fxrs span{font-size:11.5px;font-weight:600;color:var(--navy);background:#e8f0f8;border-radius:6px;padding:2px 7px;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .fxrs span.doc{background:var(--goodbg);color:var(--good)}
  /* Tablet and desktop: the sheet as a table, one per area, categories as
     bands inside it, the header staying put under the search bar. */
  .fxtwrap{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);box-shadow:var(--shadow)}
  .fxt{width:100%;border-collapse:separate;border-spacing:0;font-size:13px;table-layout:auto}
  .fxt th{position:sticky;top:var(--fxbar,0px);z-index:2;background:var(--navy);color:#fff;font-size:10.5px;letter-spacing:.07em;text-transform:uppercase;text-align:left;padding:9px 10px;white-space:nowrap;font-weight:700}
  .fxt th:first-child{border-top-left-radius:var(--r-card)}
  .fxt th:last-child{border-top-right-radius:var(--r-card)}
  .fxt td{padding:9px 10px;border-top:1px solid var(--line);vertical-align:top;color:#22374d;overflow-wrap:anywhere;line-height:1.35}
  .fxt tr:first-child td{border-top:none}
  .fxt tr.fxcard{cursor:pointer}
  .fxt tr.fxcard:hover td{background:rgba(70,136,195,.06)}
  .fxt tr.fxcard:active td{background:rgba(70,136,195,.14)}
  .fxt td.ph{width:44px;padding-right:0}
  .fxt td.ph .fxth{width:44px;height:44px;border-radius:8px}
  .fxt td.it{min-width:150px}
  .fxt td.it b{display:block;font-weight:700}
  .fxt td.it small{display:block;color:var(--muted);font-size:12px;margin-top:1px}
  .fxt tr.fxtcat td{background:#eaf2fb;color:var(--blue);font-size:10.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;padding:7px 10px;border-top:1px solid rgba(70,136,195,.22)}
  .fxt td.num{white-space:nowrap;font-variant-numeric:tabular-nums}
  .fxt td.c-size{min-width:120px}
  /* Cabinet codes wrap at their commas, never inside a code (768px iPad). */
  .fxt td.c-model{font-variant-numeric:tabular-nums;overflow-wrap:normal;min-width:110px}
  .fxt td.c-colour{min-width:120px}
  .fxt td.c-sheen{white-space:nowrap}
  .fxt td.acts{white-space:nowrap;text-align:right}
  .fxpill{display:inline-block;font-size:11px;font-weight:700;color:var(--good);background:var(--goodbg);border-radius:6px;padding:3px 8px}
  .fxlink{display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:700;color:var(--blue);text-decoration:none;margin-left:8px;padding:4px 0}
  .fxlink svg{width:13px;height:13px}
  /* The detail sheet: the photo large, then everything the row carries. */
  /* The detail is an in-app window, front and center at every width
     (Thomas, 2026-09-24): never a sheet parked at the foot of the screen
     with square corners. Rounded all round, roomy, scrolls inside itself. */
  #fxModal{z-index:85;align-items:center;justify-content:center;padding:calc(16px + env(safe-area-inset-top)) 14px calc(16px + env(safe-area-inset-bottom));box-sizing:border-box;
    background-color:#fff;background-image:linear-gradient(rgba(15,44,71,.65),rgba(15,44,71,.65));
    background-size:100% calc(100% - env(safe-area-inset-bottom));background-repeat:no-repeat}
  #fxModal .sheet{width:100%;max-width:760px;max-height:calc(100dvh - 32px - env(safe-area-inset-top) - env(safe-area-inset-bottom));margin:auto 0;padding:0;position:relative;background:#fff;border-radius:22px;overflow:auto;box-shadow:0 30px 100px rgba(0,0,0,.35)}
  /* The photo whole, first, above the details (v204): no fixed box height,
     so nothing is cropped and no empty band is left under it. */
  .fxdph{background:#fff;height:auto;padding:14px;display:flex;align-items:center;justify-content:center;border-radius:22px 22px 0 0;overflow:hidden}
  .fxdph img{width:100%;height:auto;max-height:46vh;object-fit:contain;display:block;background:#fff}
  .fxdx{position:absolute;top:10px;right:10px;width:36px;height:36px;border-radius:50%;background:rgba(15,44,71,.72);color:#fff;border:none;font-size:22px;line-height:1;cursor:pointer;z-index:2;font-family:var(--font)}
  .fxdbody{padding:18px 20px 26px}
  .fxdcrumb{font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--blue);font-weight:700}
  .fxdname{font-size:22px;font-weight:800;color:var(--navy);margin-top:4px;line-height:1.2;padding-right:32px;overflow-wrap:anywhere}
  .fxdmake{font-size:15px;color:var(--muted);font-weight:600;margin-top:3px;overflow-wrap:anywhere}
  .fxdgrid{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:16px}
  .fxdgrid>div{background:var(--paper);border-radius:10px;padding:9px 11px;min-width:0}
  .fxdgrid>div.wide{grid-column:1/-1}
  .fxdgrid .k,.fxdnotes .k{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);font-weight:600}
  .fxdgrid .v{font-size:14px;font-weight:600;color:#22374d;margin-top:2px;overflow-wrap:anywhere;line-height:1.35}
  .fxdnotes{margin-top:14px;font-size:13.5px;line-height:1.5;color:#41546b;overflow-wrap:anywhere}
  .fxdnotes .k{display:block;margin-bottom:2px}
  .fxdacts{display:flex;flex-direction:column;gap:8px;margin-top:18px}
  .fxdacts .btn{width:100%;box-sizing:border-box}
  .fxdacts a.fxsrc{display:inline-flex;align-items:center;justify-content:center;gap:8px}
  .fxdacts a.fxsrc .ext{width:15px;height:15px;color:#fff}
  .fxdsrc{margin-top:10px;font-size:12px;color:#5b7896;text-align:center;overflow-wrap:anywhere}
  .fxdsrc.muted{color:#93a3b3}
  @media(min-width:768px){
    .fxsearch{max-width:520px}
    .fxdph img{max-height:52vh}
    .fxdbody{padding:22px 28px 30px}
    .fxdgrid{grid-template-columns:1fr 1fr 1fr}
    .fxdacts{flex-direction:row;flex-wrap:wrap}
    .fxdacts .btn{width:auto;flex:1 1 180px}
    /* From a tablet up the names sit in columns: the same rows, as cards. */
    .fxlist{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;background:none;border:none;box-shadow:none;overflow:visible}
    .fxlist .fxcard,.fxlist .fxcard:first-child{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);box-shadow:var(--shadow)}
    .fxlist .fxcard:hover{background:rgba(70,136,195,.08);border-color:var(--blue)}
    /* The sub's dark view keeps its translucent cards here too; the light
       card color above painted white names on white (found 2026-10-08). */
    body.subview .fxlist .fxcard,body.subview .fxlist .fxcard:first-child{background:rgba(255,255,255,.07);border-color:rgba(255,255,255,.12);box-shadow:none}
    body.subview .fxlist .fxcard:hover{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.3)}
  }
  @media(min-width:1024px){
    .fxlist{grid-template-columns:repeat(3,minmax(0,1fr))}
    .fxt td.c-size{min-width:190px}
    .fxt td.c-model{white-space:nowrap}
  }
  .grpnote{font-weight:500;color:var(--muted);text-transform:none;letter-spacing:0;font-size:11.5px;margin-left:8px}

  .apr{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:16px;margin-bottom:12px;box-shadow:var(--shadow)}
  .apr .head{display:flex;justify-content:space-between;align-items:flex-start;gap:10px}
  .apr .t{font-size:15px;font-weight:700;color:var(--navy)}
  .apr .d{font-size:12.5px;color:var(--muted);margin-top:3px}
  .apr table{width:100%;border-collapse:collapse;font-size:13px;margin:12px 0 4px}
  .apr td{padding:7px 0;border-bottom:1px solid var(--line)}
  .apr tr:last-child td{border-bottom:none;font-weight:700;color:var(--navy)}
  .apr .num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
  /* A completed approval is one tap target: the whole card, not a button
     buried inside it. Thomas, 2026-09-15: "just have that be a clickable
     button instead of having a separate Open the signed copy button." */
  button.apr{display:block;width:100%;text-align:left;font-family:var(--font);cursor:pointer;-webkit-tap-highlight-color:transparent}
  button.apr:active{transform:translateY(1px);background:#f7fafd}
  .apr .rt{display:flex;align-items:center;gap:8px;flex:none}
  .apr .rt .ext{color:var(--blue)}
  .aprbtns{display:flex;gap:8px;margin-top:14px}
  .btn{flex:1;padding:12px;border-radius:var(--r-btn);font-size:12.5px;font-weight:700;letter-spacing:.04em;cursor:pointer;text-align:center;text-decoration:none;text-transform:uppercase;font-family:var(--font);border:none}
  .btn.pri{background:var(--grad);color:#fff;box-shadow:var(--btnshadow)}
  .btn.pri:active{transform:translateY(1px)}
  .btn.pri[disabled]{opacity:.45;pointer-events:none}
  .btn.sec{background:#fff;border:1.5px solid rgba(25,72,114,.25);color:var(--navy)}
  .btn.danger{background:#fff;border:1.5px solid #b3391f;color:#8a2f1f}
  .btn.danger:active{background:#8a2f1f;color:#fff}
  .acccol{display:contents}
  .acctrow{display:flex;justify-content:space-between;gap:12px;padding:10px 0;border-bottom:1px solid var(--line);font-size:14px}
  .notiftoggle{display:flex;align-items:center;justify-content:space-between;gap:14px;margin-top:6px;font-size:14px;color:var(--navy);cursor:pointer}
  .switch{position:relative;flex:none;width:46px;height:26px}
  .switch input{opacity:0;width:0;height:0;position:absolute}
  .switch .slider{position:absolute;inset:0;background:#c7d0da;border-radius:999px;transition:background .18s}
  .switch .slider:before{content:"";position:absolute;height:20px;width:20px;left:3px;top:3px;background:#fff;border-radius:50%;transition:transform .18s;box-shadow:0 1px 2px rgba(0,0,0,.25)}
  .switch input:checked + .slider{background:#1a8f4a}
  .switch input:checked + .slider:before{transform:translateX(20px)}
  .acctrow:last-child{border-bottom:none}
  .acctrow .k{color:var(--muted);font-size:12.5px;text-transform:uppercase;letter-spacing:.06em;font-weight:600;padding-top:2px}
  .acctrow .v{font-weight:600;color:#22374d;text-align:right;word-break:break-word}
  .acctinput{width:100%;padding:12px;border:1px solid rgba(25,72,114,.25);border-radius:var(--r-btn);font-family:var(--font);font-size:15px;margin-top:8px;color:#22374d;background:#fff}
  /* Shared demo account: controls stay on screen so a prospect sees what a real
     account offers, but they are inert. The server refuses them regardless. */
  .demonote{margin-top:12px;font-size:12.5px;line-height:1.5;color:var(--muted);background:#f2f6fa;border:1px solid var(--line);border-radius:10px;padding:10px 12px}
  .acctinput:disabled{background:#f4f7fa;color:#8fa3b6;cursor:not-allowed}
  .btn:disabled,.btn[disabled]{opacity:.45;cursor:not-allowed;pointer-events:none}
  .notiftoggle input:disabled+.slider{opacity:.45;cursor:not-allowed}
  .notifrow{display:flex;align-items:center;gap:10px;padding:10px 0;border-bottom:1px solid var(--line);cursor:pointer}
  .notifrow:last-child{border-bottom:none}
  .notifrow .dot{width:8px;height:8px;border-radius:50%;background:var(--warn);flex:none}
  .notifrow .t{font-size:13.5px;font-weight:600;color:#22374d}
  .notifrow .d{font-size:12px;color:var(--muted)}
  .aprdone{background:var(--goodbg);border-radius:var(--r-btn);padding:12px;margin-top:12px;font-size:13px;color:var(--good)}

  .modal{display:none;position:fixed;inset:0;background:rgba(15,44,71,.65);z-index:50;align-items:flex-end;justify-content:center}
  /* The share window opens over an open document (the viewer is at 70). */
  #shareModal{z-index:80}
  .modal.open{display:flex}
  .sheet{background:var(--paper);border-radius:22px 22px 0 0;width:100%;max-width:430px;padding:22px 20px calc(30px + env(safe-area-inset-bottom));max-height:86vh;overflow:auto}
  /* Locks the page behind any open modal so only the modal scrolls. */
  body.modal-open{overflow:hidden}
  #createModal{align-items:center;justify-content:center;padding:28px;background:rgba(5,21,40,.9);backdrop-filter:blur(12px);overflow-y:auto;-webkit-overflow-scrolling:touch}
  #createModal .sheet{width:100%;max-width:590px;max-height:calc(100dvh - 56px);padding:0;border:1px solid rgba(255,255,255,.18);border-radius:24px;background:#fff;overflow:auto;box-shadow:0 30px 100px rgba(0,0,0,.35)}
  .caHeader{padding:20px 28px;border-bottom:1px solid rgba(255,255,255,.12);background:linear-gradient(145deg,#06172d,#0a3769);text-align:center}
  .caHeader img{display:block;width:330px;max-width:88%;height:auto;margin:0 auto;filter:drop-shadow(0 8px 15px rgba(0,0,0,.24))}
  .caCard{padding:28px 32px 30px;background:#fff}
  .caSocialGrid{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-top:18px}
  .caSocial{display:none;align-items:center;justify-content:center;gap:10px;width:100%;min-height:48px;padding:11px 14px;border:1px solid #ccd7e2;border-radius:11px;background:#fff;color:#17324d;font-family:var(--font);font-size:13px;font-weight:700;text-decoration:none;cursor:pointer}
  .caSocial:hover{border-color:#8fabca;background:#f8fafc}
  .caDivider{display:none;align-items:center;gap:12px;margin:17px 0 5px;color:#8796a5;font-size:10px;font-weight:700;letter-spacing:.1em;text-transform:uppercase}
  .caDivider:before,.caDivider:after{content:"";flex:1;height:1px;background:#e0e6ec}
  .provider-verified{display:none;align-items:center;gap:11px;margin:16px 0 4px;padding:12px 13px;border:1px solid #cbe4d5;border-radius:11px;background:#eff9f3;color:#245e3d;font-size:12.5px;font-weight:600}
  .provider-verified svg{width:19px;height:19px;flex:none}
  .provider-verified.show{display:flex}
  #createModal.social-flow .caSocialGrid,#createModal.social-flow .caDivider{display:none !important}
  #createModal.social-flow #caEmail{background:#f1f5f8;color:#51687e}
  @media (max-width:767px){
    #createModal{align-items:flex-start;padding:0;background:#fff}
    #createModal .sheet{min-height:100dvh;max-height:none;border:0;border-radius:0;background:#fff}
    .caHeader{padding:calc(14px + env(safe-area-inset-top)) 20px 14px}
    .caHeader img{width:285px}
    .caCard{padding:22px 20px calc(26px + env(safe-area-inset-bottom))}
    #createModal .sheet h3{font-size:22px}
    #createModal .sheet .sub{font-size:12.5px;margin-bottom:3px}
    #createModal .sheet label{margin:11px 0 5px}
    #createModal .sheet input[type=text],#createModal .sheet input[type=email],#createModal .sheet input[type=tel],#createModal .sheet input[type=password],#createModal .sheet select{padding:11px 12px}
    #createModal .sheet .agree2{margin:12px 0 0;font-size:12.5px}
    #createModal .sheet .aprbtns{margin-top:14px !important}
    #createModal .copyline{display:none}
  }
  @media (max-width:420px){.caSocialGrid{grid-template-columns:1fr}}
  /* Scoped under .sheet so these beat the generic ".sheet label" rule
     (which would otherwise force display:block — collapsing the gap — and uppercase). */
  .sheet .agree2{display:flex;align-items:flex-start;gap:14px;margin:16px 0 2px;font-size:13.5px;line-height:1.5;color:var(--navy);cursor:pointer;text-transform:none;letter-spacing:normal;font-weight:400}
  .sheet .agree2 input{width:18px;height:18px;flex:none;margin-top:1px}
  .sheet .agree2 a{color:var(--blue);font-weight:600}
  /* Change-order builder rows */
  .corow{display:flex;gap:8px;margin-bottom:8px}
  .corow input{padding:11px 12px;border-radius:var(--r-btn);border:1px solid rgba(25,72,114,.25);font-size:15px;font-family:var(--font);color:var(--navy);background:#fff}
  .corow input:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 3px rgba(70,136,195,.15)}
  .corow .codesc{flex:1;min-width:0}
  .corow .coamt{width:104px;text-align:right;flex:none}
  .corow .corm{background:#f0f3f7;border:none;border-radius:10px;width:34px;flex:none;color:#8794a3;font-size:17px;cursor:pointer;font-family:var(--font)}
  .cototal{display:flex;justify-content:space-between;align-items:center;margin-top:12px;padding:12px 2px 0;border-top:1px solid var(--line);font-size:13.5px;color:var(--muted)}
  .cototal b{color:var(--navy);font-size:16px}
  #coErr,#dpErr{background:#fde8e8;color:#b4232a;border-radius:var(--r-btn);padding:10px 13px;font-size:13px}
  .pwmatch{display:none;align-items:center;gap:7px;font-size:13px;font-weight:600;margin-top:9px}
  .pwmatch.show{display:flex}
  .pwmatch.ok{color:#1a8f4a}
  .pwmatch.bad{color:#c0392b}
  .pwmatch svg{width:18px;height:18px;flex:none}
  .sheet h3{font-weight:600;font-size:19px;color:var(--navy);margin-bottom:4px;letter-spacing:-0.01em}
  .sheet .impact{font-size:15px;font-weight:700;color:var(--navy);margin-bottom:14px}
  .sheet label{display:block;font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);margin:14px 0 6px;font-weight:600}
  .sheet input[type=text],.sheet input[type=email],.sheet input[type=tel],.sheet input[type=password],.sheet select{width:100%;padding:13px;border-radius:var(--r-btn);border:1px solid rgba(25,72,114,.25);background:#fff;font-size:16px;font-family:var(--font);color:var(--navy)}
  .sheet input:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 3px rgba(70,136,195,.15)}
  .sheet .sub{margin-bottom:14px}
  .agree{display:flex;gap:10px;align-items:flex-start;margin-top:14px;font-size:12.5px;color:var(--muted)}
  .agree input{margin-top:3px;accent-color:var(--navy)}

  #toast{display:none;position:fixed;bottom:104px;left:50%;transform:translateX(-50%);background:var(--navy);color:#fff;padding:12px 20px;border-radius:999px;font-size:13px;z-index:60;white-space:nowrap;box-shadow:0 6px 18px rgba(15,44,71,.4)}

  /* ---------- recaps, signing, admin sends (2026-09-11) ---------- */
  .adminacts{display:flex;gap:8px;margin:0 0 14px}
  .adminacts .btn{flex:1;padding:11px 10px;font-size:11.5px}
  .recaprow{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;text-align:left;background:var(--card);border:1px solid var(--line);
    border-radius:var(--r-card);padding:12px 14px;margin-top:8px;box-shadow:var(--shadow);font-family:var(--font);cursor:pointer;color:inherit;text-decoration:none}
  .recaprow .rt{display:block;font-size:14px;font-weight:600;color:var(--navy);line-height:1.3}
  .recaprow .rd{display:block;font-size:12px;color:var(--muted);margin-top:2px}
  .recaprow .ext{width:16px;height:16px;color:var(--blue);flex:none}
  .sheet textarea{width:100%;box-sizing:border-box;min-height:180px;padding:13px;border-radius:var(--r-btn);border:1px solid rgba(25,72,114,.25);background:#fff;font-size:15px;line-height:1.5;font-family:var(--font);color:var(--navy);resize:vertical}
  .sheet textarea:focus,.sheet select:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 3px rgba(70,136,195,.15)}
  .sheet .hint{font-size:12px;color:var(--muted);margin-top:6px;line-height:1.45}
  /* The weekly recap window (v152): a shorter Next week box, the day it
     shows on the dashboard, the change order checklist and, under the
     payment, the total the client will see. */
  .sheet textarea.short{min-height:90px}
  .sheet input[type=date]{width:100%;padding:12px 13px;border-radius:var(--r-btn);border:1px solid rgba(25,72,114,.25);background:#fff;font-size:16px;font-family:var(--font);color:var(--navy)}
  .rccos{display:flex;flex-direction:column;gap:6px}
  .sheet label.rcco{display:flex;align-items:center;gap:10px;margin:0;padding:10px 12px;min-height:44px;border:1px solid var(--line);border-radius:var(--r-btn);background:#fff;cursor:pointer;
    text-transform:none;letter-spacing:normal;font-size:14px;font-weight:400;color:var(--navy)}
  .rcco input{width:20px;height:20px;flex:none;margin:0}
  .rcco .n{flex:1;min-width:0;font-weight:700;overflow-wrap:anywhere}
  .rcco .n small{display:block;font-size:12px;font-weight:400;color:var(--muted);margin-top:1px}
  .rcco .a{font-size:13.5px;color:#22374d;font-variant-numeric:tabular-nums;white-space:nowrap}
  .rcchip{flex:none;font-size:11px;font-weight:700;border-radius:999px;padding:3px 8px;white-space:nowrap}
  .rcchip.ok{background:var(--goodbg);color:var(--good)}
  .rcchip.wait{background:#fff4e0;color:#8a5a00}
  .rcsum{margin-top:10px;padding:10px 12px;border:1px solid var(--line);border-radius:var(--r-btn);background:#f7fafd}
  .rcsum[hidden]{display:none}
  .rcsum .lead{font-size:12.5px;color:var(--muted);margin:0 0 6px;line-height:1.45}
  .rcsum .more{font-size:12.5px;color:var(--muted);margin-top:6px;line-height:1.45}
  /* Review before a send (v152): what goes out, who gets it, and one
     button that sends. It sits over the form it came from (z 50), under
     the viewer (z 70) that Preview opens. */
  #srModal{z-index:55}
  #srModal .srkick{font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--blue);font-weight:700;margin-bottom:4px}
  #srModal .srsub{font-size:13px;color:var(--muted);margin:0 0 4px}
  #srModal .srsub:empty{display:none}
  #srErr{background:#fde8e8;color:#b4232a;border-radius:var(--r-btn);padding:10px 13px;font-size:13px}
  .srsec{margin-top:16px}
  .srsec .k{display:block;font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:600;margin-bottom:6px}
  .srtext{font-size:13.5px;color:#22374d;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere}
  .srnote{font-size:12.5px;color:var(--muted);line-height:1.45;margin-top:4px}
  .srlines{width:100%;border-collapse:collapse;font-size:13.5px}
  .srlines td{padding:7px 0;border-bottom:1px solid var(--line);color:#22374d;vertical-align:top;overflow-wrap:anywhere}
  .srlines td.m{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums;padding-left:12px}
  .srlines td .s{display:block;font-size:12px;color:var(--muted)}
  .srlines tr.tot td{border-bottom:none;border-top:1.5px solid #dfe6ee;font-weight:700;color:var(--navy)}
  .srgo{display:flex;align-items:center;gap:8px;font-size:13.5px;color:#22374d;padding:3px 0}
  .srgo svg{width:16px;height:16px;flex:none;color:#1a8f4a}
  .srbtns{flex-wrap:wrap}
  .srbtns #srSend{flex-basis:100%}
  /* Sign: a bar along the foot of the viewer frame while a document waits
     for a signature, and the sheet that takes the signature. The sheet sits
     above the viewer (z 70). */
  .vsign{flex:none;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px 10px 14px;background:#eef3f8;border-bottom:1px solid rgba(25,72,114,.14)}
  .vsign[hidden]{display:none}
  /* A page (a recap) shown inline in the viewer: the branded table sits on
     its own gray ground and the body scrolls like any document. */
  .vpage{min-height:100%;background:#eef2f6}
  .vpage img{max-width:100%}
  /* The admin's controls at the top of Approvals. */
  .aprctl{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:14px 16px;margin-bottom:12px;box-shadow:var(--shadow)}
  .aprctl label{display:block;font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);margin:0 0 6px;font-weight:600}
  .aprctl select{width:100%;padding:12px;border-radius:var(--r-btn);border:1px solid rgba(25,72,114,.25);background:#fff;font-size:15px;font-family:var(--font);color:var(--navy)}
  .aprctl .note{font-size:12px;color:var(--muted);margin-top:6px;line-height:1.45}
  .aprctl .adminacts{margin:14px 0 0}
  .vsign .vst{font-size:13px;color:var(--navy);line-height:1.35;min-width:0}
  .vsign .vst.err{color:#8a2f1f;font-weight:600}
  .vsign .btn{flex:none;padding:11px 18px;width:auto}
  .vsign .btn.nudge{animation:esgNudge 1.6s ease-out 2}
  #signModal{z-index:80}
  #signModal .sheet{max-width:520px}
  #sgSub.err{color:#8a2f1f;font-weight:600}
  .sigtabs{display:flex;gap:6px;margin:14px 0 10px;background:#e8eef4;padding:4px;border-radius:12px}
  .sigtabs button{flex:1;padding:9px;border:0;border-radius:9px;background:transparent;font-family:var(--font);font-size:13px;font-weight:600;color:var(--muted);cursor:pointer}
  .sigtabs button.on{background:#fff;color:var(--navy);box-shadow:0 1px 3px rgba(25,72,114,.15)}
  .sigstyles{display:grid;gap:8px}
  .sigstyle{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border:1.5px solid rgba(25,72,114,.18);border-radius:12px;background:#fff;cursor:pointer;min-height:64px}
  .sigstyle.on{border-color:var(--blue);box-shadow:0 0 0 3px rgba(70,136,195,.15)}
  .sigstyle .sigprev{font-size:30px;line-height:1.1;color:#1b2e44;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
  .sigstyle small{flex:none;font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.06em}
  .sig1{font-family:'Snell Roundhand','Savoye LET','Apple Chancery','Brush Script MT','Segoe Script',cursive}
  .sig2{font-family:'Noteworthy','Bradley Hand','Segoe Print','Comic Sans MS',cursive}
  .sig3{font-family:'Zapfino','Apple Chancery','Lucida Handwriting','Segoe Script',cursive;font-size:22px}
  .sigpad{position:relative;border:1.5px dashed rgba(25,72,114,.35);border-radius:12px;background:#fff;height:170px;overflow:hidden;touch-action:none}
  .sigpad canvas{display:block;width:100%;height:100%}
  .sigpad .sigline{position:absolute;left:18px;right:18px;bottom:34px;border-top:1px solid rgba(25,72,114,.25);pointer-events:none}
  .sigpad .sighint{position:absolute;left:0;right:0;bottom:10px;text-align:center;font-size:11.5px;color:var(--muted);pointer-events:none}
  .sigpad.has .sighint{display:none}
  .sigclear{display:inline-block;margin-top:8px;background:none;border:0;font-family:var(--font);font-size:12.5px;font-weight:600;color:var(--blue);cursor:pointer;padding:4px 0}
  .sigwho{font-size:12px;color:var(--muted);margin-top:10px;line-height:1.45}
  /* Attach a file to a client approval (admin). */
  .filepick{display:flex;align-items:center;gap:10px;margin-top:2px}
  .filepick .btn{width:auto;padding:11px 14px;flex:none;font-size:14px}
  .filepick .fname{flex:1;min-width:0;font-size:13px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .filepick.has .fname{color:var(--navy);font-weight:600}
  .orline{font-size:11px;color:var(--muted);text-align:center;margin:12px 0 4px;letter-spacing:.08em;text-transform:uppercase}
  .sdnote{font-size:12px;color:var(--muted);margin-top:6px;line-height:1.45}
  /* ---------- SIGNING SESSION (v150) ----------
     Thomas, 2026-09-26: signing off should look and feel like the e-sign
     service clients already know (the name stays out of this file). The look and
     the order of a signing service's recipient session, in the Torc light
     brand: consent first, a bar across the top with Finish, a yellow Sign tag
     on the page where the signature goes, a Start tab on the left edge, the
     Adopt your signature window, and a full screen "done" at the end. The
     page overlays sit in percentages of the page, so zoom and a resize keep
     them in place. Inside .esg-layer one em is one point of the PDF page. */
  .pdfpage.esg-host{container-type:inline-size}
  .esg-layer{position:absolute;inset:0;pointer-events:none;font-size:.58px;font-size:calc(100cqw / var(--esgW,612));z-index:2}
  .esg-layer>*{position:absolute}
  .esg-tag{pointer-events:auto;display:flex;align-items:center;justify-content:center;gap:6px;min-height:34px;min-width:84px;box-sizing:border-box;padding:4px 10px;
    background:#ffd24d;border:1px solid #d9a300;border-radius:4px;color:#3b2c00;font-family:var(--font);font-size:13px;font-weight:700;letter-spacing:.02em;cursor:pointer;
    box-shadow:0 1px 3px rgba(60,40,0,.25);animation:esgPulse 1.8s ease-out infinite}
  .esg-tag svg{width:15px;height:15px;flex:none}
  .esg-tag.inert{cursor:default;animation:none}
  .esg-tag:focus-visible{outline:2px solid var(--navy);outline-offset:2px}
  @keyframes esgPulse{0%{box-shadow:0 0 0 0 rgba(255,196,40,.75),0 1px 3px rgba(60,40,0,.25)}70%{box-shadow:0 0 0 10px rgba(255,196,40,0),0 1px 3px rgba(60,40,0,.25)}100%{box-shadow:0 0 0 0 rgba(255,196,40,0),0 1px 3px rgba(60,40,0,.25)}}
  @keyframes esgNudge{0%{box-shadow:0 0 0 0 rgba(70,136,195,.6)}70%{box-shadow:0 0 0 12px rgba(70,136,195,0)}100%{box-shadow:0 0 0 0 rgba(70,136,195,0)}}
  @media (prefers-reduced-motion:reduce){.esg-tag,.vsign .btn.nudge{animation:none}}
  /* The placed signature, framed the way the stamp on the signed copy is:
     "Signed by:" at the top left and a thin brand-blue bracket down the left
     side to the hairline, ticks at both ends. The numbers are the stamp's
     (lib/signpdf.js stampInPlace), in points: "Signed by:" 6.5 in from the
     bracket, the signature from 10 below the top to 2.5 above the bottom
     and 6.5 in, the printed name at 9. .esg-by's own em is 6.5 points (its
     font-size), so its left is 1em, not 6.5em (v151). */
  .esg-placed{box-sizing:border-box;pointer-events:auto;cursor:pointer}
  .esg-by{position:absolute;left:1em;top:0;font-size:6.5em;line-height:1;color:#6b7c8e;font-family:Helvetica,Arial,sans-serif;white-space:nowrap}
  .esg-brk{position:absolute;left:0;top:0;bottom:0;width:3em;border:1px solid #4688c3;border-right:0}
  .esg-placed img{position:absolute;left:6.5em;top:10em;bottom:2.5em;width:calc(100% - 8.5em);height:calc(100% - 12.5em);object-fit:contain;object-position:left bottom}
  .esg-placed.inert{cursor:default}
  .esg-name{white-space:nowrap;font-size:9em;line-height:1;color:#22374d;font-family:Helvetica,Arial,sans-serif;transform:translateY(-80%)}
  .esg-date{display:flex;flex-direction:column;justify-content:flex-end;box-sizing:border-box;padding-bottom:1.5em;font-family:Helvetica,Arial,sans-serif;line-height:1.15}
  .esg-date b{font-size:10em;font-weight:400;color:#22374d;white-space:nowrap}
  .esg-date span{font-size:7.5em;color:#6b7c8e;white-space:nowrap}
  .esg-change{pointer-events:auto;transform:translate(-50%,3px);background:#fff;border:1px solid rgba(70,136,195,.55);border-radius:999px;padding:2px 10px;
    font-family:var(--font);font-size:11px;font-weight:600;color:var(--blue);cursor:pointer;box-shadow:0 1px 3px rgba(25,72,114,.15)}
  /* A document with no signature block of its own (a spec sheet): a clearly
     labelled place to sign after the last page. */
  .esg-end{background:#fff;border-radius:6px;box-shadow:0 1px 6px rgba(20,45,70,.16);padding:14px 14px 6px;margin:0 auto 10px}
  .esg-endlbl{font-size:13px;color:var(--navy);line-height:1.45;font-weight:600}
  .esg-endbox{position:relative;width:100%;aspect-ratio:612 / 120;container-type:inline-size}
  .esg-endbox .esg-hair{position:absolute;border-top:1px solid #9aa9b8}
  .esg-endbox .esg-cap{position:absolute;font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:#6b7c8e;font-family:var(--font)}
  /* The Start tab on the left edge takes the reader to the Sign tag. */
  .esg-start{position:absolute;left:0;top:50%;z-index:6;height:40px;width:78px;padding:0 18px 0 10px;border:0;background:#ffd24d;color:#3b2c00;
    font-family:var(--font);font-size:13px;font-weight:800;letter-spacing:.03em;text-align:left;cursor:pointer;
    clip-path:polygon(0 0,76% 0,100% 50%,76% 100%,0 100%);filter:drop-shadow(0 1px 2px rgba(0,0,0,.25))}
  .esg-start[hidden]{display:none}
  /* Consent: a card over the document. */
  .esg-consent{position:absolute;left:0;right:0;bottom:0;top:0;z-index:7;background:rgba(233,238,243,.82);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);
    overflow:auto;padding:18px 14px calc(18px + env(safe-area-inset-bottom));box-sizing:border-box}
  .esg-consent[hidden]{display:none}
  .esg-ccard{max-width:440px;margin:0 auto;background:#fff;border-radius:16px;overflow:hidden;box-shadow:0 14px 44px rgba(25,72,114,.22)}
  .esg-chead{background:linear-gradient(145deg,#06172d,#0a3769);padding:14px 18px;text-align:center}
  .esg-chead img{display:block;width:170px;max-width:70%;height:auto;margin:0 auto}
  .esg-cbody{padding:18px 18px 16px}
  .esg-cbody h3{font-size:19px;font-weight:600;color:var(--navy);margin:0 0 6px;letter-spacing:-.01em}
  .esg-cline{font-size:14px;color:#22374d;line-height:1.5;margin:0 0 14px}
  .esg-ccheck{display:flex;gap:10px;align-items:flex-start;font-size:14px;color:var(--navy);line-height:1.45;cursor:pointer;padding:12px;border:1px solid rgba(25,72,114,.16);border-radius:10px;background:#f7f9fb}
  .esg-ccheck input{width:20px;height:20px;flex:none;margin:0;accent-color:var(--navy)}
  .esg-clink{background:none;border:0;padding:8px 0 2px;font-family:var(--font);font-size:13px;font-weight:600;color:var(--blue);cursor:pointer}
  .esg-disc{margin-top:6px;padding:12px 14px;border-radius:10px;background:#f4f7fa;font-size:12.5px;color:#22374d;line-height:1.5}
  .esg-disc[hidden]{display:none}
  .esg-disc p{margin:0 0 8px}
  .esg-disc p:last-child{margin:0}
  .esg-cgo{display:block;width:100%;margin-top:16px;padding:13px}
  .esg-clater{display:block;width:100%;margin-top:6px;background:none;border:0;padding:10px;font-family:var(--font);font-size:13.5px;font-weight:600;color:var(--muted);cursor:pointer}
  /* Adopt your signature: the preview is framed like the stamp. */
  .esg-prevlbl{font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);margin:16px 0 6px;font-weight:600}
  .esg-prev{position:relative;height:92px;border:1px solid rgba(25,72,114,.14);border-radius:12px;background:#fff;box-sizing:border-box;padding:0;overflow:hidden}
  .esg-prev .esg-brk{left:14px;top:12px;bottom:14px;width:10px;border-color:#4688c3}
  .esg-prev .esg-by{left:30px;top:10px;font-size:11px}
  .esg-prevsig{position:absolute;left:30px;right:12px;top:26px;bottom:12px;display:flex;align-items:flex-end;overflow:hidden}
  .esg-prevsig img{max-height:100%;max-width:100%;object-fit:contain;object-position:left bottom;display:block}
  .esg-prev .esg-ptxt{font-size:34px;line-height:1.05;color:#1b2e44;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
  .esg-prev .esg-ptxt.sig3{font-size:25px}
  .esg-prevsig .esg-pnone{font-size:12.5px;color:var(--muted);align-self:center}
  .esg-legal{font-size:12px;color:var(--muted);line-height:1.5;margin:14px 0 0}
  /* Finishing, then finished: full screen over the viewer. */
  #esgDone{position:fixed;inset:0;z-index:76;display:flex;align-items:center;justify-content:center;padding:calc(16px + env(safe-area-inset-top)) 16px calc(16px + env(safe-area-inset-bottom));box-sizing:border-box;
    background-color:#fff;background-image:linear-gradient(rgba(10,30,50,.55),rgba(10,30,50,.55));
    background-size:100% calc(100% - env(safe-area-inset-bottom));background-repeat:no-repeat}
  #esgDone[hidden]{display:none}
  #esgDone.fin{background:var(--paper)}
  .esg-busy{display:flex;flex-direction:column;align-items:center;gap:14px;background:#fff;border-radius:16px;padding:22px 30px;font-size:15px;font-weight:600;color:var(--navy);box-shadow:0 14px 44px rgba(0,0,0,.25)}
  .esg-busy[hidden]{display:none}
  .esg-spin{width:34px;height:34px;border-radius:50%;border:3px solid rgba(70,136,195,.2);border-top-color:var(--blue);animation:esgSpin .8s linear infinite}
  @keyframes esgSpin{to{transform:rotate(360deg)}}
  .esg-fin{width:100%;max-width:420px;text-align:center}
  .esg-fin[hidden]{display:none}
  .esg-check{width:92px;height:92px;margin:0 auto 20px;border-radius:50%;background:#2f8f5b;color:#fff;display:flex;align-items:center;justify-content:center;box-shadow:0 10px 30px rgba(47,143,91,.3)}
  .esg-check svg{width:50px;height:50px}
  .esg-fin h2{font-size:24px;font-weight:700;color:var(--navy);margin:0 0 10px;letter-spacing:-.01em}
  .esg-fin p{font-size:15px;color:#22374d;line-height:1.55;margin:0 0 24px}
  .esg-finbtns{display:flex;flex-direction:column;gap:10px}
  .esg-finbtns .btn{display:block;width:100%;padding:14px}

  /* ---------- IN-PORTAL DOCUMENT VIEWER ---------- */
  /* Thomas, 2026-09-11: "pop up as an independent window that's a viewer
     with a blue border, where I can intuitively know that I can scroll left
     and right with my thumb ... have that picture be the focal point." So the
     viewer is a window, not a page: a dark backdrop, a framed panel with the
     brand-blue border inset from the phone's edges, the document filling it.
     A photo sits centered on a dark ground so it reads as the subject rather
     than a strip in a white page; a PDF keeps its light reading ground. */
  /* One field behind the framed sheet, from the status bar to the home
     indicator. The fill used to be rgba(10,30,50,.92), so the header navy
     showed through above the window and the page showed through below it.
     v230 then painted that bottom inset white and covered the frame's
     shadow. #1d2f42 is the same backdrop, opaque, so the top and the bottom
     match and the sheet still floats in it. */
  #viewer{display:none;position:fixed;inset:0;z-index:70;background:#1d2f42;
    padding:calc(8px + env(safe-area-inset-top)) 8px calc(8px + env(safe-area-inset-bottom));box-sizing:border-box}
  #viewer.open{display:flex;flex-direction:column}
  .vframe{flex:1;min-height:0;display:flex;flex-direction:column;width:100%;max-width:1180px;margin:0 auto;
    border:3px solid var(--blue);border-radius:18px;overflow:hidden;background:var(--paper);
    box-shadow:0 18px 60px rgba(0,0,0,.45);position:relative}
  .vhead{background:var(--navy);color:#fff;padding:8px 12px;display:flex;align-items:center;gap:10px;flex:none}
  .vhead .vback{background:rgba(255,255,255,.92);border:none;color:var(--navy);font-family:var(--font);font-size:12.5px;font-weight:600;padding:8px 14px;border-radius:var(--r-btn);cursor:pointer;flex:none}
  .vhead .vtitle{flex:1;min-width:0;font-size:14px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .vhead .vsub{font-size:11.5px;color:rgba(255,255,255,.8);font-weight:400}
  /* Download and share sit together at the top right of an open document.
     Icons, not labels: two words would crowd out the document's own title on
     a phone. 44px targets per the mobile contract. */
  .vhead .vacts{display:flex;align-items:center;gap:6px;flex:none}
  .vhead .vact{display:flex;align-items:center;justify-content:center;width:44px;height:44px;background:rgba(255,255,255,.92);border:none;border-radius:var(--r-btn);color:var(--navy);cursor:pointer;text-decoration:none;padding:0}
  .vhead .vact svg{width:19px;height:19px}
  /* Thomas, 2026-09-11: "remove the download button on all of the files and
     just keep the share button." Share hands the file to the phone's own
     sheet, which has Save in it, so nothing is lost. */
  #vDownload{display:none!important}
  /* The 3D scan opens as a web page filling the viewer, so its own controls
     get the whole screen. No zoom layer sizing games: the frame owns it. */
  #vBody.web,#vBody.web #vZoom{height:100%;overflow:hidden}
  #vBody.web{background:#0b1b2b}
  iframe.vweb{display:block;width:100%;height:100%;border:0}
  .scanbtn{display:block;box-sizing:border-box;width:100%;text-align:center;text-decoration:none;
    background:var(--blue);color:#fff;border:1px solid var(--blue);border-radius:var(--r-card);
    padding:13px 14px;margin-bottom:12px;font-size:14px;font-weight:700;box-shadow:var(--shadow)}
  .scanbtn:hover{background:#3a76ab;border-color:#3a76ab}
  .scanbtn:active{background:#2f6293;border-color:#2f6293}
  .vhead .vact:active{background:#fff}
  .vbody{flex:1;overflow:auto;background:#e9eef3;position:relative;touch-action:pan-y}
  /* A photo: the frame is the picture. Centered, fitted, on the dark ground. */
  .vbody.photo{background:#0f2c47;overflow:hidden}
  .vbody.photo #vZoom{width:100%;height:100%;display:flex;align-items:center;justify-content:center}
  .vbody.photo img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;margin:0;background:transparent;
    box-shadow:0 10px 40px rgba(0,0,0,.5)}
  /* Where you are in a folder of pictures: dots for a handful, a count for
     more. Sits at the foot of the frame, over the picture, and is the sign
     that a thumb can swipe. */
  .gpos{position:absolute;left:0;right:0;bottom:12px;display:flex;justify-content:center;pointer-events:none;z-index:2}
  .gpos[hidden]{display:none}
  .gpos .gpill{display:inline-flex;align-items:center;gap:6px;background:rgba(10,30,50,.62);color:#fff;
    border-radius:999px;padding:6px 12px;font-size:11.5px;font-weight:600;letter-spacing:.03em;backdrop-filter:blur(6px)}
  .gpos .gdot{width:6px;height:6px;border-radius:50%;background:rgba(255,255,255,.45)}
  .gpos .gdot.on{background:#fff;transform:scale(1.25)}
  /* pan-y (not the default "auto") is deliberate: it keeps single-finger
     vertical scroll working exactly as before, while explicitly refusing the
     browser's OWN pinch-zoom/double-tap-zoom on this element so it can never
     fight the JS-driven zoom below, which owns that gesture instead. */
  .vbody.zooming{touch-action:none}
  /* Gallery arrows. Anchored to #viewer rather than to #vBody so a step to the
     next photo, which rewrites #vBody wholesale, never takes them with it.
     The row is click-through; only the two buttons are not. */
  .gnav{position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);
    display:flex;justify-content:space-between;padding:0 8px;pointer-events:none;z-index:2}
  .gnav[hidden]{display:none}
  /* Quieter than before: the swipe is the gesture, the arrows are the hint.
     Translucent over a photo, so the picture stays the subject. */
  .gbtn{pointer-events:auto;display:flex;align-items:center;justify-content:center;
    width:40px;height:40px;padding:0;border:none;border-radius:50%;
    background:rgba(10,30,50,.45);color:#fff;cursor:pointer;backdrop-filter:blur(4px);
    box-shadow:0 2px 10px rgba(0,0,0,.25)}
  .vbody:not(.photo)~.gnav .gbtn{background:rgba(255,255,255,.9);color:var(--navy)}
  .gbtn svg{width:22px;height:22px}
  .gbtn:active{background:#fff}
  /* At the first or last item the arrow stays put and greys out, so the row
     never jumps sideways mid-gallery. */
  .gbtn[disabled]{opacity:.22;cursor:default;box-shadow:none}
  @media(min-width:900px){#viewer{padding:24px}}
  /* Everything the viewer shows — a PDF, a photo, a sheet, a folder grid —
     renders inside this one wrapper, so pinching has a single, predictable
     element to scale. transform-origin:0 0 matches the math in the zoom
     controller, which treats (0,0) as the wrapper's own top-left corner. */
  #vZoom{transform-origin:0 0}
  .vbody iframe{width:100%;height:100%;border:none;display:block;background:#fff}
  .vbody img{display:block;max-width:100%;margin:0 auto;background:#fff}
  /* The email template previewed in the viewer: its logo is a transparent PNG on the blue header, so the white image ground above would show as a white block. */
  .vbody .vpage img{background:transparent}
  .fxwait{padding:28px 0;text-align:center;color:var(--muted);font-size:14px}
  .vloading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--muted);font-size:14px}
  .vsheet{padding:16px;background:var(--paper);min-height:100%}
  /* One card per spreadsheet row. Label left, value right, wrapping instead of
     running off the side, so a spec sheet reads top to bottom on a phone with
     no horizontal scrolling anywhere. */
  .scard{background:#fff;border:1px solid var(--line);border-radius:var(--r-card);padding:12px 14px;margin-bottom:8px;box-shadow:var(--shadow)}
  .scard .st{font-size:14.5px;font-weight:700;color:var(--navy);line-height:1.3}
  .scard .sr{display:flex;justify-content:space-between;align-items:baseline;gap:14px;padding:7px 0;border-top:1px solid var(--line)}
  .scard .st+.sr{margin-top:8px}
  .scard .sk{flex:none;font-size:11.5px;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);font-weight:600}
  .scard .sv{flex:1;min-width:0;text-align:right;font-size:13.5px;color:#22374d;font-weight:600;overflow-wrap:anywhere}
  /* Totals that belong to a section label rather than to any one row. */
  .ssum{font-size:12.5px;color:var(--muted);margin:-2px 2px 10px}
  .ssum b{color:var(--navy);font-weight:700}
  /* Two heading weights, so a spec sheet reads as organized at a glance.
     sgrp1 is the category (TILE, MILLWORK, PAINT): navy, large, on its own
     banded rule. sgrp2 is a section inside it (Interior, Master, Casita):
     smaller, brand blue, indented under its category. */
  .sgrp1{display:flex;align-items:center;gap:10px;margin:26px 0 12px;font-size:17px;font-weight:800;
    color:var(--navy);letter-spacing:-.01em;text-transform:uppercase}
  .sgrp1:first-child{margin-top:6px}
  .sgrp1::after{content:"";flex:1;height:3px;border-radius:2px;background:var(--grad);opacity:.85}
  .sgrp2{margin:16px 0 8px 2px;padding-left:10px;border-left:3px solid var(--blue);
    font-size:12px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--blue)}
  .verr{padding:28px 20px;text-align:center;color:var(--muted);font-size:14px}
  /* "Go to the Source Spreadsheet" sits above the table, full width, so the
     choice to leave for Google Sheets is obvious but never the default. */
  .vsrc{display:flex;align-items:center;justify-content:center;gap:8px;background:var(--blue);color:#fff;border:1px solid var(--blue);border-radius:var(--r-btn);padding:14px 16px;margin-bottom:14px;font-size:13.5px;font-weight:600;text-decoration:none;min-height:44px;box-shadow:var(--shadow)}
  /* A sheet's tabs and search in the viewer (v142). The table is the
     Fixtures tab's; here its header sticks to the viewer's own top. */
  .vtabs{display:flex;gap:6px;overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none;margin:0 0 10px;padding-bottom:2px}
  .vtabs::-webkit-scrollbar{display:none}
  .vtabs button{flex:none;font-family:var(--font);font-size:12.5px;font-weight:700;color:var(--navy);background:#fff;border:1px solid rgba(25,72,114,.18);border-radius:999px;padding:7px 12px;min-height:34px;cursor:pointer;white-space:nowrap;max-width:240px;overflow:hidden;text-overflow:ellipsis}
  .vtabs button.on{background:var(--navy);border-color:var(--navy);color:#fff}
  .vsheet .vq{margin-bottom:12px}
  .fxt.gen th{top:0}
  .fxt.gen td.r{text-align:right}
  .fxt.gen td a{color:var(--blue);font-weight:600;text-decoration:none;display:inline-flex;align-items:center;gap:3px}
  .vsrc:active{background:#2f6293;border-color:#2f6293}
  /* In-viewer folder browser: tapping a folder opens it here, not in Drive. */
  .vlist{padding:14px 16px}
  /* A folder row carries a soft blue tile with a folder glyph and a share
     button of its own. The bare white card with a chevron read as a filing
     cabinet; this reads as somewhere your pictures live. */
  /* No tile behind it. The glyph alone in brand blue is the accent; a filled
     chip on every row was more furniture than a folder list needs. */
  .vrow .vico{flex:none;width:26px;height:26px;display:flex;align-items:center;
    justify-content:center;color:var(--blue)}
  .vrow .vico svg{width:25px;height:25px}
  .vrow .vmeta{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
  .vrow .vsubn{font-size:11.5px;color:var(--muted);font-weight:500;letter-spacing:.02em}
  .vrow .vshare{flex:none;width:38px;height:38px;border-radius:999px;border:1px solid rgba(70,136,195,.3);
    background:#fff;color:var(--blue);display:flex;align-items:center;justify-content:center;
    cursor:pointer;line-height:0;padding:0}
  .vrow .vshare:active{background:rgba(70,136,195,.12)}
  .selecting .vrow .vshare{display:none}
  /* A white card with blue type and a light outlined folder mark. The solid
     blue fill tried before this made a page of twenty rows read as a wall of
     buttons; restraint is what makes a list of a client's own pictures look
     like a document index rather than a toolbar. */
  .vrow{display:flex;align-items:center;gap:13px;width:100%;min-height:62px;text-align:left;
    background:var(--card);border:1px solid var(--line);
    border-radius:var(--r-card);padding:11px 14px;margin-bottom:8px;font-family:var(--font);
    cursor:pointer;box-shadow:var(--shadow)}
  .vrow:hover{background:rgba(70,136,195,.06);border-color:rgba(70,136,195,.34)}
  .vrow:active{background:rgba(70,136,195,.12);border-color:var(--blue)}
  .vrow:active{background:rgba(70,136,195,.16);border-color:var(--blue)}
  /* White, because the row is solid blue. Set here rather than as an override
     further up: an earlier rule loses to this one no matter how specific it
     looks, which is how the title stayed navy-on-blue the first time. */
  /* Navy on white: the blue Thomas asked for, and 10:1 rather than the 3.77:1
     white-on-blue managed, so it is comfortably readable at 14px. */
  .vrow .vn{flex:1;min-width:0;font-size:14px;font-weight:600;color:var(--navy);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .vrow .vc{flex:none;color:var(--blue);font-size:20px;line-height:1}
  /* Photo gallery. Square thumbnails, three across on a phone, so a week of
     progress pictures is something you look at rather than a list of filenames. */
  .pgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;padding:4px 16px 20px}
  /* ---- bulk selection ---- */
  /* The mark lives in the DOM from first paint and only becomes visible in
     selection mode, so turning selection on never re-renders a grid of photos
     that has already loaded its thumbnails. */
  .selmark{display:none;position:absolute;top:8px;left:8px;width:24px;height:24px;border-radius:50%;
    background:rgba(255,255,255,.94);border:2px solid var(--navy);z-index:3;box-shadow:0 1px 4px rgba(9,32,56,.3)}
  .selecting .selmark{display:block}
  .vrow .selmark,.doc .selmark{left:auto;right:12px;top:50%;transform:translateY(-50%)}
  .selecting .sel .selmark{background:var(--blue);border-color:var(--blue)}
  .selecting .sel .selmark::after{content:'';position:absolute;left:6px;top:2px;width:6px;height:11px;
    border:solid #fff;border-width:0 2.5px 2.5px 0;transform:rotate(45deg)}
  .selecting .pcell.sel{outline:3px solid var(--blue);outline-offset:-3px}
  .selecting .vrow.sel,.selecting .doc.sel{border-color:var(--blue)}
  .selecting .dsh{display:none}
  .vrow{position:relative}
  .shead{display:flex;align-items:center;justify-content:space-between;gap:12px}
  .shead h2{margin-bottom:0}
  /* Material allowances read as ONE continuous chart. Every category and every
     line item underneath it live in the same table rather than each category
     becoming its own card, which is what made the page look like a stack of
     unrelated boxes. Built as rows rather than a <table> so the figures can
     stack on a narrow phone instead of forcing a sideways scroll. */
  .alw{margin-top:14px;border:1px solid var(--line);border-radius:var(--r-card);overflow:hidden;background:var(--card);box-shadow:var(--shadow)}
  .alwhead{display:flex;justify-content:space-between;gap:10px;align-items:baseline;
    background:var(--navy);color:#fff;padding:12px 14px}
  .alwhead .k{font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;opacity:.85}
  .alwhead .v{font-size:17px;font-weight:800;font-variant-numeric:tabular-nums}
  /* A category band: the name, its allowance, and what is left of it. */
  .alwsec{display:flex;flex-wrap:wrap;gap:6px 16px;align-items:baseline;
    background:#eaf2fb;border-top:1px solid rgba(70,136,195,.22);
    border-bottom:1px solid rgba(70,136,195,.22);padding:11px 14px}
  .alwsec .an{flex:1 1 100%;font-size:13.5px;font-weight:700;color:var(--navy)}
  .alwsec .af{display:flex;gap:6px;align-items:baseline;font-size:12.5px}
  .alwsec .af .k{color:var(--muted);text-transform:uppercase;letter-spacing:.05em;font-size:11.5px}
  .alwsec .af .v{font-weight:700;font-variant-numeric:tabular-nums;color:var(--navy)}
  .alwsec .af .v.good{color:var(--good)}
  .alwsec .af .v.over{color:var(--warn)}
  /* A line item under a category. Zebra striping is what makes a long list
     scannable, and is the whole reason this reads as a chart. */
  .alwrow{display:flex;justify-content:space-between;gap:12px;align-items:baseline;
    padding:9px 14px 9px 26px;font-size:13px;border-bottom:1px solid var(--line)}
  .alwrow:nth-of-type(even){background:#fafcfe}
  .alwrow .n{min-width:0;color:#22374d;overflow-wrap:anywhere}
  .alwrow .a{flex:none;font-weight:600;font-variant-numeric:tabular-nums;color:#22374d}
  .alwrow .n small{display:block;margin-top:2px;font-size:12px;color:var(--muted)}
  .alwrow .a small{display:block;margin-top:2px;text-align:right;font-size:11.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}
  .alwrow .a small.ok{color:var(--good)}
  .alwfoot{padding:10px 14px;font-size:12px;color:var(--muted);background:#f7fafd}
  /* Fixtures carry seven attributes per item, so a line item here is a small
     block rather than a single row: what and where in bold, the product under
     it, then only the attributes that are actually filled in. */
  .fxitem{padding:10px 14px 11px 26px;border-bottom:1px solid var(--line)}
  .fxitem:nth-of-type(even){background:#fafcfe}
  .fxn{font-size:13.5px;font-weight:700;color:var(--navy)}
  .fxp{font-size:13px;color:#22374d;margin-top:1px}
  .fxa{display:flex;flex-wrap:wrap;gap:3px 14px;margin-top:5px}
  .fxa span{font-size:11.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
  .fxa i{font-style:normal;text-transform:uppercase;letter-spacing:.05em;font-size:10.5px;opacity:.75;margin-right:4px}
  .fxa b{font-weight:600;color:#22374d}
  /* A label-only row inside a group ("Interior", "Exterior") is a sub-heading,
     not an item, and must not become another category band. */
  .fxsub{padding:9px 14px 5px 20px;font-size:11.5px;font-weight:700;letter-spacing:.06em;
    text-transform:uppercase;color:var(--blue);background:#fff}
  .selbtn{background:none;border:1px solid var(--line);color:var(--navy);font-family:var(--font);
    font-weight:700;font-size:13px;border-radius:999px;padding:7px 15px;cursor:pointer;white-space:nowrap}
  .selbtn.on{background:var(--navy);color:#fff;border-color:var(--navy)}
  .shacts{display:flex;align-items:center;gap:8px}
  .selact{display:none;border:none;border-radius:999px;width:36px;height:36px;line-height:0;
    cursor:pointer;align-items:center;justify-content:center}
  .selact.share{background:var(--blue);color:#fff;box-shadow:0 2px 8px rgba(46,107,163,.35)}
  .shacts.selecting .selact{display:inline-flex}
  /* Sharing a selection is the primary action on screen, so it stops being a
     bare glyph and becomes a real labelled button. The viewer header is already
     blue, so it takes the app's active-on-blue idiom (same as the top nav): a
     solid white pill with the blue mark. On the white Documents page the same
     action is a solid blue pill instead. */
  .vlabel{display:none}
  /* Solid navy, white type: a real blue button, the same as the Documents
     tab's. It cannot be brand blue here - the viewer header IS brand blue at
     this corner, so a #4688c3 pill vanishes into it, and the white pill this
     replaces read as washed out and disabled rather than as the primary
     action. Navy is the dark end of the same family, so it stays on-brand
     while separating cleanly from the header behind it. */
  .vact.sharing{background:var(--navy);color:#fff;border-radius:999px;width:auto;
    padding:0 15px;gap:7px;box-shadow:0 2px 10px rgba(9,32,56,.35)}
  .vact.sharing:active{background:#123a5e}
  .vact.sharing .vlabel{display:inline;font-family:var(--font);font-weight:700;font-size:14px;letter-spacing:.01em}
  .vact.sharing svg{width:18px;height:18px}
  .selact.share{background:var(--blue);color:#fff;border-radius:999px;width:auto;height:36px;
    padding:0 15px;gap:7px;box-shadow:0 2px 8px rgba(46,107,163,.35)}
  .selact.share .slabel{font-family:var(--font);font-weight:700;font-size:13.5px}
  #selBar{position:fixed;left:0;right:0;bottom:0;display:none;align-items:center;justify-content:space-between;
    gap:12px;background:var(--navy);color:#fff;padding:12px 16px;
    padding-bottom:calc(12px + env(safe-area-inset-bottom));z-index:80;box-shadow:0 -6px 20px rgba(9,32,56,.28)}
  #selBar.open{display:flex}
  #selCount{font-weight:700;font-size:14px}
  #selBar button{font-family:var(--font);font-weight:700;font-size:14px;border-radius:10px;
    padding:10px 16px;cursor:pointer;border:none}
  .selcancel{background:rgba(255,255,255,.16);color:#fff}
  /* The prepared files wait here rather than behind a toast: the send tap has
     to be deliberate and must not be racing a timer. */
  .selsend{background:#fff;color:var(--navy)}
  .selshare{background:#fff;color:var(--navy)}
  .selshare:disabled{opacity:.45}
  .pcell{position:relative;padding:0;border:1px solid var(--line);border-radius:10px;overflow:hidden;
    background:#e5ecf3;aspect-ratio:1/1;cursor:pointer;box-shadow:var(--shadow)}
  .pcell img{width:100%;height:100%;object-fit:cover;display:block}
  .pcell:active{border-color:var(--blue);opacity:.85}
  /* Document tiles (PDFs, spec sheets) are a card, not a square: a preview
     area on top, sized to actually show the page rather than a crop of it,
     and the filename in a solid label bar below — never text stamped over
     the image. The bar's background is a flat, known color, so the caption
     reads the same on every document regardless of what the scanned page
     underneath happens to look like. */
  .pcell.doc{aspect-ratio:auto;display:flex;flex-direction:column;background:#fff}
  .pcell.doc .pthumb{display:block;aspect-ratio:3/4;background:#eef3f8;overflow:hidden}
  .pcell.doc .pthumb img{width:100%;height:100%;object-fit:cover;object-position:top}
  /* Every label bar is the SAME height, three lines tall, whether the filename
     needs three lines or one. It was `min-height:2.7em`, so the bar grew with
     the text and a short name like "Winters-2nd PC" sat under a stub bar while
     the file beside it had a full one — the tiles read as inconsistent rather
     than as a set. Height is fixed here (3 lines plus the vertical padding,
     border-box) and the clamp still truncates anything longer. */
  .pcell.doc .pcap{overflow:hidden;background:var(--navy);color:#fff;font-size:13px;
    font-weight:600;line-height:1.35;letter-spacing:.01em;text-align:left;padding:8px 9px;
    height:calc(1.35em * 3 + 16px)}
  /* No -webkit-line-clamp here on purpose. It was declared on .pcap for a long
     time and never once worked: `display:-webkit-box` computes to `flow-root`
     in this engine (verified by forcing it inline in the live page), which
     leaves the clamp inert, so a long filename just ran past the bar and got
     sliced mid-line. fitDocLabels() below does the truncation by measurement
     instead, which needs no vendor display mode and behaves the same in
     WKWebView as in Chrome. */
  .pcell.doc .pcapt{display:block}
  /* Drive has no preview for this one: fill the thumbnail area with a badge
     instead of a broken image, still inside the same solid-bar treatment. */
  .pcell.doc.noimg .pthumb{display:flex;align-items:center;justify-content:center;padding:10px}
  .pcell.doc.noimg .pthumb img{display:none}
  .pdocbadge{font-size:11px;font-weight:700;letter-spacing:.08em;color:var(--muted);
    background:#e3ebf2;border-radius:6px;padding:4px 9px}
  @media(min-width:700px){.pgrid{grid-template-columns:repeat(5,1fr)}}
  /* PDF pages drawn by PDF.js. Each canvas scales to the column width, so
     pinch-zoom on the scroller magnifies an already high-resolution bitmap. */
  .pdfwrap{padding:10px 10px 24px}
  .pdfpage{position:relative;display:block;width:100%;margin:0 auto 10px;background:#fff;border-radius:6px;overflow:hidden;box-shadow:0 1px 6px rgba(20,45,70,.16)}
  .pdfpage canvas{display:block;width:100%;height:100%}

  /* The page under a light tab bar is the bar's white, on a client page and
     when an admin views it as a client. A dark sub page is #06172d, set on
     body.subview. The home indicator then matches the bar in both themes. */
  html:has(#app.on):not(:has(body.subview)),html:has(#app.on):not(:has(body.subview)) body{background-color:#fff}
  /* Flush to the screen edge. The padding is the home indicator, on the bar
     itself, so the bar's background reaches the bottom of the screen. A
     shadow of the same color continues past that edge when the web view
     leaves a gap. Where there is no inset, 4px is enough. */
  #nav{display:none;position:fixed;bottom:0;left:0;right:0;transform:none;width:100%;max-width:none;background:#fff;background-color:#fff;border-top:0;z-index:30;padding-bottom:max(env(safe-area-inset-bottom),4px);box-shadow:0 -4px 16px rgba(25,72,114,.10),0 max(env(safe-area-inset-bottom),40px) 0 0 #fff}
  /* The inset, painted again in the bar's own color. It sits inside the bar
     (bottom:0), not in a slab hanging below it. */
  #nav::after{content:"";position:absolute;left:0;right:0;bottom:0;height:env(safe-area-inset-bottom);background:#fff;pointer-events:none}
  #nav .bar{display:flex}
  /* Measured on a 360px Android: six tabs give each 60px, of which 56px is
     usable, and the word DOCUMENTS painted 67.4px. Nothing clipped it, so it
     ran under its neighbours - the overlap Thomas saw on the Galaxy. Three
     changes, in order of how much they buy:
       - the label is now a real box that ellipsizes, so overlap is impossible
         at ANY width, including phones narrower than anything measured here;
       - sentence case instead of small caps, which is both the native tab-bar
         idiom and about 15% narrower;
       - a fluid size that holds 11px on a Pixel and eases to 9px on a 320px
         handset rather than one fixed size that has to suit both.
     Deviating from the 11.5px floor in torc-app-standards is deliberate and
     measured: six upper-case labels cannot fit 320px at any legible size, and
     an ellipsis beats text running through the tab beside it. */
  #nav button{flex:1;min-width:0;background:none;border:none;padding:9px 1px 10px;
    font-size:clamp(9px,2.7vw,11px);letter-spacing:.01em;color:var(--muted);cursor:pointer;
    font-weight:600;font-family:var(--font);overflow:hidden}
  #nav button .lbl{display:block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.25}
  /* Sentence case plus a shorter word for the one tab that cannot fit. The
     full label stays in the aria-label, so a screen reader still says
     Documents. */
  #nav button[data-s="docs"] .lbl::after{content:'Docs'}
  #nav button[data-s="docs"] .lbl{font-size:0}
  #nav button[data-s="docs"] .lbl::after{font-size:clamp(9px,2.7vw,11px)}
  #nav button .ic{display:flex;justify-content:center;margin-bottom:3px}
  #nav button .ic svg{width:24px;height:24px}
  #nav button.active{color:var(--navy)}
  .navbadge{position:relative;display:inline-flex}
  /* Anchored to the ICON, not to the tab's right edge. `.ic` is a full-width
     flex container (its display:flex beats .navbadge's inline-flex on
     specificity), so a badge positioned `right:-10px` sat 10px PAST the tab
     edge - and #nav button's overflow:hidden, added to stop long labels
     colliding, then sliced it in half. Positioning from the center keeps it
     over the icon at every tab width, clipped by nothing. */
  .navbadge[data-n]:not([data-n="0"])::after{content:attr(data-n);position:absolute;top:-4px;
    left:calc(50% + 6px);right:auto;background:var(--warn);color:#fff;font-size:9px;
    border-radius:50%;width:15px;height:15px;line-height:15px;text-align:center;
    font-family:var(--font);font-weight:700}

  footer b{color:var(--navy)}

  /* ---------- DESKTOP / WEB VERSION ---------- */
  .topnav{display:none;gap:4px;margin-top:4px}
  .topnav button{background:none;border:none;color:rgba(255,255,255,.85);font-family:var(--font);font-weight:600;font-size:12.5px;letter-spacing:.06em;text-transform:uppercase;padding:6px 14px;border-radius:var(--r-btn);cursor:pointer}
  .topnav button.active{background:#fff;color:var(--navy);box-shadow:var(--btnshadow)}
  .tbadge{display:none;background:var(--warn);color:#fff;border-radius:999px;padding:1px 7px;font-size:10px;margin-left:4px}
  .tbadge.on{display:inline}
  /* Tablets: 768-899 used to fall between the phone styles (max 767) and the
     desktop styles (min 900), so an iPad or Pixel Tablet in portrait rendered
     a 430px phone column floating in dead gutters. The desktop layout starts
     at 768 now; the four money cards stay two-up until there is room. */
  @media(min-width:768px) and (max-width:999px){
    .grid2{grid-template-columns:repeat(2,1fr) !important}
  }
  /* The iPad mini reports 744pt in portrait, under the 768 desktop cut, so it
     was still rendering the 430px phone column with a floating mini footer.
     Between 600 and 767 the phone layout now fills the screen edge to edge:
     full-width frame, full-width bottom tab bar, two-up cards, wider sheets. */
  @media(min-width:600px) and (max-width:767px){
    #frame{max-width:none}
    #nav{max-width:none}
    #nav button{font-size:11.5px}
    #nav button .lbl{font-size:11.5px}
    #nav button[data-s="docs"] .lbl{font-size:11.5px}
    #nav button[data-s="docs"] .lbl::after{content:none}
    .sheet{max-width:640px;border-radius:22px 22px 0 0}
    .grid2{grid-template-columns:repeat(2,1fr)}
    .screen{padding:18px 28px 96px}
  }
  @media(min-width:768px){
    #frame{max-width:none;margin:0;min-height:100vh}
    html:not(.native) #nav{display:none !important}
    html:not(.native) .topnav{display:flex}
    /* v255: one menu on a computer, the header, for everyone. The admin
       project view used to swap it for the phone's bottom bar (html.admin-
       tools); Thomas, 2026-10-18: "When I'm logged into the client or
       subcontractor desktop view, it should all read as a header." */
    .apphead{padding:calc(6px + env(safe-area-inset-top)) 32px 6px}
    .apphead.compact{padding:calc(4px + env(safe-area-inset-top)) 32px 4px}
    .apphead img{height:24px;max-width:32%}
    .apphead .proj{font-size:13px;padding-left:14px}
    #adminbar{padding:8px 40px}
    /* width:100% matters: #app.on is a flex column, and a flex item with
       auto side margins shrinks to its content instead of stretching. That
       is why Documents and Approvals, whose rows have no intrinsic width,
       drew as a 430px phone column in the middle of a 1440px window while
       Home and Payments (wide grids) looked right. Thomas, 2026-09-30: "it
       looks like I'm viewing a mobile view from the web." */
    .screen{width:100%;max-width:1100px;margin:0 auto;padding:32px 32px 28px}
    .grid2{grid-template-columns:repeat(4,1fr);gap:14px}
    /* Documents on the web: ONE column, wider than the phone's, every row
       the same size. Thomas, 2026-09-30, after v164 drew the rows two and
       three to a row: "You made it very confusing by having a
       different-sized session bar for Gate Code and Contacts, and then you
       have two columns. I just want to have a single column and just have
       wider tabs for each folder ... relatively wide tabs in a single
       column, with the contract and proposal section, then the project
       files to follow. I love the 3D scan there as well." Same rule for a
       homeowner's project and a sub's overview: the gate code, Contacts, the
       3D scan and every folder are one stack of equal blue rows. */
    html:not(.native) #docsMyJob,html:not(.native) #docsNotes,html:not(.native) #docsContacts,html:not(.native) #scanBtn,html:not(.native) #docsList,html:not(.native) #docsMeta{width:100%;max-width:760px;margin-left:auto;margin-right:auto}
    html:not(.native) #docsList .grp{margin:18px 0 10px}
    html:not(.native) #docsList .grp:first-child{margin-top:0}
    html:not(.native) #docsList a.doc,html:not(.native) #docsList div.doc,html:not(.native) #docsContacts a.doc,html:not(.native) .docnote,html:not(.native) #scanBtn{box-sizing:border-box;min-height:62px;padding-top:15px;padding-bottom:15px;margin-bottom:10px;font-size:14.5px}
    html:not(.native) #docsMeta{margin-bottom:10px}
    /* Approvals on the web: waiting items full width (they carry a table
       and a button), finished items two to a row, the total on its own row. */
    html:not(.native) #aprCos,html:not(.native) #aprDone{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}
    html:not(.native) #aprCos .apr,html:not(.native) #aprDone .apr{margin-bottom:0}
    html:not(.native) #aprCos .cototal{grid-column:1/-1;margin:2px 0 4px}
    html:not(.native) #coFolderBtn{max-width:480px;margin:18px auto 12px !important}
    html:not(.native) .aprctl{max-width:560px}
    h2{font-size:30px}
    .sub{font-size:14px}
    #app{padding-bottom:0}
    /* Short pages keep the copyright on the bottom edge. The frame is the
       viewport column (the demo banner, when it is on, takes its own row);
       the app fills whatever is left so the copyright meets the bottom edge
       instead of sitting one banner-height past it. */
    html:not(.native) body:has(#app.on){background:#fff}
    html:not(.native) body:has(#app.on) #frame{display:flex;flex-direction:column;min-height:100dvh}
    html:not(.native) #app.on{display:flex;flex-direction:column;flex:1 0 auto;width:100%;background:var(--paper)}
    html:not(.native) #app.on .screen.active{flex:1 0 auto}
    /* The copyright follows the page. A short page still reaches the bottom
       edge because the screen grows in this column; a long page scrolls the
       line into view at the end. */
    html:not(.native) #app.on .copyline{position:static;margin:0;background:var(--paper);padding-bottom:calc(8px + env(safe-area-inset-bottom))}
    .modal{align-items:center}
    .sheet{border-radius:22px;max-width:480px}
    #toast{bottom:60px}
  }
  /* ---------- the app on a tablet ----------
     Same shell as the phone, at tablet size: header and bottom tab bar locked,
     only the screen scrolls, tab bar edge to edge with full labels. Content
     keeps the roomy tablet columns from the block above. */
  @media(min-width:768px){
    html.native body:has(#app.on){overflow:hidden;height:100dvh;overscroll-behavior:none}
    html.native #app.on{display:flex;flex-direction:column;position:fixed;inset:0;overflow:hidden;padding-bottom:0;background:var(--paper);touch-action:none}
    html.native #app.on .apphead{position:relative;top:auto;flex:none;padding:calc(12px + env(safe-area-inset-top)) 32px 6px}
    html.native #app.on .apphead.compact{padding-top:calc(12px + env(safe-area-inset-top))}
    html.native #app.on #adminbar{flex:none}
    html.native #app.on .screen.active{flex:1;min-height:0;width:100%;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;background:var(--paper);padding-bottom:120px;touch-action:pan-y}
    html.native #app.on .copyline{flex:none;margin-top:0;background:var(--paper);padding-bottom:calc(8px + env(safe-area-inset-bottom))}
    html.native #app.on:has(#nav[style*="display: block"]) .copyline,
    html.native #app.on:has(#nav[style*="display:block"]) .copyline{display:none}
    html.native body:not(.subview) #app.on:not(:has(#nav[style*="display: block"])):not(:has(#nav[style*="display:block"])){overflow-x:hidden;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;touch-action:pan-y}
    html.native body:not(.subview) #app.on:not(:has(#nav[style*="display: block"])):not(:has(#nav[style*="display:block"])) .screen.active{flex:none;min-height:0;overflow:visible;padding-bottom:16px;touch-action:auto}
    html.native #nav{max-width:none}
    html.native #nav .bar{max-width:760px;margin:0 auto}
    html.native #nav button{font-size:12.5px;padding:11px 2px 12px}
    html.native #nav button .lbl{font-size:12.5px}
    html.native #nav button[data-s="docs"] .lbl{font-size:12.5px}
    html.native #nav button[data-s="docs"] .lbl::after{content:none}
    html.native #nav button .ic svg{width:27px;height:27px}
    html.native #toast{bottom:110px}
  }
  /* The critical rule paints #app as a block so the first frame is not a
     blank page. It must not stay a block. The phone and the iPad lock the
     document and scroll the open screen, and a computer scrolls the page,
     only while this shell is a column. Same weight as that critical rule;
     this file arrives after it, so the column wins. */
  html.ready #app.on{display:flex;flex-direction:column}

  /* ---------- the computer layout (v255) ----------
     Thomas, 2026-10-18: "I'm really interested in refining the desktop view
     as well, since most professionals will be using that." Only a browser
     at 768 px and up (never the iOS app, never the Mission Control embed). */
  @media(min-width:768px){
    /* html and body both overflow-x:hidden made body its own scroll box, so
       the sticky header scrolled away with the page. clip trims the same
       sideways overflow without making a scroll box. */
    html:not(.native),html:not(.native) body{overflow-x:clip}
    html:not(.native) #app.on .apphead{position:sticky;top:0;z-index:30}
    /* .screen{overflow-x:hidden} made each screen a scroll box too, so
       nothing inside it could stay in view. */
    html:not(.native) .screen{overflow-x:clip}
    /* The subcontractor Schedule is a month calendar: it takes the full
       width like the homeowner's, so the words on the days fit. */
    html:not(.native) body.subview #s-subsched{max-width:1100px}
    /* Documents is one 760 px column; its title and Select sit over that
       column instead of out at the window's left edge. */
    html:not(.native) #s-docs>.shead,html:not(.native) #s-docs>.sub{width:100%;max-width:760px;margin-left:auto;margin-right:auto}
  }
  /* Wide windows: the header is ONE row. The logo and the job on the left,
     the six tabs, the button on the right. */
  @media(min-width:1100px){
    html:not(.native):not(.embed) .apphead{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto;align-items:center;column-gap:22px;padding:10px 32px}
    html:not(.native):not(.embed) .apphead.compact{padding:8px 32px}
    html:not(.native):not(.embed) .apphead .row{display:contents}
    html:not(.native):not(.embed) .apphead img{order:1;max-width:none}
    html:not(.native):not(.embed) .apphead .proj{order:2}
    html:not(.native):not(.embed) .apphead .topnav{order:3;margin-top:0;flex-wrap:nowrap}
    html:not(.native):not(.embed) .apphead .out{order:4;font-size:12px;padding:8px 14px}
    html:not(.native):not(.embed) .topnav button:not(.active):hover{background:rgba(255,255,255,.12);color:#fff}

    /* Home (v273). Thomas, 2026-10-31: "the latest photos are in the
       bottom right-hand corner, and it just doesn't look very uniform ...
       more cohesive." The page is a stack of full-width bands, each one a
       row of equal cells: the title; the money as a row of four; the
       progress bar; a notice (what needs him, the recap that arrived) as a
       banner with its button on the right; then Messages, This week's recap
       and Latest Photos side by side, the same height, each button on the
       same baseline. A card that is not there (no recap yet, no photos)
       gives its width to the others, so nothing ever sits alone in a
       corner. Six columns: a card takes two of them, or three when there
       are two cards. Phones and the iPad app keep their own stack. (Was
       v264: a 400 px right column that left the photos under the recap and
       a hole beside the messages.) */
    #s-home.screen.active{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));column-gap:20px;align-content:start}
    #s-home>*{grid-column:1/-1;min-width:0}
    /* The order on a computer, whatever the phone's stack says: the money
       before the progress bar, the notices after it. */
    #s-home>h2{order:1}
    #s-home>#phaseNote{order:2}
    #s-home>#homeMeta{order:3}
    #s-home>#homeCards{order:4}
    #s-home>#progCard{order:5}
    #s-home>#recapAlert{order:6}
    #s-home>#homeAlert{order:7}
    #s-home>#homeMessages{order:8}
    #s-home>#homeSide>#recapCard{order:9}
    #s-home>#homeSide>#photosCard{order:10}
    #s-home>#homeMeta{margin-bottom:8px}
    #s-home>#homeCards{grid-template-columns:repeat(4,minmax(0,1fr));gap:20px;margin:14px 0 20px}
    #s-home>#homeCards .card{padding:18px 20px}
    #s-home>#homeCards .value{font-size:24px;margin-top:6px}
    #s-home>#homeCards .note{margin-top:6px}
    #s-home>#progCard{margin:0 0 20px;padding:16px 22px;gap:20px}
    /* The notices: one band each, the words on the left and the button on
       the right, in line with them. The script paints them display:block;
       only a shown one is laid out as a band. */
    #s-home>.alert{margin:0 0 20px;padding:18px 22px}
    #s-home>.alert[style*="block"]{display:grid !important;grid-template-columns:minmax(0,1fr) auto;grid-template-areas:"t go" "d go";align-items:center;column-gap:28px}
    #s-home>.alert.ok[style*="block"]{grid-template-columns:minmax(0,1fr);grid-template-areas:"t" "d"}
    #s-home>.alert .t{grid-area:t;font-size:16px}
    #s-home>.alert .d{grid-area:d;font-size:13.5px;margin-top:3px}
    #s-home>.alert .go{grid-area:go;margin:0;white-space:nowrap}
    /* The three cards. Columns, so the button meets the bottom edge of
       every card in the row at once; the button its own width, left. */
    #s-home>#homeSide{display:contents}
    #s-home>#homeMessages,#s-home>#homeSide>.card{grid-column:span 2;margin:0 0 20px}
    #s-home>#homeMessages:empty{display:none}
    #s-home>#homeMessages .msgcard,#s-home>#homeSide>.card{display:flex;flex-direction:column;padding:18px 20px;box-sizing:border-box}
    #s-home>#homeMessages .msgcard{height:100%;margin:0}
    #s-home>#homeMessages .msgprev{margin:10px 0 18px;font-size:14.5px;line-height:1.5}
    #s-home>#homeMessages .msgcard .btn,#s-home>#homeSide>.card .btn{display:inline-flex !important;align-items:center;justify-content:center;align-self:flex-start;margin-top:auto;padding:11px 22px;flex:none}
    #s-home>#homeSide>#photosCard>div{margin-top:0 !important}
    #s-home>#homeSide>#photosCard #photosNote{margin:10px 0 18px !important;line-height:1.5}
    #s-home>#homeSide>#photosCard>div:last-child{display:flex;flex-direction:column;flex:1}
    #s-home>#homeSide>#recapCard #recapList{margin:10px 0 0}
    #s-home>#homeSide>#recapCard .recaprow{margin-top:0;background:var(--paper);box-shadow:none}
    /* Two cards, not three: each takes half. One: the whole row. */
    #s-home:has(>#homeSide>.card[style*="none"])>#homeMessages,#s-home:has(>#homeSide>.card[style*="none"])>#homeSide>.card{grid-column:span 3}
    #s-home:has(>#homeMessages:empty)>#homeSide>.card{grid-column:span 3}
    #s-home:not(:has(>#homeSide>.card:not([style*="none"])))>#homeMessages{grid-column:1/-1}
    #s-home:has(>#homeMessages:empty):has(>#homeSide>.card[style*="none"])>#homeSide>.card{grid-column:1/-1}
    /* The financials stamp and the admin's buttons close the page. */
    #s-home>#finMeta{order:11;margin-top:-8px}
    #s-home>#finMeta:empty{display:none}
    #s-home>#adminActs{order:12;margin:0 0 20px}

    /* Payments (v264). Thomas: "I would like the material allowances to be
       beneath the payment schedule." The balance and the pay button share
       one row at the top, the schedule runs the full width under them, and
       the allowances follow it, the words on the left and the figures on
       the right. */
    #s-money.screen.active{display:grid;grid-template-columns:minmax(0,1fr) 360px;column-gap:24px;grid-template-rows:repeat(8,auto) 1fr;align-content:start}
    #s-money>*{grid-column:1/-1}
    #s-money>h2{grid-row:1}
    #s-money>#payHead{grid-row:2}
    #s-money>#balCard{grid-column:1;grid-row:3/5;margin-bottom:0;display:flex;flex-direction:column;justify-content:center}
    #s-money>#payBtn{grid-column:2;grid-row:3;align-self:end;margin:0}
    #s-money>#payNote{grid-column:2;grid-row:4;align-self:start;margin:8px 0 0;text-align:left}
    #s-money>#schedPayHead{grid-row:5;margin-top:26px}
    #s-money>#payList{grid-row:6}
    #s-money>#allowHead{grid-row:7}
    /* Material allowances (v275). Thomas, 2026-10-31: "left-aligned ...
       fix the formatting." One left-aligned stack, nothing floating to the
       right: the words first, the chart the full width of the card under
       them, the tracker button its own width at the bottom left. (Was
       v264: the words in a narrow left column with the chart beside them.) */
    #s-money>#allowCard{grid-row:8;display:flex;flex-direction:column;align-items:flex-start;padding:22px 24px}
    #s-money>#allowCard>#allowIntro{font-size:14.5px;line-height:1.6;max-width:760px;color:#22374d}
    #s-money>#allowCard>#allowIntro:empty{display:none}
    #s-money>#allowCard>#allowChart{width:100%}
    #s-money>#allowCard>#allowChart .alw{margin-top:16px}
    #s-money>#allowCard>#allowChart:empty+#allowTable{margin-top:16px !important}
    #s-money>#allowCard>#allowTable{width:100%;max-width:760px}
    #s-money>#allowCard>#trackerWrap{margin-top:18px}
    #s-money>#allowCard>#trackerWrap .btn{display:inline-flex !important;align-items:center;justify-content:center;padding:11px 22px;width:auto}
    /* The chart on a computer: roomier rows, the label and the figure on
       one line. */
    #s-money>#allowCard .alwhead{padding:13px 18px}
    #s-money>#allowCard .alwsec{padding:12px 18px;gap:6px 22px}
    #s-money>#allowCard .alwsec .an{flex:1 1 auto}
    #s-money>#allowCard .alwrow{padding:10px 18px 10px 28px;font-size:13.5px}
    #s-money>#allowCard .alwfoot{padding:11px 18px}

    /* Account: two columns of cards, so a label and its value are not a
       whole window apart. */
    #s-account.screen.active{columns:2;column-gap:16px}
    #s-account>h2,#s-account>.sub{column-span:all}
    #s-account .acccol>.card{break-inside:avoid;display:inline-block;width:100%}
    /* The subcontractor's Account (v264): the photo and name across the
       top, then two fixed columns. Who they are and what they hear about
       on the left; the password, the tour, the session and the account
       itself on the right. Fixed columns, not flowing ones, so nothing
       lands where it happens to fit. */
    html:not(.native) body.subview #s-account.screen.active{columns:auto;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:20px;align-content:start}
    html:not(.native) body.subview #s-account>h2,html:not(.native) body.subview #s-account>.sub,html:not(.native) body.subview #s-account>#accPhotoBlock{grid-column:1/-1}
    html:not(.native) body.subview #s-account>#accPhotoBlock{margin:0 0 18px;padding:18px 20px;background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.12);border-radius:var(--r-card)}
    html:not(.native) body.subview #s-account .acccol{display:block;min-width:0}
    html:not(.native) body.subview #s-account>#accColA{grid-column:1}
    html:not(.native) body.subview #s-account>#accColB{grid-column:2}
    html:not(.native) body.subview #s-account .acccol>.card{display:block;margin-bottom:14px}
  }
  @media(min-width:1400px){
    html:not(.native) .screen{max-width:1200px}
    html:not(.native) body.subview #s-subsched{max-width:1200px}
  }

  /* ---------- Mission Control embed (?embed=1) ----------
     Thomas runs the estate from Mission Control now, so the admin dashboard —
     every client, every project, every overview folder — has to be readable
     there instead of behind a separate trip into this app. Embed strips only
     the chrome Mission Control already provides: the wordmark, the "Admin"
     tag, the sign-out row and the copyright line.

     The one rule that is NOT cosmetic is the tab bar. This app's #nav is
     position:fixed to the bottom on phones, and Mission Control's own tab bar
     is fixed to the same edge, so framed on a phone the two stack on top of
     each other. Embed pins the in-header .topnav on at every width and drops
     #nav entirely — one bar, and it belongs to the shell. */
  html.embed,body.embed{background:transparent}
  .embed #picker .logo,.embed #picker .tag,.embed .copyline,.embed .signout{display:none!important}
  .embed #picker{padding-top:20px}
  .embed #nav{display:none!important}
  .embed #frame{max-width:none;margin:0}
  .embed #app{padding-bottom:0}
  .embed .topnav{display:flex;flex-wrap:wrap;justify-content:center}
  /* Six uppercase tabs at full desktop size wrap to three rows in a phone-width
     frame and eat half the visible area before any project content appears.
     Tighten them where the frame is narrow. */
  @media(max-width:767px){
    .embed .topnav{gap:2px;margin-top:8px}
    .embed .topnav button{padding:7px 9px;font-size:11px;letter-spacing:.03em}
    .embed .apphead{padding-bottom:8px}
    .embed .apphead img{height:24px}
  }
  .embed .screen{padding-bottom:32px}
  /* Signed out inside the frame. Google and Apple both refuse to run their
     sign-in flows in an iframe, so the framed page never offers a form it
     cannot complete — it sends him to the portal's own window instead. */
  #embedGate.on{display:flex}
  #embedGate{display:none;position:fixed;inset:0;z-index:60;background:#f4f7fa;
    flex-direction:column;align-items:center;justify-content:center;
    gap:14px;padding:32px 24px;text-align:center;font-family:var(--font)}
  #embedGate p{color:#5b6b7c;font-size:14px;line-height:1.6;max-width:340px;margin:0}
  #embedGate a{background:linear-gradient(225deg,#4688c3 35%,#194872);color:#fff;text-decoration:none;
    padding:11px 20px;border-radius:12px;font-weight:600;font-size:13.5px}

  /* ---------- guided tour (v137) ----------
     A self-playing look at the three things a homeowner does here: approve,
     pay, read documents. A hand travels the real app and taps the real
     buttons while a caption says what is happening. Navy and blue only; amber
     stays "you need to act". Sits above everything (sign window is 80). */
  #tour{display:none;position:fixed;inset:0;z-index:90}
  #tour.on{display:block}
  #tourBlock{position:absolute;inset:0;background:transparent}
  /* The dim is four panels around the lit box (one huge box-shadow does not
     paint reliably at this size), with four corner pieces that round the
     opening. Everything moves on the same clock, so it glides as one. */
  #tourDim{position:absolute;inset:0;opacity:0;transition:opacity .3s ease;pointer-events:none}
  #tour.lit #tourDim{opacity:1}
  #tourDim b,#tourSpot{position:fixed;transition:top .5s cubic-bezier(.5,0,.15,1),left .5s cubic-bezier(.5,0,.15,1),width .5s cubic-bezier(.5,0,.15,1),height .5s cubic-bezier(.5,0,.15,1)}
  #tourDim b{display:block;background:rgba(6,20,34,.72)}
  #tour.snap #tourDim b,#tour.snap #tourSpot{transition:none}
  #tourSpot{top:50%;left:50%;width:0;height:0;border-radius:16px;pointer-events:none}
  #tourSpot i{position:absolute;width:16px;height:16px}
  #tourSpot i.c1{top:0;left:0;background:radial-gradient(circle at 100% 100%,transparent 15.5px,rgba(6,20,34,.72) 16px)}
  #tourSpot i.c2{top:0;right:0;background:radial-gradient(circle at 0 100%,transparent 15.5px,rgba(6,20,34,.72) 16px)}
  #tourSpot i.c3{bottom:0;left:0;background:radial-gradient(circle at 100% 0,transparent 15.5px,rgba(6,20,34,.72) 16px)}
  #tourSpot i.c4{bottom:0;right:0;background:radial-gradient(circle at 0 0,transparent 15.5px,rgba(6,20,34,.72) 16px)}
  #tourSpot::after{content:'';position:absolute;inset:0;border-radius:16px;border:2px solid rgba(125,190,245,.95);box-shadow:0 0 0 5px rgba(70,136,195,.26),0 0 34px rgba(70,136,195,.55);animation:tourPulse 2.4s ease-in-out infinite}
  #tourSpot.none::after,#tourSpot.none i{display:none}
  @keyframes tourPulse{0%,100%{opacity:1}50%{opacity:.5}}
  @keyframes tourRise{from{opacity:0;transform:translateY(14px) scale(.97)}to{opacity:1;transform:none}}
  @keyframes tourIn{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}
  @keyframes tourDraw{to{stroke-dashoffset:0}}
  @keyframes tourPop{from{transform:scale(.5);opacity:0}to{transform:none;opacity:1}}
  @keyframes tourRipple{from{transform:translate(-50%,-50%) scale(.2);opacity:.85}to{transform:translate(-50%,-50%) scale(1);opacity:0}}
  @keyframes tourShine{0%{transform:translateX(-130%) skewX(-20deg)}55%,100%{transform:translateX(330%) skewX(-20deg)}}
  @keyframes tourGlow{0%,100%{box-shadow:0 0 0 0 rgba(125,190,245,.0),var(--btnshadow)}50%{box-shadow:0 0 0 7px rgba(125,190,245,.45),var(--btnshadow)}}
  @keyframes tourCaret{50%{border-right-color:transparent}}

  /* The flashing circle: exactly where to tap. White and navy together, so it
     reads on a white card and on a blue button alike. */
  #tourBeacon{position:fixed;top:0;left:0;width:0;height:0;pointer-events:none;display:none}
  #tourBeacon.on{display:block}
  #tourBeacon i{position:absolute;left:-30px;top:-30px;width:60px;height:60px;border-radius:50%;border:3px solid #fff;box-shadow:0 0 0 2px #0f2c47,inset 0 0 0 2px #0f2c47;background:rgba(70,136,195,.25);animation:tourBeacon 1.5s ease-out infinite}
  #tourBeacon i+i{animation-delay:.5s}
  #tourBeacon u{position:absolute;left:-13px;top:-13px;width:26px;height:26px;border-radius:50%;background:#fff;border:4px solid #0f2c47;box-shadow:0 0 0 3px #fff,0 4px 14px rgba(3,12,22,.55);animation:tourBlink 1.5s ease-in-out infinite}
  @keyframes tourBeacon{0%{transform:scale(.4);opacity:1}85%,100%{transform:scale(1.35);opacity:0}}
  @keyframes tourBlink{0%,100%{transform:scale(1)}50%{transform:scale(.78)}}
  .tourRip{position:fixed;width:74px;height:74px;border-radius:50%;pointer-events:none;border:2px solid rgba(160,210,255,.95);background:rgba(125,190,245,.28);animation:tourRipple .6s ease-out forwards}
  .tour-press{transform:scale(.965) !important;filter:brightness(.9);transition:transform .12s ease,filter .12s ease !important}
  .screen.tour-enter > *{animation:tourIn .5s cubic-bezier(.2,.7,.2,1) both}
  .screen.tour-enter > *:nth-child(2){animation-delay:.05s}
  .screen.tour-enter > *:nth-child(3){animation-delay:.1s}
  .screen.tour-enter > *:nth-child(4){animation-delay:.15s}
  .screen.tour-enter > *:nth-child(5){animation-delay:.2s}
  .screen.tour-enter > *:nth-child(n+6){animation-delay:.25s}
  #modal.tour-sheet .sheet,#mjModal.tour-sheet .sheet{animation:tourIn .42s cubic-bezier(.2,.7,.2,1) both}
  #mConfirm.tour-live{opacity:1 !important;animation:tourGlow 1.4s ease-in-out infinite}
  #mName.tour-typing{border-right:2px solid var(--blue);animation:tourCaret .8s steps(1) infinite}

  /* The caption card: deep navy glass, white type. */
  #tourCard{position:fixed;left:16px;top:0;width:calc(100% - 32px);max-width:372px;border-radius:20px;overflow:hidden;color:#fff;
    background:linear-gradient(155deg,rgba(28,74,118,.97),rgba(10,32,54,.97));border:1px solid rgba(255,255,255,.14);
    box-shadow:0 2px 6px rgba(3,12,22,.3),0 28px 70px rgba(3,12,22,.55);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);
    font-family:var(--font);outline:none;visibility:hidden;transition:top .5s cubic-bezier(.5,0,.15,1),left .5s cubic-bezier(.5,0,.15,1)}
  #tour.snap #tourCard{transition:none}
  #tourCard.show{visibility:visible;animation:tourRise .38s cubic-bezier(.2,.7,.2,1) both}
  #tourCard .tin{padding:16px 18px 14px}
  #tourCard .tsegs{display:flex;gap:5px;margin-bottom:14px}
  #tourCard .tsegs i{flex:1;height:3px;border-radius:2px;background:rgba(255,255,255,.2);overflow:hidden}
  #tourCard .tsegs b{display:block;height:100%;width:0;background:#8cc4f5;border-radius:2px;transition:width .45s ease}
  #tourCard .twords{transition:opacity .2s ease,transform .2s ease}
  #tourCard .twords.swap{opacity:0;transform:translateY(6px)}
  #tourCard .tkick{font-size:11.5px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:#8cc4f5;margin-bottom:6px}
  #tourCard h3{font-size:22px;line-height:1.2;font-weight:800;letter-spacing:-.01em;margin:0 0 5px;color:#fff}
  #tourCard p:empty{display:none}
  #tourCard p{font-size:14.5px;line-height:1.5;color:rgba(255,255,255,.82);margin:0;text-wrap:balance}
  #tourCard .tfoot{display:flex;align-items:center;gap:8px;margin-top:10px}
  #tourCard .tskip{background:none;border:none;font-family:var(--font);font-size:13px;font-weight:600;color:rgba(255,255,255,.62);cursor:pointer;padding:12px 10px 12px 0;margin:-6px auto -6px 0}
  #tourCard .tskip:hover{color:#fff}
  #tourCard .tbtn{min-width:44px;height:44px;border-radius:22px;font-family:var(--font);font-size:12.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;cursor:pointer;border:none;display:flex;align-items:center;justify-content:center;padding:0 14px}
  #tourCard .tbtn.back{background:rgba(255,255,255,.1);color:#fff;border:1px solid rgba(255,255,255,.2)}
  #tourCard .tbtn.next{background:#fff;color:var(--navy);padding:0 20px;position:relative;overflow:hidden}
  #tourCard .tbtn[disabled]{opacity:.45;pointer-events:none}
  #tourCard .tbtn:focus-visible,#tourCard .tskip:focus-visible{outline:2px solid #8cc4f5;outline-offset:2px}
  #tourCard .tlogo,#tourCard .tchips,#tourCard .tcheck{display:none}
  /* hello + finish */
  #tourCard.hero{text-align:center;max-width:388px}
  #tourCard.hero .tin{padding:30px 24px 22px}
  #tourCard.hero .tsegs,#tourCard.hero .tkick{display:none}
  #tourCard.hero .tlogo{display:block;height:36px;width:auto;margin:0 auto 22px;animation:tourIn .6s .1s both}
  #tourCard.hero h3{font-size:26px;animation:tourIn .6s .22s both}
  #tourCard.hero p{font-size:15.5px;animation:tourIn .6s .34s both}
  #tourCard.hero .tchips{display:flex;justify-content:center;gap:10px;margin:22px 0 4px}
  #tourCard .tchips span{flex:1;max-width:104px;padding:12px 6px 10px;border-radius:14px;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.14);font-size:12px;font-weight:600;color:rgba(255,255,255,.9);animation:tourPop .5s cubic-bezier(.3,1.5,.5,1) both}
  #tourCard .tchips span:nth-child(1){animation-delay:.55s}
  #tourCard .tchips span:nth-child(2){animation-delay:.7s}
  #tourCard .tchips span:nth-child(3){animation-delay:.85s}
  #tourCard .tchips svg{display:block;width:24px;height:24px;margin:0 auto 6px;color:#8cc4f5}
  #tourCard.hero .tfoot{flex-direction:column-reverse;gap:2px;margin-top:20px;animation:tourIn .6s 1s both}
  #tourCard.hero .tskip{margin:0;padding:12px}
  #tourCard.hero .tbtn.next{width:100%;height:50px;border-radius:12px;font-size:13.5px}
  #tourCard.hero .tbtn.next::after{content:'';position:absolute;top:0;bottom:0;left:0;width:34%;background:linear-gradient(90deg,transparent,rgba(70,136,195,.35),transparent);animation:tourShine 2.8s 1.6s ease-in-out infinite}
  #tourCard.hero .tbtn.back{display:none}
  #tourCard.fin .tlogo,#tourCard.fin .tchips,#tourCard.fin .tskip{display:none}
  #tourCard.fin .tcheck{display:block;width:64px;height:64px;margin:0 auto 16px;animation:tourPop .45s cubic-bezier(.3,1.5,.5,1) both}
  #tourCard .tcheck circle{fill:rgba(140,196,245,.12);stroke:#8cc4f5;stroke-width:3;stroke-dasharray:160;stroke-dashoffset:160;animation:tourDraw .6s .15s ease-out forwards}
  #tourCard .tcheck path{fill:none;stroke:#fff;stroke-width:4.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:40;stroke-dashoffset:40;animation:tourDraw .4s .6s ease-out forwards}
  @media (prefers-reduced-motion:reduce){
    #tourDim,#tourDim b,#tourSpot,#tourCard{transition:none}
    #tourBeacon i{animation:none;opacity:0}
    #tourBeacon u{animation:none}
    #tourSpot::after,#tourCard.show,#tourCard.hero *,#tourCard.fin .tcheck,.screen.tour-enter > *,#modal.tour-sheet .sheet,#mConfirm.tour-live{animation:none !important}
    #tourCard .tcheck circle,#tourCard .tcheck path{animation:none;stroke-dashoffset:0}
  }
