/* ============================================================
   FlyHi Sourcing — dark theme
   Every colour is a token. Reskinning means editing :root, not
   hunting through 130 rules.
   ============================================================ */
:root{
  /* --- brand ---
     FlyHi navy #0668A2. The three derived values exist because one hex
     can't do every job on a dark background:
       --navy      fills (white label on top clears 5.9:1)
       --navy-lift hover, one step brighter
       --navy-ink  navy as TEXT or hairline — the brand hex itself only
                   hits 3.2:1 on #131210, this tint reaches 6.8:1
       --navy-dim  translucent wash for tinted chips */
  --navy:#0668A2;
  --navy-lift:#0a7fc4;
  --navy-ink:#4fa3da;
  --navy-dim:rgba(6,104,162,.24);

  /* --- surfaces, darkest to lightest ---
     The Material dark ladder — #121212 and its elevation steps — carrying
     a trace of warmth rather than none. Chroma here is 0.004, about a
     fifth of the blue cast this replaced: enough that the greys don't read
     cold, far too little to read as brown. Neutral is the target; the
     warmth is a lean, not a colour.

     Material's steps sit slightly lighter than the old ramp at every
     level, which costs contrast against text — so the inks below were
     re-solved against these values rather than carried over. */
  --bg:#131210;
  --panel:#201e1c;
  --raise:#262322;
  --line:#353331;
  --line-2:#292725;

  /* --- text ---
     Lightness solved so every ink/surface pair holds the ratio it had on
     the original ramp, against surfaces that are now lighter. Chroma is
     0.0032 — a hair under the surfaces', so text never reads warmer than
     the panel it sits on. */
  --ink:#f2f1ef;
  --ink-2:#abaaa8;
  --ink-3:#767573;

  --warn:#e2a06a;
  --warn-bg:rgba(226,160,106,.12);
  --ok:#5fc79a;
  --ok-bg:rgba(95,199,154,.10);

  --radius:14px;
  --topbar-h:56px;
  --sans:"Miranda Sans","Inter",system-ui,-apple-system,"Segoe UI",sans-serif;
  --display:"Stack Sans Headline","Miranda Sans",system-ui,sans-serif;
  --mono:ui-monospace,"SF Mono",Menlo,monospace;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
/* Wide content scrolls inside its own box; the page itself never does. */
html{overflow-x:hidden}
body{
  background:var(--bg);color:var(--ink);
  font-family:var(--sans);font-size:14px;line-height:1.5;
  -webkit-font-smoothing:antialiased;overflow:hidden;
}
::selection{background:var(--navy-lift);color:#fff}

/* ---------- layout ---------- */
.app{display:grid;grid-template-columns:232px 1fr;height:100vh}
.work{display:grid;grid-template-columns:minmax(340px,26%) 1fr;height:100vh;min-width:0}
/* Column layout so the table gets its own scroll box below a fixed head —
   that box is what the sticky <th> sticks to. app.js sets display:flex here. */
#view-clients{height:100vh;flex-direction:column;padding-bottom:0}
.pane{overflow-y:auto;padding:26px 24px 70px;min-width:0}
.pane-left{border-right:1px solid var(--line);background:var(--panel)}

header{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:18px}
h1{font-family:var(--display);font-weight:600;font-size:26px;letter-spacing:-.015em;margin:0}
.sub{color:var(--ink-3);font-size:13px}
.count{font-size:12px;color:var(--ink-3);letter-spacing:.06em;text-transform:uppercase}

/* ---------- sidebar ---------- */
.side{
  display:flex;flex-direction:column;gap:3px;padding:24px 16px;
  border-right:1px solid var(--line);background:#0b0e12;overflow-y:auto;
}
/* The wordmark is white-on-transparent, so the black MEDIA block disappears
   into the sidebar and it reads as one continuous white logo. */
.brand{padding:4px 8px 24px}
.brand img{display:block;width:100%;max-width:184px;height:auto}
.nav{
  display:flex;align-items:center;gap:8px;width:100%;text-align:left;
  border:none;background:none;border-radius:9px;padding:11px 13px;
  font-family:inherit;font-size:14px;color:var(--ink-2);transition:.13s;
}
.nav:hover{background:var(--raise);color:var(--ink)}
.nav.on{background:var(--navy);color:#fff}
.nav-n{
  margin-left:auto;font-size:11px;font-variant-numeric:tabular-nums;
  background:rgba(255,255,255,.09);border-radius:999px;padding:1px 7px;
}
.nav.on .nav-n{background:rgba(255,255,255,.2)}
/* The views that exist as a frame and nothing else. A hairline above them
   and a quieter colour, so a click landing on an empty page reads as
   not-built-yet rather than as something that failed to load. They still
   hover and still take .on like any other nav item — they work, there is
   just nothing in them. */
.nav-soon{
  display:flex;flex-direction:column;gap:3px;
  margin-top:10px;padding-top:9px;border-top:1px solid var(--line-2);
}
.nav-soon .nav{color:var(--ink-3)}
.nav-soon .nav:hover{color:var(--ink-2)}
.nav-soon .nav.on{color:#fff}

.side-foot{
  margin-top:auto;padding:14px 12px 0;border-top:1px solid var(--line-2);
  font-size:11px;color:var(--ink-3);display:flex;flex-direction:column;gap:5px;align-items:flex-start;
}

/* The build you're looking at, and a way into what changed. Quiet enough
   to ignore, legible enough to read out over the phone. */
/* 10px type in the corner of a sidebar is a small thing to hit, so the
   padding is the click target rather than decoration. */
.ver{
  font-family:var(--mono);font-size:10px;letter-spacing:.06em;
  color:var(--ink-3);text-decoration:none;padding:5px 6px 5px 0;
}
.ver:hover{color:var(--ink-2);text-decoration:underline}

/* ---------- what's new ----------
   Native <dialog>: Esc, focus trapping and the backdrop are the browser's
   job, so this is only paint. */
.modal{
  width:min(560px,calc(100vw - 32px));max-height:min(70vh,720px);
  padding:0;border:1px solid var(--line);border-radius:var(--radius);
  background:var(--panel);color:var(--ink);
  box-shadow:0 24px 60px rgba(0,0,0,.55);
}
.modal::backdrop{background:rgba(11,10,10,.62)}
.modal-head{
  display:flex;align-items:center;gap:14px;
  padding:16px 20px;border-bottom:1px solid var(--line-2);
  position:sticky;top:0;background:var(--panel);border-radius:var(--radius) var(--radius) 0 0;
}
.modal-head h3{margin:0;font-size:15px;font-weight:600}
.modal-head .link{margin-left:auto}
.modal-body{padding:6px 20px 20px;overflow-y:auto;max-height:calc(70vh - 56px)}
.modal-body:focus{outline:none}
.modal .link:focus-visible{outline:2px solid var(--navy-ink);outline-offset:3px;border-radius:4px}
.rel{padding:16px 0;border-bottom:1px solid var(--line-2)}
.rel:last-child{border-bottom:none}
.rel h4{
  margin:0 0 9px;display:flex;align-items:baseline;gap:10px;
  font-family:var(--mono);font-size:11px;font-weight:400;letter-spacing:.06em;color:var(--ink-2);
}
.rel h4 time{font-family:var(--sans);font-size:11px;color:var(--ink-3);letter-spacing:0}
.rel ul{margin:0;padding-left:17px;display:flex;flex-direction:column;gap:6px}
.rel li{font-size:12.5px;line-height:1.55;color:var(--ink-2)}

/* ---------- mobile chrome ----------
   Both are position:fixed, which takes them out of .app's grid entirely —
   the desktop two-column layout never sees them. */
.topbar,.scrim{display:none}
.burger{
  display:grid;place-items:center;width:42px;height:42px;flex:none;
  border:1px solid var(--line);border-radius:11px;
  background:var(--panel);color:var(--ink);
}
.burger:active{background:var(--raise)}

/* ---------- controls ---------- */
button{font-family:inherit;font-size:13px;cursor:pointer}
.btn{
  border:1px solid var(--navy);background:var(--navy);color:#fff;
  border-radius:999px;padding:9px 20px;font-weight:500;transition:.15s;
}
.btn:hover{background:var(--navy-lift);border-color:var(--navy-lift)}
.btn:disabled{opacity:.4;cursor:not-allowed}
.btn-ghost{background:transparent;color:var(--ink-2);border-color:var(--line)}
.btn-ghost:hover{background:var(--raise);color:var(--ink);border-color:var(--ink-3)}
.btn-sm{padding:6px 14px;font-size:12px}
.link{
  background:none;border:none;color:var(--ink-3);padding:0;
  text-decoration:underline;text-underline-offset:3px;
}
.link:hover{color:var(--navy-ink)}

textarea{
  width:100%;min-height:150px;resize:vertical;
  font-family:var(--mono);font-size:12px;line-height:1.55;
  border:1px solid var(--line);border-radius:10px;padding:14px;
  background:var(--bg);color:var(--ink);
}
textarea:focus,input:focus,select:focus{outline:2px solid var(--navy-lift);outline-offset:1px}
input,select{color-scheme:dark}
.paste-actions{display:flex;align-items:center;gap:12px;margin-top:12px;flex-wrap:wrap}
.hint{color:var(--ink-3);font-size:12px}

/* ---------- draft form ---------- */
/* Always on screen. Paste is the fast path, not the only one — a lead met
   at an open house is typed in, and a form that appears only after a
   successful parse has nowhere to type it. */
#draft{margin-top:20px}
/* Which five are required, said on a blank form rather than only after a
   failed save. The warn colour still waits for an attempt — see
   draftTried in app.js — this is just the marking. */
#draft .field[data-req] > label::after{content:' *';color:var(--ink-3)}
.draft-cols{display:grid;gap:20px}
.group h3{
  font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);margin:0 0 12px;padding-bottom:8px;border-bottom:1px solid var(--line-2);
}
.field{display:grid;grid-template-columns:104px 1fr;align-items:center;gap:10px;margin-bottom:8px}
.field label{font-size:12px;color:var(--ink-2)}
.field input{
  font-family:inherit;font-size:13px;color:var(--ink);
  border:1px solid transparent;border-bottom:1px solid var(--line);
  border-radius:4px;padding:5px 7px;background:transparent;width:100%;
}
.field select{
  font-family:inherit;font-size:13px;color:var(--ink);
  border:1px solid transparent;border-bottom:1px solid var(--line);
  border-radius:4px;padding:5px 7px;background:transparent;width:100%;
}
.field input:hover,.field select:hover{background:var(--raise)}
.field.missing input,.field.missing select{border-bottom-color:var(--warn);background:var(--warn-bg)}
.field-row{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.extras{margin:14px 0 0;font-size:11.5px;color:var(--ink-3);line-height:1.7}
.extras b{font-weight:500;color:var(--ink-2)}

.flag{
  display:flex;gap:8px;align-items:flex-start;border-radius:10px;
  padding:10px 13px;margin-bottom:16px;font-size:12.5px;
  background:var(--raise);border:1px solid var(--line);color:var(--ink-2);
}
.flag.dupe{background:var(--warn-bg);border-color:rgba(226,160,106,.3);color:var(--warn)}
.flag.client{background:var(--ok-bg);border-color:rgba(95,199,154,.3);color:var(--ok)}
.flag.checking{color:var(--ink-3)}
.flag b{font-weight:600}
.draft-foot{display:flex;align-items:center;gap:12px;margin-top:22px;padding-top:16px;border-top:1px solid var(--line-2)}

/* ---------- job form: line items ----------
   The editor sits inside the Order group and reads as one block: what you
   can pick on top, what has been picked below it, the total at the foot.
   Ruled rather than spaced, for the reason the services list is — a name
   over a price with nothing between rows reads as two rows. */
.lines{margin:14px 0 4px}
.linepick{display:flex;flex-direction:column;gap:12px}
.addons{display:grid;grid-template-columns:1fr 1fr;gap:6px 12px}
@media (max-width:560px){ .addons{grid-template-columns:1fr} }

/* The whole row is the control, not just the 13px box in front of it. */
.addon{
  display:flex;align-items:center;gap:8px;cursor:pointer;
  padding:6px 8px;border:1px solid transparent;border-radius:8px;
  font-size:12.5px;color:var(--ink-2);transition:.14s;
}
.addon:hover{background:var(--raise);border-color:var(--line)}
.addon.on{color:var(--ink);border-color:var(--navy-dim);background:rgba(6,104,162,.08)}
.addon input{accent-color:var(--navy);flex:none;margin:0}
.addon span{flex:1;min-width:0}
.addon b{font-weight:500;font-variant-numeric:tabular-nums;color:var(--ink-3)}
.addon.on b{color:var(--ink-2)}

.linerows{margin-top:14px}
.linerow{
  display:grid;grid-template-columns:1fr 110px 32px;gap:8px;
  align-items:center;padding:4px 0;border-bottom:1px solid var(--line-2);
}
.linerow:last-child{border-bottom:0}
.linerow input{
  font-family:inherit;font-size:13px;color:var(--ink);
  border:1px solid transparent;border-bottom:1px solid var(--line);
  border-radius:4px;padding:5px 7px;background:transparent;width:100%;
}
.linerow input:hover{background:var(--raise)}
/* Prices right-aligned and tabular so a column of them can be read down
   rather than across. */
.lprice{text-align:right;font-variant-numeric:tabular-nums}

.linefoot{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:12px}
.linetotal{
  font-size:13px;font-weight:600;color:var(--ink);
  font-variant-numeric:tabular-nums;
}

/* ---------- call queue ---------- */
.list-head{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin:0 0 4px}
.list-head h2{font-family:var(--display);font-size:20px;font-weight:600;letter-spacing:-.015em;margin:0}
#listHint{display:block;margin-bottom:10px}
.empty{
  border:1px dashed var(--line);border-radius:var(--radius);
  padding:38px 22px;text-align:center;color:var(--ink-3);font-size:13px;
}
.agent-card{
  border:1px solid var(--line);border-radius:var(--radius);
  margin-bottom:14px;overflow:hidden;background:var(--panel);
}
.agent-top{display:flex;align-items:center;gap:14px;padding:15px 22px;background:var(--raise);flex-wrap:wrap}
.agent-name{font-weight:600;font-size:14.5px}
.agent-meta{color:var(--ink-2);font-size:12.5px}
.agent-meta span{margin-right:14px;white-space:nowrap}
/* Jobs invert the sourcing card's contents, not its shape: one job per
   card, so the address heads the strip and the agent drops into the body
   where the address sits on a sourcing card. */
.pill{
  font-size:11px;letter-spacing:.04em;color:var(--ink-2);
  border:1px solid var(--line);border-radius:999px;padding:2px 10px;background:var(--panel);
}
.pill.is-client{background:var(--ok-bg);border-color:rgba(95,199,154,.32);color:var(--ok);text-transform:capitalize}
.pill.is-lead{background:var(--navy-dim);border-color:rgba(79,163,218,.34);color:var(--navy-ink);text-transform:capitalize}
/* The job type. Solid enough to read as the job's own label rather than as
   one more service chip beside it. Unclassified is the backlog marker —
   dashed, so a card that still needs sorting looks unfinished on purpose. */
.pill.is-type{background:var(--raise);border-color:var(--ink-3);color:var(--ink)}
.pill.is-type.is-unclassified{
  background:transparent;border-style:dashed;border-color:var(--line);color:var(--ink-3);
}

/* Both cards are their own button — clicking anywhere that isn't a control
   opens the drawer under it, so both have to say so on hover. */
#list .listing,#jobList .listing{cursor:pointer;transition:background .16s ease}
#list .listing:hover,#jobList .listing:hover,
#list .listing.on,#jobList .listing.on{background:var(--raise)}

/* Map is the anchor of the card; text sits beside it. */
.listing{
  display:grid;grid-template-columns:minmax(300px,460px) 1fr;gap:26px;
  align-items:start;padding:24px 22px;border-top:1px solid var(--line-2);
}
/* z-index:0 is doing real work: it makes this a stacking context, which traps
   Leaflet's internal panes (z-index 400-700) and the .approx overlay
   inside the card. Without it those 400s sit in the root stacking context and
   paint straight over the drawer, the scrim and the fixed topbar. */
.lmapwrap{position:relative;z-index:0;width:100%;height:320px;border-radius:12px;overflow:hidden}
.lmap{width:100%;height:100%;background:#eeece}
.leaflet-container{background:#eeecea;font-family:inherit}

/* Light map inside a dark card: the frame is a dark hairline so the panel
   has an edge, and the overlays flip to dark-on-white. */
.frame{
  position:absolute;inset:0;pointer-events:none;border-radius:12px;
  box-shadow:inset 0 0 0 1px rgba(6,104,162,.35),
             0 0 0 1px rgba(0,0,0,.35);
}
.frame::before,.frame::after{
  content:'';position:absolute;width:14px;height:14px;
  border-color:rgba(6,104,162,.55);border-style:solid;
}
.frame::before{top:9px;left:9px;border-width:2px 0 0 2px;border-radius:3px 0 0 0}
.frame::after{bottom:9px;right:9px;border-width:0 2px 2px 0;border-radius:0 0 3px 0}
/* The coordinate readout is gone — it told you nothing you could act on.
   Only the approximate-pin warning survives, because that one changes
   how much you trust the dot. */
.approx{
  position:absolute;left:10px;bottom:9px;z-index:400;
  font-family:var(--mono);font-size:8.5px;letter-spacing:.06em;
  text-transform:uppercase;color:#8a5a2b;background:rgba(255,255,255,.85);
  border-radius:4px;padding:2px 6px;
}
/* Navy pin on a light map — brand colour, full strength, no lift needed. */
.pin{position:relative}
.pin-core,.pin-ring{position:absolute;left:50%;top:50%;border-radius:50%;transform:translate(-50%,-50%)}
.pin-core{
  width:12px;height:12px;background:var(--navy);
  box-shadow:0 0 0 3px #fff, 0 2px 6px rgba(0,0,0,.4);
}
.pin-ring{
  width:12px;height:12px;border:2px solid rgba(6,104,162,.8);
  animation:ping 2.6s cubic-bezier(.2,.6,.3,1) infinite;
}
@keyframes ping{
  0%{width:12px;height:12px;opacity:.85}
  70%{width:62px;height:62px;opacity:0}
  100%{width:62px;height:62px;opacity:0}
}
.lmap-empty{
  position:relative;width:100%;height:100%;display:grid;place-items:center;gap:7px;
  border-radius:12px;background:#eeecea;overflow:hidden;border:1px solid var(--line);
  font-family:var(--mono);font-size:10px;letter-spacing:.1em;
  color:#656361;text-transform:uppercase;
}
.lmap-empty .link{color:var(--navy)}
.scan{
  position:absolute;left:0;right:0;height:1px;top:0;
  background:linear-gradient(90deg,transparent,rgba(6,104,162,.55),transparent);
  animation:sweep 1.9s linear infinite;
}
@keyframes sweep{0%{top:8%}100%{top:92%}}

.linfo{display:flex;flex-direction:column;gap:13px;min-width:0;padding-top:2px}
.lhead{display:flex;align-items:baseline;gap:12px}
.listing .addr{font-weight:500;font-size:16px}
.lsub{color:var(--ink-3);font-size:12.5px;margin-top:-9px}
.meta{display:flex;align-items:center;gap:16px;flex-wrap:wrap}
.listing-actions{margin-left:auto;display:flex;gap:12px;align-items:center}
.stat{color:var(--ink-2);font-size:12.5px;font-variant-numeric:tabular-nums}
.stat b{font-weight:500;color:var(--ink)}
.sqft{
  font-family:var(--display);font-size:19px;font-weight:600;
  font-variant-numeric:tabular-nums;letter-spacing:-.01em;
}
.sqft i{
  font-family:var(--sans);font-style:normal;font-size:10.5px;font-weight:500;
  letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3);margin-left:5px;
}
.when{color:var(--ink-3);font-size:12px;white-space:nowrap}
.due{color:var(--warn);font-weight:500}

/* ---------- call tracking ---------- */
.status{
  font-size:11px;font-weight:500;letter-spacing:.03em;
  border-radius:999px;padding:3px 11px;white-space:nowrap;
  border:1px solid var(--line);color:var(--ink-2);background:var(--raise);
}
.status.new{border-style:dashed;color:var(--ink-3)}
.status.wait{background:var(--warn-bg);border-color:rgba(226,160,106,.3);color:var(--warn)}
.status.good{background:var(--ok-bg);border-color:rgba(95,199,154,.3);color:var(--ok)}
.status.dead{background:var(--line-2);border-color:var(--line);color:var(--ink-3)}
/* An order nobody has answered. Louder than .wait, which means "we are
   waiting on them" — this one means they are waiting on us. */
.status.ask{
  background:var(--navy);border-color:var(--navy);color:#fff;font-weight:600;
}

/* How long a lead has been sitting, beside the status pill. It shares the
   pill's metrics but carries no border of its own until it is worth one —
   a queue where every card is outlined is a queue with no signal in it.
   Quiet for the first day, ink-2 through the first week, warn after it,
   and filled once it is a fortnight old. */
.age{
  font-size:11px;font-weight:500;letter-spacing:.03em;
  border-radius:999px;padding:3px 11px;white-space:nowrap;
  border:1px solid transparent;color:var(--ink-3);
}
.age.aging{color:var(--ink-2)}
.age.old{color:var(--warn)}
.age.stale{background:var(--warn-bg);border-color:var(--warn-bg);color:var(--warn);font-weight:600}

/* Never called and a week gone. The badge is legible once you are reading
   the card; this is what makes the card findable from the top of a
   nineteen-card page. padding-left absorbs the border so the text inside
   a cold card still lines up with the text in every other one. */
.listing.cold{border-left:3px solid var(--warn);padding-left:19px}

/* The Confirm / Decline row on an inbound order's card. */
.decide{
  display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  margin-top:10px;padding-top:10px;border-top:1px dashed var(--line-2);
}
.decide .ref{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  letter-spacing:.04em;color:var(--ink-3);
}
/* The chip for a filter that has something waiting behind it. */
.filter.is-wait{
  background:var(--navy);border-color:var(--navy);color:#fff;font-weight:600;
}
/* Both cards open the same way. display:none can't be animated, so the
   drawer is a one-row grid that goes from 0fr to 1fr — the row is what
   moves, and the content inside is clipped rather than reflowed, which is
   what keeps it from jumping. visibility rides along so nothing inside a
   closed drawer is tabbable or clickable; it flips to hidden only at the
   end of the close, so the collapse stays visible while it runs. */
.drawer{
  display:grid;grid-template-rows:0fr;opacity:0;visibility:hidden;
  transition:grid-template-rows .34s cubic-bezier(.22,.61,.36,1),
             opacity .24s ease, visibility .34s;
}
.drawer.open{grid-template-rows:1fr;opacity:1;visibility:visible}
/* min-height:0 is load-bearing: a grid item's default min-height is its
   content, which would refuse to collapse to a 0fr row. */
.drawer-in{overflow:hidden;min-height:0}

/* Everything visual sits on the inner element, so a closed drawer has no
   border and no padding to collapse through. */
.callbar .drawer-in{
  display:flex;flex-direction:column;gap:11px;
  padding:14px 22px 16px;border-top:1px solid var(--line-2);background:var(--raise);
}
/* The offers, side by side above the buttons. Equal columns because none
   of them is the default — which one you lead with depends on the house.
   How many there are is pitch_order's answer, not this file's, so the track
   count comes from auto-fit rather than being written down here as well. */
.offers{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:10px}
.offer{
  display:flex;flex-direction:column;gap:3px;
  border:1px solid var(--line);border-radius:11px;
  padding:11px 14px;background:var(--panel);
}
.offer-k{font-size:12px;color:var(--ink-2)}
.offer-v{
  font-family:var(--display);font-size:22px;font-weight:600;color:var(--ink);
  font-variant-numeric:tabular-nums;letter-spacing:-.01em;
  display:flex;align-items:baseline;gap:8px;
}
/* The list price next to it: what the half is half of, struck through so
   nobody reads it as the quote. */
.offer-v i{
  font-family:var(--sans);font-style:normal;font-size:12px;font-weight:400;
  color:var(--ink-3);text-decoration:line-through;
}
.offer-t{font-size:11px;color:var(--ink-3)}

/* Wraps by design: the set is editable, so this row has to hold six states
   or sixteen. row-gap is the one that matters once it does wrap. */
.outs{display:flex;gap:9px;align-items:center;flex-wrap:wrap}
.out{
  border:1px solid var(--line);background:var(--panel);color:var(--ink-2);
  border-radius:999px;padding:10px 19px;font-size:13.5px;transition:.12s;
}
.out:hover{border-color:var(--navy-ink);color:var(--ink);background:var(--navy-dim)}
.out.good:hover{border-color:rgba(95,199,154,.5);background:var(--ok-bg);color:var(--ok)}
.out.dead:hover{border-color:var(--line);background:var(--panel);color:var(--ink-3)}
/* The empty one at the end. Dashed so it reads as a slot rather than a
   seventh thing you can log. */
.out-add{
  border-style:dashed;padding:10px 16px;font-size:15px;line-height:1.05;color:var(--ink-3);
}
.states-edit,.states-done{margin-left:auto}

/* Edit mode: every state becomes its own two fields and a discard. */
.outs.editing{gap:8px}
.out-edit{
  display:inline-flex;align-items:center;gap:4px;
  border:1px solid var(--line);border-radius:999px;
  padding:2px 4px 2px 10px;background:var(--panel);
}
.out-edit.is-new{border-style:dashed}
.out-label{
  width:104px;font-family:inherit;font-size:12px;color:var(--ink);
  border:none;background:transparent;padding:4px 0;
}
.out-tone{
  font-family:inherit;font-size:11px;color:var(--ink-3);
  border:none;background:transparent;padding:3px 0;
}
.out-x{
  width:20px;height:20px;border-radius:50%;font-size:13px;line-height:1;
  color:var(--ink-3);background:transparent;border:none;
}
.out-x:hover{background:var(--warn-bg);color:var(--warn)}
.out-x.is-add:hover{background:var(--navy-dim);color:var(--navy-ink)}

/* ---- follow up ----
   Its own row under the call states, and deliberately quieter than they
   are: logging what happened is the primary act on this card, scheduling
   a call back is the secondary one. Smaller chips, no fill until hover,
   and a leading label so the row says what it is rather than looking
   like three more outcomes you could log. */
.fup{
  display:flex;gap:8px;align-items:center;flex-wrap:wrap;
  margin-top:14px;padding-top:14px;border-top:1px solid var(--line-2);
}
.fup-k{
  font-size:11px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-3);margin-right:2px;
}
.fchip{
  border:1px solid var(--line);background:transparent;color:var(--ink-2);
  border-radius:999px;padding:6px 14px;font-size:12.5px;transition:.12s;cursor:pointer;
}
.fchip:hover{border-color:var(--navy-ink);color:var(--ink);background:var(--navy-dim)}
/* The date already set. Marked so the row reads as the state it is in,
   not as four things you could press. */
.fchip.on{border-color:var(--navy-ink);background:var(--navy-dim);color:var(--ink)}
/* The native picker, dressed to match. `color-scheme:dark` is what makes
   the browser's own calendar and its indicator render dark — without it
   Chrome paints a white sheet over a dark card. */
.fdate{
  color-scheme:dark;
  border:1px solid var(--line);background:var(--panel);color:var(--ink-2);
  border-radius:999px;padding:6px 12px;font-size:12.5px;font-family:var(--sans);
}
.fdate:hover{border-color:var(--navy-ink);color:var(--ink)}
.fdate:focus{outline:none;border-color:var(--navy-ink);color:var(--ink)}
.fclear{margin-left:2px;font-size:12.5px}

@media (max-width:560px){
  /* The label takes its own line rather than squeezing the chips, which
     are the part you have to be able to hit with a thumb. */
  .fup-k{flex:1 0 100%;margin-bottom:2px}
  .fchip,.fdate{padding:8px 14px}
}

.note-in{
  width:100%;min-height:76px;resize:vertical;
  font-family:inherit;font-size:12.5px;line-height:1.5;color:var(--ink);
  border:1px solid var(--line);border-radius:9px;background:var(--panel);padding:9px 11px;
}
.note-in:focus{outline:none;border-color:var(--navy-ink)}
.history{
  margin:0;padding-bottom:9px;border-bottom:1px solid var(--line);
  font-size:12px;color:var(--ink-2);
}
.history div{display:flex;gap:9px;padding:2px 0}
.history time{color:var(--ink-3);min-width:104px;font-variant-numeric:tabular-nums}
.history b{font-weight:500;color:var(--ink);min-width:96px}
.history i{font-style:normal;color:var(--ink-2)}

.filters{display:flex;gap:6px;margin-left:auto;flex-wrap:wrap}
.filter{
  border:1px solid var(--line);background:var(--panel);color:var(--ink-2);
  border-radius:999px;padding:5px 13px;font-size:12px;
}
.filter:hover{border-color:var(--ink-3);color:var(--ink)}
.filter.on{background:var(--navy);border-color:var(--navy);color:#fff}

/* The sort row sits under the filters, not beside them. They are two
   different questions — which leads, then in what order — and putting
   nine pills on one line made both of them harder to find. */
.list-sort{display:flex;margin:0 0 12px}
.sorts{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.sort-label{
  font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-3);margin-right:2px;
}
/* Quieter than a filter: a filter changes what you are looking at, a sort
   only changes the order of it. The chosen one is outlined rather than
   filled, so it never competes with the filter that is actually on. */
.sort-btn{padding:4px 11px;font-size:11.5px}
.sort-btn.on{
  background:var(--raise);border-color:var(--ink-3);color:var(--ink);font-weight:500;
}

/* ---------- clients table ---------- */
.search{
  width:100%;max-width:420px;margin:0 0 16px;padding:9px 13px;
  font-family:inherit;font-size:13px;border:1px solid var(--line);
  border-radius:999px;background:var(--panel);color:var(--ink);
}
.table-wrap{flex:1 1 auto;min-height:0;overflow:auto;padding-bottom:60px}
.ctable{width:100%;border-collapse:collapse;font-size:13px;min-width:820px}
.ctable th{
  position:sticky;top:0;z-index:2;background:var(--bg);
  text-align:left;font-weight:500;font-size:11px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--ink-3);
  padding:9px 14px;border-bottom:1px solid var(--line);white-space:nowrap;
}
.th-sort{cursor:pointer;user-select:none}
.th-sort:hover{color:var(--ink)}
.th-sort.on{color:var(--navy-ink)}
.th-sort::after{content:'';display:inline-block;width:11px;opacity:.5}
.th-sort.on[data-dir="asc"]::after{content:'↑';opacity:1}
.th-sort.on[data-dir="desc"]::after{content:'↓';opacity:1}
.ctable td{padding:10px 14px;border-bottom:1px solid var(--line-2);vertical-align:middle}
.ctable tbody tr:hover{background:var(--panel)}
.cname{font-weight:500;white-space:nowrap}
.ctable .muted{color:var(--ink-2);font-size:12.5px}
.ctable .nowrap{white-space:nowrap}
.ctable .trunc{max-width:230px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ctable .num{text-align:right;font-variant-numeric:tabular-nums}
.ctable th.num{text-align:right}
.ctable tr.editing{background:var(--raise)}
.ctable tr.editing td{padding:16px 14px}
.edit-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
.edit-grid label{display:flex;flex-direction:column;gap:4px;font-size:11px;color:var(--ink-3)}
.edit-grid input,.edit-grid select{
  font-family:inherit;font-size:13px;color:var(--ink);padding:7px 9px;
  border:1px solid var(--line);border-radius:8px;background:var(--bg);
}
.edit-foot{display:flex;gap:10px;align-items:center;margin-top:14px}

/* On a phone a nine-column table can only be a card. Each cell carries its
   own label, and cells the row has no value for drop out entirely — a stack
   of nine dashes tells you nothing. */
@media(max-width:700px){
  .table-wrap{overflow:visible;padding-bottom:0}
  .ctable{min-width:0}
  .ctable,.ctable tbody,.ctable tr,.ctable td{display:block;width:100%}
  .ctable thead{display:none}
  .ctable tr{
    border:1px solid var(--line);border-radius:var(--radius);
    background:var(--panel);padding:13px 15px;margin-bottom:11px;
  }
  .ctable td{
    border:none;padding:4px 0;display:flex;gap:12px;align-items:baseline;
    text-align:left;font-size:13.5px;
  }
  .ctable td::before{
    content:attr(data-label);flex:0 0 82px;
    font-size:10.5px;letter-spacing:.07em;text-transform:uppercase;
    color:var(--ink-3);
  }
  .ctable td.cname{
    font-size:16px;padding:0 0 9px;margin-bottom:7px;
    border-bottom:1px solid var(--line-2);white-space:normal;
  }
  .ctable td.cname::before{display:none}
  .ctable td.is-empty{display:none}
  .ctable td.actions{justify-content:flex-end;padding-top:9px}
  .ctable td.actions::before{display:none}
  .ctable .num{text-align:left}
  .ctable .nowrap{white-space:normal}
  .ctable .trunc{max-width:none;white-space:normal;overflow:visible;overflow-wrap:anywhere}
  .ctable tr.editing{padding:15px}
  .ctable tr.editing td{padding:0}
  .ctable tr.editing td::before{display:none}
  .edit-grid{grid-template-columns:1fr}
}

/* ---------- gate + banner ---------- */
.gate{display:grid;place-items:center;height:100vh;height:100dvh;background:var(--bg)}
.gate-box{
  width:320px;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius);padding:30px 28px;
}
.gate-logo{display:block;width:100%;max-width:188px;height:auto;margin:0 auto 18px}
.gate-box .sub{display:block;margin-bottom:20px;font-size:13px;color:var(--ink-3)}
.gate-box input{
  display:block;width:100%;margin-bottom:10px;padding:10px 12px;
  font-family:inherit;font-size:13.5px;border:1px solid var(--line);
  border-radius:9px;background:var(--bg);color:var(--ink);
}
.gate-box .btn{width:100%;margin-top:6px}
.gate-err{margin:12px 0 0;font-size:12.5px;color:var(--warn);min-height:16px}
.banner{
  position:fixed;top:0;left:0;right:0;z-index:50;
  padding:10px 16px;font-size:12.5px;font-family:var(--mono);
  background:var(--warn-bg);border-bottom:1px solid rgba(226,160,106,.3);color:var(--warn);
}

/* ============================================================
   MOBILE
   Under 980px the sidebar slides out from behind the hamburger
   instead of eating a column, and every fixed-width grid folds
   to one column.
   ============================================================ */
@media(max-width:980px){
  body{overflow:auto;-webkit-tap-highlight-color:transparent}
  body.nav-open{overflow:hidden}

  .app{
    grid-template-columns:1fr;height:auto;
    padding-top:calc(var(--topbar-h) + env(safe-area-inset-top,0px));
  }
  .work{grid-template-columns:1fr;height:auto}
  #view-clients{height:auto;padding-bottom:36px}
  .pane{overflow:visible;padding:20px 16px 40px}
  .pane-left{border-right:none;border-bottom:1px solid var(--line)}

  .topbar{
    display:flex;align-items:center;gap:12px;
    position:fixed;top:0;left:0;right:0;z-index:40;
    height:calc(var(--topbar-h) + env(safe-area-inset-top,0px));
    padding:env(safe-area-inset-top,0px) 14px 0;
    background:#0b0e12;border-bottom:1px solid var(--line);
  }
  .topbar-logo{display:block;height:21px;width:auto}

  /* Off-canvas drawer. visibility (not just transform) keeps the closed
     menu out of the tab order. */
  .side{
    position:fixed;top:0;bottom:0;left:0;z-index:60;
    width:min(78vw,274px);height:100%;
    padding:calc(20px + env(safe-area-inset-top,0px)) 14px
            calc(18px + env(safe-area-inset-bottom,0px));
    border-right:1px solid var(--line);
    box-shadow:0 0 44px rgba(0,0,0,.55);
    visibility:hidden;transform:translateX(-100%);
    transition:transform .22s ease,visibility .22s ease;
  }
  .side.open{visibility:visible;transform:none}
  .brand img{max-width:184px}
  .nav{padding:13px 12px;font-size:14.5px}
  .nav-gear{padding:13px 12px}

  .scrim{
    display:block;position:fixed;inset:0;z-index:50;
    background:rgba(0,0,0,.55);
    opacity:0;pointer-events:none;transition:opacity .22s ease;
  }
  body.nav-open .scrim{opacity:1;pointer-events:auto}

  .listing{grid-template-columns:1fr;padding:18px}
  .lmapwrap{height:230px}
  .field{grid-template-columns:96px 1fr}
  .edit-grid{grid-template-columns:1fr 1fr}

  /* 16px is the threshold under which iOS Safari zooms the page on focus. */
  input,select,textarea,.search,.field input,.note-in,.sort-pick select{font-size:16px}
}

@media(max-width:640px){
  h1{font-size:22px}
  .pane{padding:18px 13px 34px}
  .gate{padding:0 16px}
  .gate-box{width:100%;max-width:320px}

  /* A 96px label column leaves nothing for the input on a 360px screen. */
  .field{grid-template-columns:1fr;gap:3px;margin-bottom:12px}
  .field input{padding:7px 9px;border:1px solid var(--line);border-radius:8px}

  .filters{margin-left:0}
  .listing{padding:16px 13px}
  .lmapwrap{height:200px}
  .agent-top{padding:13px 15px;gap:10px}
  .listing-actions{margin-left:0}
  .callbar .drawer-in{padding:12px 14px}
  /* Two offer tiles side by side on a phone leaves each about 24 characters
     wide, which breaks the price onto its own line. */
  .offers{grid-template-columns:1fr}
  .history div{flex-wrap:wrap;gap:4px 9px}
  .history time{min-width:0}

  /* Touch targets — 32px tall buttons are a miss on a phone. */
  .btn{padding:11px 20px}
  .btn-sm{padding:9px 15px}
  .filter,.out{padding:8px 14px}

  .setting-row .home-label{flex:1 1 100%}
  .home-row{gap:10px;padding:12px 13px}
}
@media(prefers-reduced-motion:reduce){*{transition:none!important}.pin-ring,.scan{animation:none}}

/* ---------- jobs ----------
   The cards are the sourcing cards — same .agent-card, .listing, .lmapwrap.
   Only the toolbar above them is new. */
#jobHint{display:block;margin-bottom:16px}
.job-tools{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:18px}
.job-tools .search{margin:0;flex:1 1 240px;max-width:360px}
/* Seven categories plus the shooters won't share a line, so the type
   filter gets its own row under the tools. margin-left:0 undoes the
   auto that pushes .filters right inside a .list-head. */
.jtypes{margin-left:0;margin-bottom:18px}
/* .filters pushes itself right in a header row; inside the toolbar it's
   just another control, so the auto margin has to go. */
.job-tools .filters{margin-left:0}
/* Grid view: three across, and the card turns vertical — map on top, the
   info column under it. Same markup, so a job card doesn't know which view
   it's in. */
#jobList.grid{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:14px;align-items:start;
}
#jobList.grid .agent-card{margin-bottom:0}
/* The address is the heading of a job card in either view, so it is one
   size in both — the grid card is narrower, not less important. */
#jobList .agent-name{font-size:16px}
#jobList.grid .agent-top{padding:12px 15px;gap:8px}
#jobList.grid .agent-meta{font-size:11.5px}
#jobList.grid .listing{grid-template-columns:1fr;gap:15px;padding:15px}
#jobList.grid .lmapwrap{height:272px}
#jobList.grid .linfo{gap:11px}
#jobList.grid .listing .addr{font-size:14.5px}
#jobList.grid .sqft{font-size:16px}
#jobList.grid .meta{gap:10px}
#jobList.grid .jobbar .drawer-in{padding:15px}
#jobList.grid .dcols{grid-template-columns:1fr;gap:16px}
/* A grid card is one column, so the panel lands under the info rather than
   beside it, and the map keeps its height. */
#jobList.grid .listing.on .jstats{padding-top:2px}
/* Three columns of detail under a job. Roomy on purpose — this is where
   the rest of a job's fields are going. */
.jobbar .drawer-in{
  padding:18px 22px 20px;border-top:1px solid var(--line-2);background:var(--raise);
}
.dcols{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:22px}
.dcol h4{
  margin:0 0 10px;font-family:var(--mono);font-size:9.5px;font-weight:400;
  letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);
}
.dcol dl{margin:0;display:flex;flex-direction:column;gap:6px}
/* Label column fixed so the values line up down the card; the value wraps
   inside its own cell rather than pushing the label around. */
.dl-row{display:grid;grid-template-columns:86px 1fr;gap:10px;align-items:baseline}
.dl-row dt{font-size:12px;color:var(--ink-3)}
.dl-row dd{
  margin:0;font-size:12.5px;color:var(--ink);
  font-variant-numeric:tabular-nums;overflow-wrap:anywhere;
}
.dlist{
  list-style:none;margin:12px 0 0;padding:11px 0 0;
  border-top:1px solid var(--line);display:flex;flex-direction:column;gap:5px;
}
.dlist li{display:flex;gap:10px;align-items:baseline;font-size:12px}
.dlist li span{color:var(--ink-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dlist li i{font-style:normal;color:var(--ink-3);margin-left:auto;white-space:nowrap}

/* Open a job card and a third column appears in the body, filling what was
   dead space to the right of the map. It only exists in the open state, so
   the closed card is exactly what it was. */
#jobList:not(.grid) .listing{
  transition:grid-template-columns .34s cubic-bezier(.22,.61,.36,1),
             background .16s ease;
}
#jobList:not(.grid) .listing.on{grid-template-columns:minmax(300px,460px) 1fr minmax(280px,360px)}
#jobList:not(.grid) .listing.on .lmapwrap{height:530px}
.jstats{display:none}
.listing.on .jstats{
  display:flex;flex-direction:column;gap:16px;min-width:0;
  animation:statsIn .34s cubic-bezier(.22,.61,.36,1) both;
}
@keyframes statsIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.jstats h4{
  margin:0;font-family:var(--mono);font-size:9.5px;font-weight:400;
  letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);
}
.tiles{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
.tile{
  display:flex;flex-direction:column;gap:2px;
  border:1px solid var(--line);border-radius:10px;padding:9px 10px;background:var(--panel);
}
/* Proportional figures, not tabular: these three sit side by side rather
   than in a column, so there is nothing for equal-width digits to line up
   with and they only make the number look loose. */
.tile b{
  font-family:var(--display);font-size:17px;font-weight:600;color:var(--ink);
  letter-spacing:-.01em;
}
.tile span{font-size:10.5px;color:var(--ink-3)}
.fig{margin:0;display:flex;flex-direction:column;gap:7px}
.fig figcaption{
  font-size:11px;color:var(--ink-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.fig-note{margin:0;font-size:11.5px;color:var(--ink-3);line-height:1.5}
.chart{width:100%;height:auto;overflow:visible}
/* Two marks, and every value is written out as well — nothing here is
   carried by colour alone. */
.chart .bar{fill:var(--ink-3)}
.chart .bar.is-this{fill:var(--navy-ink)}
.chart .track{fill:var(--line-2)}
.chart .axis{stroke:var(--line);stroke-width:1}
.chart .rule{stroke:var(--line-2);stroke-width:1}
.chart .tick{font-family:var(--sans);font-size:9px;fill:var(--ink-3)}
/* Painted with a surface-coloured stroke underneath so it stays readable
   where it passes over a neighbouring bar. */
.chart .bar-lbl{
  font-family:var(--sans);font-size:10px;fill:var(--ink-2);
  font-variant-numeric:tabular-nums;
  paint-order:stroke;stroke:var(--panel);stroke-width:2.5px;stroke-linejoin:round;
}

.viewpick{margin-left:auto}

.sort-pick{
  display:flex;align-items:center;gap:9px;white-space:nowrap;
  font-size:11px;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-3);
}
.sort-pick select{
  font-family:inherit;font-size:13px;letter-spacing:0;text-transform:none;
  color:var(--ink);background:var(--panel);border:1px solid var(--line);
  border-radius:999px;padding:8px 13px;
}
.sort-pick select:hover{border-color:var(--ink-3)}
/* Services are a comma string in the import — one chip each, so a job with
   three add-ons reads as three things rather than one long line. */
.svc{display:flex;gap:6px;flex-wrap:wrap}
.svc .pill{background:var(--raise);color:var(--ink-2)}
.addr .unit,.agent-name .unit{
  font-style:normal;font-weight:400;font-size:13px;
  color:var(--ink-3);margin-left:4px;
}
.more{display:flex;justify-content:center;padding:6px 0 24px}

@media(max-width:1280px){
  #jobList.grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}

@media(max-width:980px){
  .dcols{grid-template-columns:1fr;gap:16px}
  .jobbar .drawer-in{padding:15px}
  /* One column below the drawer breakpoint — a third of a phone is not a
     map. The toggle stays, it just has one layout to give you. */
  #jobList.grid{grid-template-columns:1fr}
  .viewpick{margin-left:0}
  .job-tools .search{max-width:none}
}

/* ---------- home points on the map ----------
   Muted on purpose. These are context, not the subject — the navy
   listing pin has to stay the thing your eye lands on first. */
.home-pin{color:#504e4c}
.home-mark{
  display:grid;place-items:center;width:24px;height:24px;
  background:rgba(255,255,255,.92);border-radius:7px;
  box-shadow:0 0 0 1px rgba(0,0,0,.18), 0 1px 4px rgba(0,0,0,.22);
}

/* ---------- attribution ---------- */
.by{font-weight:500;color:var(--ink-2)}
.history u{
  text-decoration:none;color:var(--navy-ink);font-size:11.5px;
  min-width:64px;
}

/* ---------- sidebar gear ---------- */
.nav-gear{
  gap:9px;margin-bottom:8px;font-size:13px;
  /* .side-foot is a column flex with align-items:flex-start, which would
     shrink a button to its text width. */
  align-self:stretch;
}
.nav-gear svg{flex:none;opacity:.75}
.nav-gear.on svg{opacity:1}
.side-foot{padding-top:12px}

/* ---------- home ----------
   A to-do list. Rows, never tiles: the thing being read is a name and an
   address, so the type that carries those is the biggest thing in the
   row and everything else sits around it. Nothing here renders when its
   section is empty, so there are no "zero" states to style. */
#view-home{max-width:900px}

/* The one number. Big because it is the only one, and it is money. */
.owed{
  display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;
  margin:0 0 30px;padding:18px 22px;
  border:1px solid var(--line);border-left:3px solid var(--warn);
  border-radius:var(--radius);background:var(--panel);
}
.owed-n{
  font-family:var(--display);font-size:32px;font-weight:600;
  letter-spacing:-.02em;font-variant-numeric:tabular-nums;color:var(--ink);
}
.owed-k{font-size:13px;color:var(--ink-2)}

.hsec{margin:0 0 34px}
.hsec h2{
  display:flex;align-items:center;gap:10px;
  font-family:var(--display);font-size:16px;font-weight:600;
  letter-spacing:-.01em;margin:0 0 12px;
}
/* The count rides the header so the shape of the day reads without
   scrolling — how many, before you look at which. */
.hcount{
  font-family:var(--sans);font-size:11px;font-weight:600;
  font-variant-numeric:tabular-nums;color:var(--ink-2);
  background:var(--raise);border-radius:999px;padding:2px 8px;
}
.hsec[data-hsec="orders"] .hcount{background:var(--navy);color:#fff}

.hrows{display:flex;flex-direction:column;gap:1px;background:var(--line-2);
  border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
.hday{
  font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);margin:16px 0 8px;
}
.hsec .hday:first-of-type{margin-top:0}

.hrow{
  display:flex;align-items:center;gap:14px;width:100%;text-align:left;
  border:none;background:var(--panel);padding:13px 16px;
  font:inherit;color:inherit;cursor:pointer;transition:.12s;
}
.hrow:hover{background:var(--raise)}
.hrow:focus-visible{outline:2px solid var(--navy);outline-offset:-2px}

/* The time a shoot happens, first and monospaced so a column of them
   lines up and can be read down rather than across. */
.hlead{
  flex:none;min-width:74px;font-variant-numeric:tabular-nums;
  font-weight:600;font-size:13.5px;color:var(--ink);
}
.hmain{display:flex;flex-direction:column;gap:1px;flex:1;min-width:0}
.hmain b{font-weight:600;font-size:14px;color:var(--ink)}
.hmain i{
  font-style:normal;font-size:12.5px;color:var(--ink-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.hmain .hsub{font-size:12px;color:var(--ink-3)}
.hamt{
  flex:none;font-family:var(--display);font-weight:600;font-size:15px;
  font-variant-numeric:tabular-nums;color:var(--ink);
}
.hwhen{flex:none;font-size:12px;color:var(--ink-3);min-width:92px;text-align:right}
.hact{display:flex;align-items:center;gap:10px;flex:none}

/* The section total. Only Delivered-not-paid has one, because it is the
   only section where the rows sum to something worth knowing. */
.hfoot{
  display:flex;align-items:baseline;gap:12px;justify-content:flex-end;
  padding:11px 16px 0;font-size:12.5px;color:var(--ink-2);
}
.hfoot b{
  font-family:var(--display);font-size:16px;font-weight:600;color:var(--ink);
  font-variant-numeric:tabular-nums;
}

/* The calling queue, as a sentence. Sits between the sections and the
   shoot counts: below the things that need a decision, above the things
   that are only worth knowing. Quiet on purpose — it is a door, not an
   alarm — but it renders whenever there is anyone to ring, including on a
   morning when every section above it is empty. */
.hcall{
  margin:0 0 34px;padding:13px 16px;
  border:1px solid var(--line);border-radius:var(--radius);
  background:var(--panel);font-size:13.5px;color:var(--ink-3);
}
.hcall .link{font-size:13.5px}
.hcall .is-total{font-weight:600;color:var(--ink)}
/* The board renders nothing at all when the only thing waiting is the
   queue, so this line must not inherit a gap from a section that isn't
   there. */
#homeBoard:empty + #homeCalls .hcall{margin-top:2px}

/* Everything empty, said once. */
.hclear{color:var(--ink-3);font-size:14px;margin:6px 0 0;max-width:60ch;line-height:1.6}

/* Shoots per person. Small, at the bottom, out of the way of the work —
   it is the only thing on this page that is not a task. */
.hcrew{margin:44px 0 0;padding-top:22px;border-top:1px solid var(--line-2)}
.hcrew h2{
  font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);margin:0 0 14px;
}
/* The section carries two groups now, Leads then Shoots. The first
   heading sits under the section's own padding; a second one needs its
   own air above it or the two grids read as one. */
.hcrew .crewgrid + h2{margin-top:24px}
/* Four readings per card now, not two — the column has to be wide
   enough that they do not stack one per line. */
.crewgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:10px}
.crew{
  display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;
  border:1px solid var(--line);border-radius:11px;padding:11px 14px;background:var(--panel);
}
.crew-n{font-size:13px;font-weight:600;flex:1 0 100%;color:var(--ink)}
.crew-v{display:flex;align-items:baseline;gap:5px}
.crew-v b{
  font-family:var(--display);font-size:19px;font-weight:600;
  font-variant-numeric:tabular-nums;
}
.crew-v i{font-style:normal;font-size:11.5px;color:var(--ink-3)}

/* A page you check on a phone before leaving the house. The row stops
   being a single line and becomes two: the identity on top, the time and
   the money under it, nothing truncated to nothing. */
@media (max-width:640px){
  .hrow{flex-wrap:wrap;gap:4px 12px;padding:12px 14px}
  .hlead{min-width:0;order:-1;flex:1 0 100%;color:var(--navy-ink)}
  .hmain{flex:1 0 100%}
  .hmain i{white-space:normal}
  .hwhen{min-width:0;text-align:left;margin-left:auto}
  .hact{flex:1 0 100%;margin-top:8px}
  .hact .btn{flex:1}
  .owed{padding:15px 17px}
  .owed-n{font-size:26px}
}

/* ---------- ordering ----------
   A queue, not a card list: these rows exist to be counted and scanned,
   and the decision is made on the Jobs tab where the map and the history
   are. Reference first, because it is what a customer says on the phone. */
.orow{
  display:flex;align-items:baseline;gap:14px;
  padding:12px 0;border-bottom:1px solid var(--line-2);
}
.orow:last-of-type{border-bottom:none}
.oref{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:12px;letter-spacing:.04em;color:var(--ink-3);flex:none;min-width:82px;
}
.omain{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}
.omain b{font-weight:600;font-size:14px}
.omain i{font-style:normal;font-size:12.5px;color:var(--ink-2)}
.oamt{
  font-family:var(--display);font-weight:600;font-size:15px;
  font-variant-numeric:tabular-nums;flex:none;
}
.oamt em{font-family:var(--sans);font-style:normal;font-size:12px;font-weight:400;color:var(--ink-3)}
.owhen{font-size:12px;color:var(--ink-3);flex:none;min-width:70px;text-align:right}
@media (max-width:700px){
  .orow{flex-wrap:wrap;gap:6px 12px}
  .owhen{text-align:left}
}

/* ---------- settings ---------- */
.setting{
  max-width:720px;margin:0 0 34px;padding-bottom:26px;
  border-bottom:1px solid var(--line-2);
}
.setting:last-child{border-bottom:none}
.setting h3{
  font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);margin:0 0 8px;
}
.setting-note{margin:0 0 16px;font-size:12.5px;color:var(--ink-2);line-height:1.6;max-width:60ch}
.setting-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.setting-row .search{margin:0;flex:1 1 240px;max-width:none}
.setting-row .home-label{flex:0 1 180px}

.home-row{
  display:flex;align-items:center;gap:14px;
  padding:13px 16px;margin-top:10px;
  border:1px solid var(--line);border-radius:11px;background:var(--panel);
}
.home-ico{
  font-size:17px;line-height:1;color:var(--navy-ink);
  width:26px;height:26px;display:grid;place-items:center;flex:none;
  border-radius:7px;background:var(--navy-dim);
}
.home-text{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
.home-text b{font-weight:500;font-size:13.5px}
.home-text i{
  font-style:normal;font-size:12px;color:var(--ink-3);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.home-state{font-size:11px;letter-spacing:.04em;color:var(--ink-3);white-space:nowrap}
.home-state.ok{color:var(--ok)}
.home-state.bad{color:var(--warn)}

@media(max-width:980px){
  /* The drawer keeps the sidebar's full column layout, so the foot needs
     no special case any more — only the settings row does. */
  .setting-row .search{flex:1 1 100%}
}

/* ---------- job files ----------
   The fourth section spans all three columns. An upload zone and a list
   of filenames in a third of the width would wrap every row. */
.dcol-wide{grid-column:1/-1}
.dcol h4 i{font-style:normal;color:var(--ink-2)}
.drop{
  display:grid;place-items:center;text-align:center;
  border:1px dashed var(--line);border-radius:11px;
  padding:20px 16px;font-size:12.5px;color:var(--ink-3);
  transition:border-color .16s ease,background .16s ease;
}
/* The drop target has to say it is one while a file is over it — a dashed
   box that doesn't react reads as decoration. */
.drop.over{border-color:var(--navy-ink);background:var(--navy-dim);color:var(--ink)}
.drop .link{color:var(--navy-ink)}
.dprog{
  min-height:16px;margin-top:8px;
  font-family:var(--mono);font-size:11px;color:var(--ink-2);
  overflow-wrap:anywhere;
}
/* ---------- delivery ----------
   What the client owes and the page they were sent to, on one row under a
   heading of its own.

   The names are `.dlv*` and not `.share*` on purpose. Ad-blocker cosmetic
   filter lists hide any element whose class contains "share" — generic
   rules aimed at social widgets — so the old `.sharebar` was in the DOM,
   with correct CSS, and drew nothing at all in a browser with a blocker
   on. That cost an evening. Nothing in here, class or id, may contain
   "share" or "social"; the filters match ids too, which is why the input
   is `jdlv-` rather than `jshare-`. */
.dlvbar{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
/* Wraps to its own line before it squeezes the buttons off the row. */
.dlvurl{
  flex:1 1 260px;min-width:0;
  font-family:var(--mono);font-size:11.5px;color:var(--ink-2);
  background:var(--bg);border:1px solid var(--line);border-radius:8px;
  padding:8px 11px;
}
.dlvurl:focus{outline:none;border-color:var(--navy-ink);color:var(--ink)}
.dlvbar .btn{flex:none}
.dlvamt{flex:none;font-family:var(--mono);font-size:11.5px;color:var(--ink-2)}
.dlvnone{font-size:12.5px;color:var(--ink-3)}
/* The way out to the delivery page is an <a>, so it can be cmd-clicked
   into a background tab and copied out of the context menu. .btn was
   written for <button>, which brings its own font and has no underline to
   clear; the drawer's click handler already ignores anchors. */
a.btn{display:inline-block;font-family:inherit;font-size:13px;text-decoration:none}
a.btn-sm{font-size:12px}

/* ---------- test rows ----------
   A row made to exercise the app. Deliberately not a `.status` pill: it
   is not a stage the job is in, it is a statement about whether the row
   is real, and giving it the same shape as Scheduled/Shot/Delivered would
   put it in a sequence it has no place in. Dashed, uppercase, and in the
   warn colour — the same visual grammar as `.status.new`'s dashed border,
   which also means "this is not settled". */
.testtag{
  display:inline-block;font-style:normal;font-size:9.5px;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;vertical-align:middle;
  padding:1px 6px;border-radius:5px;position:relative;top:-1px;
  color:var(--warn);border:1px dashed rgba(226,160,106,.45);
  background:var(--warn-bg);
}

/* A checkbox and its label on one line, which no other field in either
   dialog needs — every other one is a stacked label over an input. */
.check{
  display:flex;align-items:center;gap:9px;cursor:pointer;
  padding:9px 0 2px;font-size:12.5px;color:var(--ink-2);
}
.check input{
  width:15px;height:15px;flex:none;margin:0;cursor:pointer;
  accent-color:var(--navy-ink);
}
.check:hover{color:var(--ink)}

/* ---------- the payment ledger on a job ----------
   The status pill and the figures share one row, so "Unpaid" and the
   balance it refers to are read as one statement rather than as a badge
   with a table under it. */
.paybar{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap}
.paybar .status{flex:none;position:relative;top:-1px}
/* Narrower than .dl-row's 86px: these labels are one word and the money
   beside them is the thing worth reading, so the column closes up. */
.paysum{display:grid;gap:4px;margin:0}
.paysum .dl-row{grid-template-columns:64px 1fr;gap:8px}

/* One payment per line. Ruled rather than spaced, the same reasoning as
   the services list: three payments separated by whitespace read as one
   block, and a row with a note on it read as two rows. */
.paylist{list-style:none;margin:10px 0 0;padding:0}
.payrow{
  display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;
  padding:7px 0;border-bottom:1px solid var(--line-2);
}
.payrow:last-child{border-bottom:none}
.payrow b{font-family:var(--mono);font-size:12.5px;color:var(--ink);font-weight:600}
.paymeta{font-size:12px;color:var(--ink-3)}
/* The note wraps to its own line rather than squeezing the amount, and
   stays quiet — it is context for the payment, not part of it. */
.paynote{
  flex:1 1 100%;font-style:normal;font-size:12px;color:var(--ink-2);
}
/* The remove button sits right, and only once there is something to its
   left worth pushing it away from. */
.payrow .iconbtn{margin-left:auto;width:26px;height:26px;font-size:15px}

.payfoot{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:11px}

.dfiles{margin-top:6px}
/* Thumbnails first, then anything with no preview to show. Both lists are
   always in the DOM so a finished upload has somewhere to append to; empty
   ones collapse rather than leaving a gap. */
.fgrid{
  list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:repeat(auto-fill,minmax(116px,1fr));gap:10px;
}
.fgrid:empty,.flist:empty{display:none}
.fgrid + .flist{margin-top:10px}
.ftile{display:flex;flex-direction:column;gap:5px;min-width:0}
.fthumb{
  display:block;width:100%;aspect-ratio:4/3;padding:0;cursor:pointer;
  border:1px solid var(--line);border-radius:9px;overflow:hidden;
  background:var(--raise);
}
.fthumb:hover{border-color:var(--ink-3)}
.fthumb:focus-visible{outline:2px solid var(--navy-ink);outline-offset:2px}
/* No src until the signed URL lands, so the tile fades in rather than
   flashing a broken-image glyph while it waits. */
.fthumb img{width:100%;height:100%;object-fit:cover;display:block;opacity:0;transition:opacity .18s ease}
.fthumb img[src]{opacity:1}
.ftile.failed .fthumb{border-style:dashed;border-color:var(--warn)}
.fcap{display:flex;align-items:center;gap:8px;font-size:11px;min-width:0}
.fcap .fsize{flex:1 1 auto;min-width:0}
.flist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:1px}
.flist li{
  display:flex;align-items:center;gap:10px;
  padding:8px 10px;border-radius:8px;font-size:12.5px;
}
.flist li:hover{background:var(--raise)}
/* The name takes the slack and truncates; everything after it is a fixed
   width, so the remove control sits in the same place on every row. */
.fname{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fsize{flex:none;font-style:normal;color:var(--ink-3);font-family:var(--mono);font-size:11px}
.flist .link{flex:none}

/* ---------- services ----------
   The price list. Names are `.svc*` and `.band*`: checked against this
   file before use, and clear of "share", "social", "ad" and "banner",
   which ad-blocker cosmetic filters hide on sight. A `.frame` collision
   between this stylesheet and the delivery page already cost a night —
   a new class here gets grepped first, every time. */
.svcset{margin-bottom:30px}
.svctitle{
  margin:0 0 4px;font-family:var(--display);font-size:17px;font-weight:600;
  letter-spacing:-.01em;color:var(--ink);
  padding-bottom:9px;border-bottom:1px solid var(--line);
}
.svctitle i{font-style:normal;font-size:12.5px;color:var(--ink-2);font-family:var(--sans)}
/* Same mono-caps as the drawer's column heads, so "Base" and "Add-ons"
   read as labels on a list rather than as headings of their own. */
.svcsub{
  margin:18px 0 2px;font-family:var(--mono);font-size:10.5px;font-weight:400;
  letter-spacing:.12em;text-transform:uppercase;color:var(--ink-2);
}
/* Name, then prices, then the controls — the controls sit in the same
   place on every row so a column of Edit links is scannable. */
/* A line under every row. Thirty services with nothing between them but
   whitespace was the "hard to read" — the eye had no rail to run along,
   and a two-line row (name over description) read as two rows. The rule
   is --line-2 so it separates without drawing itself; the section's own
   --line under .svctitle stays the heavier one. */
.svcrow{
  display:grid;grid-template-columns:minmax(160px,1.1fr) minmax(0,2fr) auto;
  gap:14px;align-items:center;
  padding:13px 11px;border-radius:9px;
  border-bottom:1px solid var(--line-2);
}
/* The last row of a list closes on the next heading's own margin, so it
   does not need a rule of its own. */
.svcrow:last-child{border-bottom:0}
.svcrow:hover{background:var(--raise)}
/* Retired, not deleted: it stays legible, just visibly not for sale. */
.svcrow.is-off .svcname{color:var(--ink-3)}
.svcname{display:flex;align-items:center;gap:9px;flex-wrap:wrap;font-size:14px;min-width:0}
/* Fixed box whether or not there's a glyph in it, so the names line up
   down the column and an iconless service doesn't sit 25px to the left. */
.svcicon{
  display:inline-flex;align-items:center;justify-content:center;
  width:21px;height:21px;flex:none;color:var(--ink-2);
}
/* Thicker than the 1.6 svgPath() draws with. These are 19px line drawings
   on a dark panel, and a hairline stroke at that size reads as a smudge. */
.svcicon svg{stroke-width:1.9}
.svcrow:hover .svcicon{color:var(--ink)}
.svctext{min-width:0}
.svcname i{font-style:normal;font-size:11.5px;color:var(--ink-3);font-family:var(--mono)}
.svcname em{
  font-style:normal;font-size:12.5px;color:var(--ink-2);
  flex:1 1 100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  margin-top:2px;
}
.svcflag{
  font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-2);border:1px solid var(--line);border-radius:999px;padding:2px 8px;
}
.svcflag.is-warn{color:var(--warn);border-color:rgba(226,160,106,.35);cursor:help}
.svcbands{display:flex;align-items:center;gap:7px;flex-wrap:wrap;min-width:0}
.svcband{
  font-size:12px;color:var(--ink-2);font-family:var(--mono);
  background:var(--raise);border:1px solid var(--line);border-radius:7px;padding:4px 9px;
  white-space:nowrap;
}
.svcrow:hover .svcband{background:var(--bg)}
.svcband b{color:var(--ink);font-weight:600}
.svcband.is-flat{border-style:dashed}
.svcnote{font-size:12.5px;color:var(--ink-3);font-style:italic}
.svcacts{display:flex;align-items:center;gap:5px;justify-content:flex-end}
/* Three controls in the width one word used to take.

   They used to sit at opacity .55 in --ink-3 and only come up to full on
   row hover, on the theory that a row of bright buttons would out-shout
   the prices. On a wide row that put three near-invisible glyphs a long
   way right of anything else on the line, and hover is no answer at all
   on a touch screen — you cannot hover to find out whether there is
   anything there to press. They are legible at rest now: --ink-2, full
   opacity, a 32px target and a 2 stroke. Hover still lifts them to --ink
   with a surface behind, so the row you are on is still the loud one. */
.iconbtn{
  display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:32px;padding:0;flex:none;
  background:none;border:1px solid transparent;border-radius:8px;
  color:var(--ink-2);transition:.14s;
}
.iconbtn svg{stroke-width:2}
.iconbtn:hover{background:var(--raise);border-color:var(--line);color:var(--ink)}
.iconbtn.is-danger:hover{background:var(--warn-bg);border-color:rgba(226,160,106,.3);color:var(--warn)}
/* Keyboard users never get the hover that reveals these, so focus has to
   do it on its own. */
.iconbtn:focus-visible{outline:2px solid var(--navy-ink);outline-offset:1px}
/* An empty job type invites the first service rather than hiding: a type
   with no prices is a decision nobody has made yet, not an absence. */
.svcnone{
  display:flex;align-items:center;gap:9px;flex-wrap:wrap;
  padding:11px 12px;font-size:12.5px;color:var(--ink-3);
  border:1px dashed var(--line);border-radius:9px;
}

/* The icon picker. A grid of the actual drawings, because picking one off
   a list of names is what this is meant to replace. */
.iconset{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(38px,1fr));
  gap:6px;max-width:100%;
}
.iconpick{
  display:flex;align-items:center;justify-content:center;
  aspect-ratio:1;padding:0;color:var(--ink-3);
  background:var(--bg);border:1px solid var(--line);border-radius:9px;transition:.14s;
}
.iconpick:hover{border-color:var(--ink-3);color:var(--ink)}
.iconpick:focus-visible{outline:2px solid var(--navy-ink);outline-offset:1px}
.iconpick.on{border-color:var(--navy-ink);background:var(--navy-dim);color:var(--ink)}
.iconnone{font-family:var(--mono);font-size:13px}

/* Bands, in the editor. From / to / price on one line so a whole set can
   be read down its columns the way a rate card is.

   Its own container class, not the `.svcbands` the price chips use: that
   one is a wrapping flex row built to lay chips out side by side, and a
   stack of grid rows is the opposite shape. Sharing it worked by accident
   and would have broken the first time either changed. */
.bandset + .jf-foot{margin-top:8px}
.bandrow{
  display:grid;grid-template-columns:1fr 12px 1fr 1fr auto;
  gap:8px;align-items:center;margin-bottom:8px;
}
.bandto{text-align:center;color:var(--ink-3);font-size:12px}
.bandcell{
  min-width:0;font-family:var(--mono);font-size:12px;color:var(--ink);
  background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:8px 10px;
}
.bandcell:focus{outline:none;border-color:var(--navy-ink)}
/* Warnings, never a block — the real price list has judgment calls in it
   and a form that refuses to save gets worked around instead of used. */
.bandwarn{
  display:flex;flex-direction:column;gap:4px;
  margin:10px 0 0;padding:10px 12px;font-size:12px;color:var(--warn);
  background:var(--warn-bg);border:1px solid rgba(226,160,106,.3);border-radius:9px;
}
.bandwarn[hidden]{display:none}

@media(max-width:700px){
  /* Three columns of prices next to a name is a wrap on a phone; stack
     instead, controls last. */
  .svcrow{grid-template-columns:1fr;gap:8px}
  .svcacts{justify-content:flex-start}
}

/* ---------- job form ---------- */
/* The Edit control sits under the drawer's three columns, on its own rule,
   so it reads as an action on the whole job rather than on the last column. */
.dfoot{
  display:flex;align-items:center;gap:12px;
  margin-top:18px;padding-top:14px;border-top:1px solid var(--line-2);
}
/* Taller than the changelog: this one is a form, and a 70vh box turns
   seventeen fields into a scroll hunt. */
.modal-form{width:min(680px,calc(100vw - 32px));max-height:min(88vh,900px)}
.modal-form .modal-body{max-height:calc(88vh - 56px)}
.modal-form .group{margin-bottom:24px}
.modal-form .group:last-of-type{margin-bottom:8px}
/* [hidden] is a UA rule, and .field's own display:grid outranks it — the
   contact picker would stay on screen underneath the new-contact fields. */
.modal-form [hidden]{display:none}
/* The combobox sits in a .field's second column, which is a grid track —
   .sug-wrap is flex:1 1 240px by default and would overflow it. */
.modal-form .sug-wrap{flex:none;width:100%}
.modal-form .search{margin:0;padding:5px 7px;border-radius:4px;
  border:1px solid transparent;border-bottom:1px solid var(--line);background:transparent}
.modal-form .search:hover{background:var(--raise)}
.jf-foot{display:flex;align-items:center;gap:12px;margin-top:10px}
.jf-foot .link{margin-left:auto;white-space:nowrap}

/* ---------- address autocomplete ---------- */
.sug-wrap{position:relative;display:block;flex:1 1 240px;min-width:0}
.sug-wrap .search{width:100%;max-width:none}
.sug{
  position:absolute;left:0;right:0;top:calc(100% + 5px);z-index:30;
  background:var(--raise);border:1px solid var(--line);border-radius:11px;
  padding:5px;box-shadow:0 12px 30px rgba(0,0,0,.45);
  max-height:260px;overflow-y:auto;
}
.sug-item{
  display:block;width:100%;text-align:left;border:none;background:none;
  font-family:inherit;font-size:13px;color:var(--ink-2);
  padding:9px 11px;border-radius:7px;line-height:1.45;
}
.sug-item:hover,.sug-item.on{background:var(--navy);color:#fff}

/* A contact row carries a name, optionally a lead tag, and whatever
   identifies them underneath — two people share a name often enough here
   that the name alone is not a choice. */
.sug-item b{font-weight:600;color:var(--ink)}
.sug-item u{
  text-decoration:none;margin-left:7px;padding:1px 6px;border-radius:99px;
  font-size:10.5px;letter-spacing:.04em;text-transform:uppercase;
  background:var(--raise);border:1px solid var(--line);color:var(--ink-3);
}
.sug-item i{display:block;margin-top:2px;font-style:normal;font-size:11.5px;color:var(--ink-3)}
.sug-item:hover b,.sug-item.on b{color:#fff}
.sug-item:hover i,.sug-item.on i,
.sug-item:hover u,.sug-item.on u{color:rgba(255,255,255,.82)}
.sug-item:hover u,.sug-item.on u{background:rgba(255,255,255,.14);border-color:transparent}

/* Not a row: nothing here is selectable, so it must not look like it is. */
.sug-note{
  display:block;padding:9px 11px;font-size:11.5px;line-height:1.5;color:var(--ink-3);
}
.sug-note b{font-weight:600;color:var(--ink-2)}

/* A contact is chosen: the box stops looking like an empty search field. */
.field.chosen .search{
  border-bottom-color:var(--navy-ink);
  background:rgba(6,104,162,.08);
  color:var(--ink);
}

/* ============================================================
   ANALYTICS — the sourcing and calling half.

   Nothing here is a dashboard tile. Every figure on the page carries its
   denominator in text beside it, which is the page's whole claim to being
   honest on a dataset a few days old, so the styling's one job is to keep
   that denominator legible rather than shrinking it into a caption. That
   is why .anx-read sets the number in the display face and leaves the
   "of 32" at reading size next to it, instead of the usual big-number
   treatment that would make the rate shout and the denominator whisper.
   ============================================================ */

/* A reading page, not a dashboard that fills whatever monitor it is
   opened on. Same reasoning as #view-home's 900px: these are sentences
   with numbers in them, and a line of prose 1,800px wide is unreadable
   however good the numbers are. Wider than Home because two charts sit
   side by side here. */
#view-analytics{max-width:1240px}

/* THE CAP THAT KEEPS THE CHARTS HONEST SIZES.

   `.chart` is width:100% over a fixed viewBox, so an SVG in a 900px
   column scales three times and drags its 9px type up to 27px with it —
   headline-sized axis labels, values crashing through their own bars.
   That is exactly what shipped in 0.20.0. The charts are drawn for a
   card and they stay drawn for a card: this caps the scale at about
   1.25x, which is the largest they read as charts rather than as
   posters. Anything that wants to be bigger needs a bigger viewBox, not
   a bigger cap — see COMPARE in analytics.js. */
.anx .chart{max-width:520px}
/* chartOrders still draws into the original 300-unit box where
   chartCompare now uses 420, so one cap would render the two at
   different type sizes on the same page. 400px puts the bar chart at
   the same ~1.25x the compare charts sit at, which is what makes the
   axis labels match rather than merely both being "not huge". */
.anleak .chart{max-width:400px}

/* ---- the two tabs ----
   A row of two, sitting under the page heading rather than in the
   sidebar: they are two readings of one subject, not two places to go.
   The active one carries the underline, which is the whole signal —
   pills here would compete with the filter chips on Sourcing for the
   same meaning. */
.antabs{
  display:flex;gap:22px;align-items:center;
  margin:16px 0 0;border-bottom:1px solid var(--line-2);
}
.antab{
  background:none;border:0;padding:0 0 10px;cursor:pointer;
  font-family:var(--sans);font-size:13.5px;color:var(--ink-3);
  border-bottom:2px solid transparent;margin-bottom:-1px;transition:.12s;
}
.antab:hover{color:var(--ink-2)}
.antab.on{color:var(--ink);border-bottom-color:var(--navy-ink);font-weight:600}
.antab:focus-visible{outline:2px solid var(--navy-ink);outline-offset:3px;border-radius:3px}
/* The first section under the tab row has the row's own rule above it
   already, so it must not draw a second one two pixels below. */
.antabs + .anstanding + .anx{border-top:0;padding-top:6px;margin-top:20px}

/* The caveat that has to survive being skim-read: every money figure on
   the Business tab is billed, not banked. Set on its own line in the
   warn colour rather than inline in the sentence, because a reader who
   takes only one thing from that paragraph has to take this one. */
.bizwarn{display:block;margin-top:6px;color:var(--warn);font-weight:600}

/* The gaps, named. Muted, but a list rather than a paragraph — each of
   these is a separate absence with a separate reason. */
.bizgap{margin:0;padding-left:18px;max-width:74ch}
.bizgap li{color:var(--ink-3);font-size:12.5px;line-height:1.6;margin-bottom:8px}
.bizgap b{color:var(--ink-2)}
.bizgap code{font-family:var(--mono);font-size:11.5px;color:var(--ink-2)}

/* The heaviest accounts stack rather than sitting in a grid — five rows
   read as a ranking, where five cards read as five equal things. */
.biztop{grid-template-columns:1fr;margin-top:12px}

/* The page's denominator, once, under the heading. Sized as prose because
   it is a sentence and not a stat. */
.anstanding{
  color:var(--ink-2);font-size:13.5px;margin:2px 0 0;max-width:78ch;line-height:1.6;
}

.anx{margin:36px 0 0;padding-top:20px;border-top:1px solid var(--line-2)}
.anx h2{
  font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);margin:0 0 14px;
}
.anx h3{font-size:12.5px;font-weight:600;color:var(--ink-2);margin:0 0 10px}

/* A reading, as a sentence with one number set large inside it. */
.anx-read{font-size:13.5px;color:var(--ink-2);margin:12px 0 0;max-width:74ch;line-height:1.6}
.anx-read b{
  font-family:var(--display);font-size:17px;font-weight:600;color:var(--ink);
  font-variant-numeric:tabular-nums;
}
/* What a section needs before it can say anything, and the caveats on the
   ones that can. Muted, because it is the section explaining itself — but
   never hidden, since on this page the reason a figure is missing is
   frequently the most useful thing on screen. */
.anx-note{color:var(--ink-3);font-size:12.5px;margin:10px 0 0;max-width:74ch;line-height:1.55}

/* Two charts that are read against each other sit side by side, and stack
   rather than squeeze — a chartCompare with a 96px label gutter stops
   being readable well before the column does. */
.anxpair{display:grid;grid-template-columns:repeat(auto-fit,minmax(330px,1fr));gap:22px 32px}
.anxhalf{min-width:0}
/* The note under a chart belongs to the chart, so it wraps to the chart's
   width rather than running the full column and reading as page prose. */
.anxhalf .anx-note{max-width:520px}

/* ---- the funnel ----
   One line, because the shape of the drop-off is the reading and four
   separate cards would make it four readings. It wraps rather than
   scrolls: a funnel with a segment off the right edge is a funnel with a
   step nobody knows about. */
.funnel{display:flex;flex-wrap:wrap;align-items:stretch;gap:10px}
.fstep{
  flex:1 1 150px;min-width:0;
  border:1px solid var(--line);border-radius:11px;padding:11px 14px;background:var(--panel);
}
.fstep-k{
  display:block;font-size:11px;font-weight:600;letter-spacing:.08em;
  text-transform:uppercase;color:var(--ink-3);margin-bottom:5px;
}
/* The count and its denominator on one baseline. The denominator is not
   a caption under the number — it sits beside it at reading size, because
   "1" and "of 32" are one fact and stacking them lets the eye take the
   first without the second. */
.fstep-v{
  display:flex;align-items:baseline;gap:7px;flex-wrap:wrap;
  font:inherit;text-align:left;
}
.fstep-v b{
  font-family:var(--display);font-size:23px;font-weight:600;color:var(--ink);
  font-variant-numeric:tabular-nums;line-height:1.1;
}
.fstep-v i{font-style:normal;font-size:12px;color:var(--ink-2)}
/* A segment that opens Sourcing is a button, and has to look like one at
   rest — the same reasoning as .iconbtn: hover is not a thing a phone
   has, so a control nobody can discover by hovering has to announce
   itself standing still. */
button.fstep-v{
  cursor:pointer;background:none;border:0;padding:0;width:100%;
}
button.fstep-v b{color:var(--navy-ink)}
button.fstep-v:hover b{color:var(--ink)}
button.fstep-v:hover i{color:var(--ink)}
button.fstep-v:focus-visible{outline:2px solid var(--navy-ink);outline-offset:3px;border-radius:4px}

/* The per-person split reuses .crew wholesale — same shape, same meaning,
   a name with counts under it. Unattributed rows are dimmed but present:
   the work happened and its denominator counts it, only the author is
   unknown. */
.anpeople{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:10px}
.anpeople .crew.is-muted .crew-n{color:var(--ink-3);font-weight:500}

/* Above the clients breakpoint the funnel is four across; below it two,
   then one, so a segment never gets narrower than its own number. */
@media (max-width:700px){
  .fstep{flex:1 1 100%}
  .anxpair{grid-template-columns:1fr}
}
