/* Colour tokens for the desktop document.
 *
 * The palette below is the original eleven. Everything after it is the same
 * colours the app was already drawing, given a name so they can be changed in
 * one place: 463 hex literals across these stylesheets resolved to one of
 * these values within a stated perceptual radius. scripts/migrate-color-tokens.mjs
 * holds the table and the radii and can prove the mapping; tests/color-tokens.test.js
 * fails if a new literal duplicates a token.
 *
 * This is what atlas-themes.css is waiting on. Its three schemes all stay in
 * the same light family because a dark one would have moved twenty colours and
 * left a thousand light. Anything on a token here moves with the scheme.
 *
 * Loaded by index.html only. mobile.html and pma.html are separate documents
 * with their own :root; a var() that is not defined in the document makes the
 * whole declaration invalid and the property is silently dropped, so these
 * names must not be used in a stylesheet those pages load.
 */
:root{
--navy:#102a43;--navy2:#173f5f;--green:#8cc63f;--bg:#f3f6f8;--card:#fff;--text:#1d2732;--muted:#6a7786;--border:#d9e2e8;--danger:#b73d3d;--warning:#b67a13;--blue:#0877c7;
/* Surfaces, lightest first. Four levels that stay four levels: --card and
   --surface-1 are the two halves of a striped table, so they are close on
   purpose and collapsing them would delete the striping. */
--surface-1:#f8fbfd;--surface-2:#f0f7fd;--surface-3:#e9eff4;--surface-deep:#091c2c;--surface-deepest:#021120;
/* The loading shimmer's own two colours. Not --bg and --surface-3: those are
   neighbours on the surface ramp, and a gradient between them measured 1.06:1
   peak-to-trough on screen -- an animation that ran, swept, and could not be
   seen. Each scheme states its own pair so each can be measured once. */
--skeleton-base:#bfcddc;--skeleton-sheen:#ffffff;
/* Status washes: the tint behind a warning row, not the warning colour. */
--wash-blue:#e8f1fb;--wash-danger:#fff0ed;--wash-warning:#fffaf0;--wash-success:#eef6e7;
/* Ink. Six greys is not elegant, but they are the six in use, and rounding
   them together would lighten or darken text that someone chose. --ink-inverse
   and --ink-on-dark* are text ON a dark surface: they must stay light when the
   surfaces invert, which is why they are separate from --card. */
--ink-inverse:#fff;--ink-mid:#17364c;--ink-soft:#314b5e;--ink-steel:#587083;--ink-muted:#5b6875;--ink-faint:#718492;--ink-fainter:#7b8ea0;--ink-on-dark:#eef8ff;--ink-on-dark-soft:#9fb3c3;--on-green:#17301c;--green-ink:#587048;--blue-ink:#285c8d;
/* Lines. 35 border greys sat within dE 3.5 of --border. */
--line-strong:#cbdde6;--line-soft:#edf1f4;
/* Accents used often enough to be palette in practice. */
--blue-deep:#1976a8;--gold:#d39a34;
/* App chrome and accent fills. Same values as --navy, --blue and --blue-deep,
   separate names because those three are also spent as ink: a dark scheme has to
   send the surface half one way and the ink half the other. */
--chrome-navy:#102a43;--blue-fill:#0877c7;--blue-deep-fill:#1976a8;
/* The accent as drawn ON dark chrome. --blue is the accent on a card and is
   dark enough to disappear against the sidebar, so the two cannot be one
   token: the panel layer needs a light one. */
--accent-on-dark:#58b4ec;
/* The mobile HQ hero. Brand blue in the light schemes and its own colour in
   dark: a saturated hero over a dark page reads as an unfinished patch rather
   than as chrome. */
--hero-top:#0c79bd;--hero-bottom:#0870ac;
--shadow:0 8px 24px rgba(16,42,67,.08);--font-display:'Michroma',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;--font-body:'Public Sans',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;--chrome-emboss:linear-gradient(180deg,#3a5f85 0%,#12293f 42%,#0a1c2e 58%,#3a5f85 100%);--chrome-silver-dark:linear-gradient(180deg,#ffffff 0%,#e5e5e8 30%,#9b9b9d 50%,#d7dadd 70%,#f8f8fa 100%)}*{box-sizing:border-box}html,body{margin:0;min-height:100%;font-family:var(--font-body);background:var(--bg);color:var(--text);touch-action:manipulation}button,input{font:inherit}button{cursor:pointer}.hidden{display:none!important}
/* The letterhead picker on an estimate or an invoice. Two options, shown as
   what they are rather than as a dropdown: the choice is a look, and a name in
   a select says nothing about it. */
.lh-pick{margin:0 0 12px;padding:11px 13px;border:1px solid var(--border);border-radius:11px;background:var(--surface-1);display:grid;gap:7px}
.lh-pick legend{padding:0 5px;font-size:9px;font-weight:900;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-faint)}
.lh-pick-option{display:flex;align-items:flex-start;gap:9px;padding:8px 9px;border:1px solid transparent;border-radius:9px;cursor:pointer}
.lh-pick-option:hover{background:var(--surface-2)}
.lh-pick-option:has(input:checked){border-color:var(--blue);background:var(--wash-blue)}
.lh-pick-option input{margin-top:2px;flex:0 0 auto}
.lh-pick-option b{display:block;font-size:12.5px;color:var(--navy)}
.lh-pick-option small{display:block;margin-top:2px;font-size:10.5px;color:var(--muted);line-height:1.45}

/* The document bar on estimate and invoice detail modals. One labelled panel
   for everything that produces the customer's copy -- letterhead choice, then
   print, email, text, save -- so the modal's foot is a workflow rather than a
   drawer of loose buttons. Manage actions (edit, record payment, delete) live
   on their own row below it. */
.atlas-docbar{margin-top:12px;padding:12px 13px;border:1px solid var(--border);border-radius:12px;background:var(--surface-1);display:grid;gap:10px}
.atlas-docbar-head{font-size:8px;font-weight:900;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-faint)}
/* A withdrawn signature must not read like a live one at a glance. The figures
   stay legible -- they are the record of what was agreed -- but the block is
   struck amber and its heading says so, because the one mistake to prevent is
   an office reading "Signed by the customer" off a document that was re-priced
   after they signed it. */
.atlas-client-decision-void{border:1px solid color-mix(in srgb,var(--warning) 42%,var(--border));background:var(--wash-warning);border-radius:12px;padding:12px 13px}
.atlas-client-decision-void .atlas-docbar-head{color:var(--warning)}
.atlas-client-decision-void .restore-detail-grid b{color:color-mix(in srgb,var(--warning) 70%,var(--text));text-decoration:line-through}
.atlas-client-decision-void .atlas-docbar-msg{color:var(--warning)}
.atlas-docbar .lh-pick{margin:0;padding:0;border:0;background:transparent;display:flex;flex-wrap:wrap;gap:8px}
.atlas-docbar .lh-pick legend{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
.atlas-docbar .lh-pick-option{flex:1 1 150px;align-items:center;border:1px solid var(--border);padding:7px 10px}
.atlas-docbar .lh-pick-option small{display:none}
.atlas-docbar-buttons{display:flex;flex-wrap:wrap;gap:8px}
.atlas-docbar-buttons .btn{min-height:38px}
.atlas-docbar-msg{display:block;min-height:14px;color:var(--muted);font-size:10px}
.atlas-docbar+.restore-actions{margin-top:10px}

/* The topbar carried backdrop-filter: saturate(180%) blur(20px) while its own
   background is a fully opaque gradient (#102a43 -> #091c2c). A backdrop blur
   behind an opaque surface is invisible by definition -- there is nothing to
   see through it -- so the filter bought nothing and cost a compositing layer
   on a position:sticky element, every frame.
   
   What it did produce was a white artifact across the bar: visible on screen,
   absent from screenshots, which is the signature of a GPU compositing seam
   rather than anything in the DOM. The bar's background-color was transparent
   (the opacity came only from the gradient image), so wherever the composited
   layer did not paint the gradient, the blurred light page behind it showed
   through white.
   
   The filter is gone and the bar now has an opaque background-color under the
   gradient, so there is no transparent base for anything to bleed through.
   The translucent surfaces elsewhere -- .mobile-topbar at 96% white,
   .restore-modal-head -- keep their blurs, which do real work. */
/* Loading skeleton.
 *
 * One implementation. There were three near-copies -- .locations-loading,
 * .customer-loading and .customer-location-skeleton -- with three animation
 * names, three bar heights and two hard-coded greys, so a page that got a
 * loading state got whichever one its author had seen. Every other page got a
 * line of text, and on a slow list that reads as a screen that has finished and
 * is empty.
 *
 * Drawn from tokens so it follows the colour scheme, and still when the viewer
 * has asked for less motion: a shimmer is decoration, and the thing it has to
 * communicate -- rows are coming -- is carried by the bars, not the movement.
 */
.atlas-skeleton{display:grid;gap:8px;padding:18px}
.atlas-skeleton>span{display:block;height:54px;border-radius:10px;background:linear-gradient(90deg,var(--skeleton-base) 45%,var(--skeleton-sheen) 50%,var(--skeleton-base) 55%);background-size:250% 100%;animation:atlasSkeletonPulse 1.2s linear infinite}
.atlas-skeleton[data-rows="compact"]>span{height:38px}
.atlas-skeleton[data-rows="tall"]>span{height:76px}
@keyframes atlasSkeletonPulse{0%{background-position:100% 0}100%{background-position:0 0}}
@media(prefers-reduced-motion:reduce){.atlas-skeleton>span{animation:none;background:var(--skeleton-base)}}
button:disabled,input:disabled{cursor:default}.auth-screen{position:fixed;inset:0;z-index:100;background:linear-gradient(180deg,var(--chrome-navy),var(--surface-deep));display:grid;place-items:center;padding:24px}.auth-card{width:min(460px,100%);background:#0d263a;border:1px solid #2b5069;border-radius:22px;padding:28px;color:var(--ink-on-dark);box-shadow:0 30px 80px rgba(0,0,0,.35)}.auth-brand{display:flex;align-items:center;gap:13px}.auth-brand-mark{width:52px;height:52px;border-radius:13px;background:var(--navy2);border:1px solid #4d7897;display:grid;place-items:center;color:#dff5ff;font-size:26px;font-weight:900}.auth-brand h1{margin:0;font-size:26px;line-height:1.2;font-family:var(--font-display);font-weight:400;letter-spacing:.008em;color:#f4f6f8;background:var(--chrome-silver-dark);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;filter:drop-shadow(0 1px 0 rgba(0,0,0,.4)) drop-shadow(0 -1px 0 rgba(255,255,255,.25))}.auth-brand small{display:block;margin-top:5px;color:var(--ink-on-dark-soft);text-transform:uppercase;letter-spacing:.1em}.auth-card h2{margin:26px 0 7px;font-size:24px}.auth-copy{margin:0 0 20px;color:#b3c7d3;line-height:1.5}.auth-form{display:grid;gap:15px}.auth-form label{display:grid;gap:7px;font-size:13px;font-weight:800}.auth-form input{width:100%;padding:14px;border-radius:10px;border:1px solid #7a94a5;background:var(--card);color:#102333;-webkit-text-fill-color:#102333;font-size:17px}.auth-submit,.primary{border:1px solid var(--green);border-radius:9px;background:var(--green);color:var(--on-green);padding:11px 15px;font-weight:850}.secondary{border:1px solid #66869a;border-radius:9px;background:transparent;color:inherit;padding:11px 15px;font-weight:800}.auth-message{min-height:21px;margin-top:10px;color:#ffc270;font-size:13px}.auth-link{display:block;width:100%;margin-top:11px;padding:6px 0;border:0;background:transparent;color:#cfe3f5;font:inherit;font-size:12.5px;text-decoration:underline;text-underline-offset:3px;cursor:pointer;text-align:center}.auth-link:hover{color:var(--ink-inverse)}.auth-link:disabled{opacity:.55;cursor:default}.auth-link:focus-visible{outline:2px solid #8ec5f0;outline-offset:2px;border-radius:6px}.auth-foot{margin-top:18px;padding-top:15px;border-top:1px solid #24485f;color:#8fa9b8;font-size:12px;line-height:1.45}.app{display:flex;min-height:100vh}.sidebar{width:250px;position:fixed;inset:0 auto 0 0;background:linear-gradient(180deg,var(--chrome-navy),var(--surface-deep));color:var(--ink-inverse);padding:20px 15px;z-index:60;display:flex;flex-direction:column}.brand{display:flex;align-items:center;padding:6px 8px 20px}.brand-wordmark{display:grid;line-height:1}.brand-wordmark b{font-size:27px;letter-spacing:.09em;color:var(--ink-inverse)}.brand-wordmark small{margin-top:5px;font-size:9px;letter-spacing:.24em;color:var(--ink-on-dark-soft)}.nav-label{font-size:10px;font-weight:800;letter-spacing:.14em;color:var(--ink-fainter);padding:0 12px 8px}.nav{overflow:auto;min-height:0;overscroll-behavior:contain}
.sidebar{overscroll-behavior:contain}/* The How-To Guide sits in the nav as a link rather than a page button, so
   it survives a script failure. It has to look like its neighbours, which
   is why every nav rule addresses both. */
.nav button,.nav a{display:flex;align-items:center;gap:10px;width:100%;text-align:left;border:0;background:transparent;color:#d6e1ea;padding:11px 13px;border-radius:9px;margin:3px 0;font-weight:650;text-decoration:none;box-sizing:border-box}.nav button.active,.nav button:hover,.nav a:hover{background:color-mix(in srgb,var(--green) 16%,transparent);color:var(--ink-inverse)}.nav button.active{box-shadow:inset 3px 0 var(--green)}.nav-icon{width:20px;text-align:center;font-size:16px;color:#b9cad6}.sidebar-foot{margin-top:auto;padding:14px 9px 0;border-top:1px solid rgba(255,255,255,.12);color:#7894a6;font-size:11px;line-height:1.5}.sidebar-status{display:flex;gap:7px;align-items:center;margin-bottom:4px}.status-dot{width:7px;height:7px;border-radius:50%;background:var(--green);box-shadow:0 0 0 3px color-mix(in srgb,var(--green) 12%,transparent)}.main{margin-left:250px;flex:1;min-width:0;background:linear-gradient(180deg,var(--surface-1) 0%,#e3e9ee 55%,var(--bg) 100%);min-height:100vh}.topbar{min-height:76px;background-color:var(--chrome-navy);background-image:linear-gradient(180deg,var(--chrome-navy),var(--surface-deep));background-size:100% 100vh;background-repeat:no-repeat;background-position:0 0;border-bottom:1px solid rgba(255,255,255,.08);display:flex;align-items:center;justify-content:space-between;padding:12px 28px;gap:14px;position:sticky;top:0;z-index:30}.top-left{display:flex;align-items:center;gap:11px;flex:1 1 auto;min-width:max-content}.top-left>div{min-width:0}.topbar h2{margin:0;color:#f4f6f8;font-size:20px;font-family:var(--font-display);font-weight:400;letter-spacing:.01em;background:var(--chrome-silver-dark);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;filter:drop-shadow(0 1px 0 rgba(255,255,255,.25)) drop-shadow(0 1px 2px rgba(0,0,0,.5));white-space:nowrap}.topbar .global-search{border-color:rgba(255,255,255,.2)}.topbar .btn,.topbar .icon-btn,.topbar .mobile-menu-btn{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.25);color:var(--ink-inverse)}.topbar .btn:hover,.topbar .icon-btn:hover{background:rgba(255,255,255,.16)}.topbar .signout-btn{color:#ff8a80}.topbar .account-avatar{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.22);color:var(--ink-inverse)}.topbar .account-block b{color:var(--ink-inverse)}.topbar .account-block small{color:var(--ink-on-dark-soft)}.eyebrow{font-size:10px;font-weight:900;letter-spacing:.13em;color:var(--ink-steel)}.topbar .eyebrow{display:none}.top-tools{display:flex;align-items:center;gap:9px;flex:0 1 auto;min-width:0}.top-tools .btn{height:40px;box-sizing:border-box;display:inline-flex;align-items:center}
/* The tools rail is a fixed-height rail, not a box that grows to fit whatever
   has loaded into it. Five modules restructure this bar over the first two
   seconds -- the desktop search is swapped for the global one, the Today chip
   fills in its date and clock, the create control arrives -- and every one of
   those changed the rail's height, which changed the topbar's height, which
   slid the entire page down and then back up. Measured at boot: the rail went
   40 -> 54 -> 56 -> 50px, the topbar 76 -> 79 -> 81 -> 76px, and the content
   under it moved four times, the last jerk two full seconds in. That is what
   "the screen glitches loading in" was. Pinned to the settled height, the bar
   is 76px from first paint and nothing injected into it can move the page. */
@media(min-width:781px){}
/* Dispatch panel slots. flow-root so an injected panel's bottom margin is
   contained rather than collapsing out through the slot -- without it the slot
   measures 92px while occupying 104, and the reserve is short by the margin
   on every panel. */
.dispatch-slot{display:flow-root}.global-search{display:flex;align-items:center;border:1px solid var(--border);border-radius:9px;background:var(--card);overflow:hidden;height:40px;flex:0 1 300px;min-width:180px}.global-search>span{padding-left:11px;color:#60788a}.global-search input{border:0;outline:0;padding:9px 8px;width:100%;min-width:0;color:var(--ink-steel);background:var(--card)}.global-search button{height:100%;border:0;border-left:1px solid var(--border);background:var(--surface-1);color:var(--muted);font-weight:700;padding:0 12px}.btn,.icon-btn,.mobile-menu-btn{border:1px solid var(--border);background:var(--card);padding:9px 13px;border-radius:9px;font-weight:700;color:var(--navy)}.btn.primary{background:var(--green);border-color:var(--green);color:var(--on-green)}.signout-btn{color:var(--danger)}.icon-btn{width:40px;height:40px;padding:0;display:grid;place-items:center}.tech-mode-btn{white-space:nowrap}.create-btn{width:40px;height:40px;padding:0;font-size:22px;line-height:1}.account-block{display:flex;align-items:center;gap:8px;height:40px;padding:0 12px;border:1px solid var(--border);border-radius:9px;background:var(--card);box-sizing:border-box}.account-avatar{width:34px;height:34px;border-radius:50%;background:var(--surface-3);border:1px solid var(--line-strong);color:var(--navy);display:grid;place-items:center;font-weight:900}.account-block b{display:block;font-size:12px;color:var(--navy);max-width:120px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.account-block small{display:block;margin-top:2px;font-size:9px;color:var(--ink-faint);text-transform:uppercase}body[data-aal="aal2"] #verifyProtected{display:none}.mobile-menu-btn{display:none}.content{padding:24px 28px 40px;max-width:1700px;margin:0 auto}.page{display:none}.page.active{display:block}.dash-heading,.section-heading{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;margin-bottom:16px}.dash-heading h1,.section-heading h1{margin:0;color:var(--navy);font-size:25px;font-family:var(--font-display);font-weight:400;letter-spacing:.008em;background:var(--chrome-emboss);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;filter:drop-shadow(0 -1px 0 rgba(255,255,255,.55)) drop-shadow(0 1.5px 1px rgba(10,25,40,.3))}.dash-heading p,.section-heading p{margin:5px 0 0;color:var(--muted);font-size:13px}.welcome-line{font-size:12px;color:var(--muted);font-weight:700}.metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:15px;margin-bottom:15px}.card{background:linear-gradient(160deg,var(--card) 0%,var(--surface-3) 100%);border:1px solid var(--border);border-radius:14px;box-shadow:var(--shadow),inset 0 1px 0 #fff,inset 0 -10px 16px -12px rgba(16,42,67,.14);padding:18px;min-width:0}.metric{position:relative;overflow:hidden}.metric:before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:#83a6bd}.metric-blue:before{background:#1682c4}.metric-green:before{background:var(--green)}.metric-gold:before{background:var(--gold)}.metric-purple:before{background:#8269b7}.metric-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}.metric-icon{width:30px;height:30px;border-radius:9px;background:linear-gradient(145deg,var(--wash-blue),#cfe3ef);display:grid;place-items:center;color:var(--navy);font-weight:900}.metric-trend{font-size:9px;font-weight:900;letter-spacing:.08em;color:#8296a5}.metric .label{font-size:11px;text-transform:uppercase;color:var(--muted);letter-spacing:.05em;font-weight:800}.metric .value{font-size:26px;font-weight:400;color:var(--navy);margin-top:5px;font-family:var(--font-display);letter-spacing:0;font-variant-numeric:tabular-nums}.metric-note{font-size:11px;color:#8495a2;margin-top:5px}.secondary-metrics .compact{padding:14px 16px}.secondary-metrics .compact .value{font-size:24px}.dashboard-layout{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(310px,.8fr);gap:15px}.dashboard-main,.dashboard-side{display:grid;gap:15px;align-content:start}.dash-card{padding:0;overflow:hidden}.card-head>div{min-width:0}.card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:16px 18px;border-bottom:1px solid var(--border)}.card-head h3{margin:0;color:var(--navy);font-size:14px;font-family:var(--font-display);font-weight:400;letter-spacing:.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}.card-head p{margin:4px 0 0;color:var(--muted);font-size:11px}.link-btn{border:0;background:transparent;color:var(--blue);font-size:12px;font-weight:800;white-space:nowrap}.empty-state{padding:30px 18px;color:#7c8d99;font-size:13px;text-align:center}.attention-item{display:flex;gap:10px;padding:17px 18px;align-items:flex-start}.attention-dot{width:9px;height:9px;border-radius:50%;background:var(--gold);margin-top:4px;flex:0 0 auto}.attention-item b{display:block;color:var(--navy);font-size:13px}.attention-item small{display:block;color:var(--muted);font-size:11px;margin-top:3px;line-height:1.4}.quick-grid{display:grid;grid-template-columns:1fr 1fr;gap:9px;padding:14px}.quick-grid button{border:1px solid var(--border);background:var(--card);border-radius:10px;padding:13px;text-align:left;color:var(--navy)}.quick-grid button b{display:block;font-size:19px;color:var(--blue)}.quick-grid button span{display:block;margin-top:5px;font-size:11px;font-weight:700}.security-card>p{padding:16px 18px 10px;margin:0;color:var(--muted);font-size:12px;line-height:1.5}.security-card>.btn{margin:0 18px 18px}.module-placeholder{padding:28px}.module-placeholder h3{margin:0 0 5px;color:var(--navy)}.module-placeholder p{margin:0;color:var(--muted);line-height:1.5}.module-placeholder .btn{margin-top:15px}.drawer-backdrop{display:none}.notice{background:var(--wash-success);border-left:4px solid var(--green);padding:12px;border-radius:8px;color:#38512b;font-size:13px}@media(max-width:1180px){.global-search{min-width:180px}.global-search input{width:100%}.account-block{display:none}.metrics{gap:12px}.content{padding-left:20px;padding-right:20px}}@media(max-width:980px){.sidebar{width:220px}.main{margin-left:220px}.desktop-search{display:none}.metrics{grid-template-columns:1fr 1fr}.dashboard-layout{grid-template-columns:1fr}.content{padding:20px}.topbar{padding-left:20px;padding-right:20px}}@media(max-width:780px){.main{margin-left:0}.drawer-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.48);z-index:50}body.nav-open .drawer-backdrop{display:block}.mobile-menu-btn{display:grid;place-items:center;width:40px;height:40px;padding:0}.topbar{min-height:68px;padding:10px 14px}.topbar h2{font-size:18px}.eyebrow{font-size:8px}.desktop-only{display:none!important}.content{padding:16px 13px 28px}.dash-heading,.section-heading{align-items:flex-start}.dash-heading h1,.section-heading h1{font-size:23px}.welcome-line{display:none}.metrics{grid-template-columns:1fr 1fr;gap:10px}.card{border-radius:12px}.metric{padding:14px}.metric .value{font-size:24px}.metric-note{font-size:10px}.dashboard-layout{gap:10px}.dashboard-main,.dashboard-side{gap:10px}.auth-screen{display:block;padding:calc(26px + env(safe-area-inset-top)) 19px}.auth-card{padding:0;border:0;background:transparent;box-shadow:none}.auth-brand{padding-top:10px}.signout-btn{font-size:12px;padding:8px 10px}.create-btn{display:none}.section-heading>.primary{font-size:12px;padding:8px 10px}}@media(max-width:480px){.metrics{grid-template-columns:1fr 1fr}.metric{min-height:128px}.metric .label{font-size:10px}.metric .value{font-size:22px}.secondary-metrics .compact{min-height:auto}.secondary-metrics .compact .value{font-size:21px}.quick-grid{grid-template-columns:1fr 1fr;padding:12px}.card-head{padding:14px}.empty-state{padding:24px 14px}.content{padding-left:11px;padding-right:11px}}
/* Atlas A mark: sidebar brand lockup + narrow-width menu button */
.sidebar .brand{gap:11px}
.sidebar .brand-a-mark{display:block;width:52px;height:52px;object-fit:contain;flex:0 0 52px}
.mobile-menu-btn .menu-a-mark{display:block;width:30px;height:30px;object-fit:contain;pointer-events:none}

/* Right-hand record drawers are injected by modules whose stylesheets load
   asynchronously, so the markup lands before its own closed state exists. The
   drawer then lays out on screen and, because the arriving rule carries both
   the off-canvas transform and a transition for it, visibly slides away rather
   than never being seen at all. Declaring the closed state here -- in the
   render-blocking stylesheet, with no transition -- means the drawer is already
   off-canvas at first layout and the module rule changes nothing. The .open
   rules are two classes, so they still win. */
.equipment-drawer,.location-drawer{position:fixed;top:0;right:0;height:100dvh;transform:translateX(104%)}

/* The sidebar is set in the platform's own UI font rather than Public Sans --
   Segoe UI on Windows, SF Pro on macOS. The stack is copied verbatim from the
   release-notes page it was asked to match, so the two render identically
   rather than merely similarly.

   ui-sans-serif is a CSS Fonts 4 generic that Chromium may not resolve. That is
   safe here: an unrecognised keyword is parsed as an ordinary family name, not
   as a syntax error, so the declaration survives and simply falls through to
   the next entry. Verified in Chromium 141 -- the declaration is kept, and
   "Segoe UI" is reached on Windows by either path.

   The nav label and the foot are included deliberately. They sit in the same
   250px column as the items, and leaving them in Public Sans reads as a bug
   rather than as a choice. */
:root{--font-ui:ui-sans-serif,-apple-system,"Segoe UI",Helvetica,Arial,sans-serif}
.sidebar .nav-label,.sidebar .nav button,.sidebar-foot{font-family:var(--font-ui)}

/* HQ arrives about half a second after first paint, and until it did, the
   pre-HQ dashboard was on screen: a different heading, different cards, a
   different layout. The screen changed from one dashboard into another on every
   load, which is what "HQ loads two separate UIs" was.

   That markup cannot simply be deleted -- atlas-dashboard-sophisticated.js reads
   the old tiles to seed the new ones before replacing them -- so it is hidden
   until HQ has rendered instead, and a small placeholder holds the space.

   Declared here, in the render-blocking stylesheet, for the same reason the nav
   dropdown is: a rule that arrives with a module cannot hide something that has
   already been painted.

   :has() is the fallback. If HQ never renders -- the module fails, the manifest
   stalls -- the section keeps data-dashboard-pending forever, and a dashboard
   that stays blank is worse than an old one. The moment anything HQ builds is
   present the pending state stops applying, and the plain [data-sophisticated]
   rule below covers browsers without :has(). */
#page-dashboard[data-dashboard-pending]:not(:has(.atlas-office-inbox))>*{display:none}
#page-dashboard[data-dashboard-pending]:not(:has(.atlas-office-inbox))>.dashboard-boot{display:flex}
#page-dashboard[data-sophisticated]>.dashboard-boot{display:none}
.dashboard-boot{display:none;flex-direction:column;gap:14px;padding:2px 0}
.dashboard-boot>span{display:block;border-radius:15px;border:1px solid var(--border);
  background:linear-gradient(180deg,var(--surface-1),var(--surface-3));opacity:.5}
.dashboard-boot>span:nth-child(1){height:118px}
.dashboard-boot>span:nth-child(2){height:96px}
.dashboard-boot>span:nth-child(3){height:150px}
/* It is a held space, not a spinner: half a second of pulsing is more movement
   than the wait deserves, and it disappears before an animation would finish. */
@media(prefers-reduced-motion:no-preference){
  .dashboard-boot>span{transition:opacity .2s ease}
}

/* atlas-restoration-admin.js gates six nav entries on permissions by setting
   the hidden property -- dispatch, history, estimates, inventory, pricebook and
   reports. It never worked. `.nav button{display:flex}` is an author rule and
   [hidden]{display:none} is a user-agent rule, so the author rule wins and all
   six render, at full height, fully clickable, for every role. Nothing errors;
   the gate simply has no effect. Found while measuring the panel below, which
   is where those entries are now listed. */
.sidebar .nav button[hidden],.nav button[hidden]{display:none!important}

/* Mobile navigation: a dropdown from the A mark, not a drawer off the left edge.
   The header used to spend 190px of a 412px-wide phone on the ATLAS wordmark,
   with the wordmark itself acting as the menu control. The A mark says the same
   thing in 42px, which is what lets the bar come down from 71px to 58px.

   --atlas-mobile-header is the single source of truth for that height: the bar
   is sized to it here and the panel hangs from it, so the two cannot drift.
   atlas-mobile-global-search.js sizes the search shell to match.

   The closed state is declared HERE, in the render-blocking stylesheet, and not
   in either module that styles this panel at runtime. That is the whole lesson
   of the equipment drawer directly above: a panel whose markup arrives before
   its own closed state lays out on screen first, and a transition arriving with
   that state animates it away in full view instead of it never being seen. The
   panel is already collapsed and invisible at first layout; body.nav-open is
   two selectors, so opening still wins. */
/* Defined unconditionally, not inside the media query that spends it. A custom
   property declared only inside @media resolves to nothing everywhere else, and
   a var() that resolves to nothing makes the whole declaration invalid -- the
   property is dropped silently rather than falling back. Nothing outside the
   phone breakpoint spends this, but the token has to exist for the one that
   does to survive. browser-checks/color-tokens.check.js catches exactly this. */
:root{--atlas-mobile-header:58px}
@media(max-width:780px){
  .topbar{min-height:var(--atlas-mobile-header);padding:7px 12px}

  .mobile-menu-btn{width:42px;height:42px;border-radius:12px}
  .mobile-menu-btn .menu-a-mark{width:30px;height:30px}
  /* The button is the panel's anchor, so it stays lit while the panel is open. */
  body.nav-open .mobile-menu-btn{background:rgba(255,255,255,.2)!important;border-color:rgba(158,219,59,.5)!important}

  .sidebar{
    /* +1px is the topbar's border-bottom, which sits outside min-height. Without
       it the panel hangs a hairline below the bar it is supposed to grow out of. */
    top:calc(var(--atlas-mobile-header) + 1px);
    bottom:auto;
    left:12px;
    right:auto;
    width:min(304px,calc(100vw - 24px));
    max-height:calc(100dvh - var(--atlas-mobile-header) - 17px);
    border:1px solid rgba(140,190,225,.22);
    border-top:0;
    border-radius:0 0 18px 18px;
    box-shadow:0 26px 60px rgba(0,0,0,.45);
    /* Origin sits over the button's centre (12px inset + half of 42px), so the
       panel unfolds from the mark rather than from the corner of the screen. */
    transform-origin:33px top;
    opacity:0;
    transform:translateY(-10px) scaleY(.92);
    visibility:hidden;
    transition:opacity .16s ease,transform .22s cubic-bezier(.2,.9,.3,1),visibility 0s linear .22s;
  }
  body.nav-open .sidebar{
    opacity:1;
    transform:none;
    visibility:visible;
    transition:opacity .16s ease,transform .22s cubic-bezier(.2,.9,.3,1),visibility 0s;
  }
  /* A dropdown dims less than a full-height drawer: the page behind it is still
     the subject, and the header stays above the backdrop either way. */
  .drawer-backdrop{background:rgba(4,16,26,.34)}
}
@media(max-width:560px){
  :root{--atlas-mobile-header:56px}
  .topbar{padding:7px 10px}
  .sidebar{left:10px;width:min(300px,calc(100vw - 20px));transform-origin:31px top}
}
/* Motion is the affordance, not the information: without it the panel still
   appears and disappears, it simply does not travel. */
@media(max-width:780px) and (prefers-reduced-motion:reduce){
  .sidebar,body.nav-open .sidebar{transition:none;transform:none}
}

/* Number fields carry no spinner anywhere in Atlas.
   The up/down arrows sit inside a right-aligned money column, so they land on
   top of the digits they are meant to nudge: on the estimate sheet the cost
   cell reads "0 ⌃⌄" with the arrows over the number, and every price column
   in the app has the same problem. A quantity or a rate is typed, not stepped
   -- nobody arrows their way from 0 to 14200 -- so the control comes off and
   the field is a plain right-aligned box.
   Firefox needs the property; WebKit needs the pseudo-elements. Both, or it
   comes back in one browser and nobody notices. */
input[type=number]{-moz-appearance:textfield;appearance:textfield}
input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;appearance:none;margin:0}

/* Form controls, the base layer.
   Atlas styled its buttons and never styled its fields. In the same estimates
   toolbar the button was 38px tall with a 9px radius and 9px 13px of padding,
   while the search input beside it was 24px tall, square, padded 1px 2px and
   drawn with `border:2px inset` -- Chromium's 3D bevel, the one Windows 95
   shipped. The select was 21px tall with no padding at all. Nothing was
   overriding the design; there was simply no rule, on 12 controls across 8 of
   the app's pages -- 3 inputs wearing the bevel and 9 selects Chromium pads
   0px -- and the browser default is what showed.

   Deliberately one element selector wide (0,0,1). `:where()` contributes no
   specificity, so the type exclusions below cost nothing and every existing
   rule still outranks this: `.lh-editor input` keeps the estimate sheet's
   transparent 6px fields, `.pma-check-fields input` keeps its 9px ones, and
   the topbar search stays borderless on its `!important`. This is the floor,
   not an override -- do not add `!important` to it.

   The excluded types are the ones with their own rendering: a checkbox or a
   radio given a 10px radius and 11px of padding is not a rounder checkbox, it
   is a broken one, and file/colour/range inputs draw their own furniture.
   Buttons come in through .btn; the button-like input types are listed so a
   stray `<input type=submit>` matches them rather than the text fields.

   --card/--navy is the pair .btn already spends, so these track the dark
   scheme (`--card:#16232e`, `--navy:#eaf1f8`) instead of staying white-on-
   white when it flips. */
input:where(:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]):not([type=image]):not([type=submit]):not([type=reset]):not([type=button])),
select,
textarea{
  border:1px solid var(--border);
  border-radius:10px;
  padding:9px 11px;
  background:var(--card);
  color:var(--navy);
  font:inherit;
}
/* Only `font:inherit` above reaches a textarea's monospace default; the rest
   of this is shape. Keep the resize handle -- it is the one browser default on
   a textarea that is an affordance rather than an artefact. */

/* A disabled field has to read as disabled. The default grey came with the
   default border, and both left together. */
:where(input,select,textarea):disabled{
  background:var(--surface-1);
  color:var(--ink-muted);
  cursor:not-allowed;
}
