/* ─────────────────────────────────────────────────────────────────────────
   app.css — the screens the design system does not already cover.

   noti.css holds the product's look. This holds the gate (sign in, onboarding)
   and the handful of app-only pieces: toasts, empty rows, form fields. If a
   rule here would be useful on any screen, it belongs in noti.css instead.
   ───────────────────────────────────────────────────────────────────────── */

/* ── the gate: sign in, sign up, onboarding ─────────────────────────────── */
.gate{min-height:100vh;display:flex;flex-direction:column;align-items:center;
  justify-content:center;padding:28px 18px;gap:20px}
.gate-card{background:var(--white);border-radius:var(--r);padding:38px 34px;width:100%;
  max-width:430px}
.gate.wide .gate-card{max-width:660px}
.gate-logo{justify-content:center;padding-bottom:22px}
.gate-card .logo b{color:var(--ink)}
.gate-card h1{font-size:26px;font-weight:800;letter-spacing:-.03em;line-height:1.15}
.gate-sub{font-size:15px;color:var(--grey);font-weight:500;margin-top:8px;line-height:1.55}
.gate-sub b{color:var(--ink);font-weight:700}
.gate-form{display:flex;flex-direction:column;gap:13px;margin-top:24px}
/* ── Google and Apple ─────────────────────────────────────────────────────
   Both vendors are strict about their buttons and both are right to be: a
   sign-in button that looks approximate looks like a phishing page. So the
   marks are theirs, drawn in their colours, on a plain surface — white for
   Google as their guidelines require, black for Apple as theirs do.

   Stacked, not side by side. Two half-width buttons at 360px put "Continue
   with Google" on three lines each. */
.oauth{display:flex;flex-direction:column;gap:9px;margin-top:22px}
.oauthb{display:flex;align-items:center;justify-content:center;gap:10px;
  width:100%;padding:13px 16px;border-radius:99px;cursor:pointer;
  background:#fff;border:1.5px solid var(--line);color:var(--ink);
  font-size:15px;font-weight:700;letter-spacing:-.01em;
  transition:border-color .15s, background .15s}
.oauthb:hover{border-color:var(--ink)}
.oauthb:disabled{opacity:.55;cursor:default}
.oauthb svg{flex:none}
.oauthb.apple{background:#000;border-color:#000;color:#fff}
.oauthb.apple:hover{background:#1c1c1e;border-color:#1c1c1e}

/* A rule with a reason on it. Without the words it reads as decoration; with
   them it says the form underneath is the other option, not the only one. */
.oauth-or{display:flex;align-items:center;gap:12px;margin:20px 0 -4px;
  font-size:12px;font-weight:700;color:var(--grey);
  text-transform:uppercase;letter-spacing:.06em}
.oauth-or::before,.oauth-or::after{content:'';flex:1;height:1px;background:var(--line)}

.gate-alt{font-size:14px;color:var(--grey);font-weight:500;margin-top:20px;text-align:center}
.gate-foot{font-size:13.5px;color:var(--grey);font-weight:500}
.gate-foot b{color:var(--ink);font-weight:700}
/* The Terms and Privacy links: under every gate, and the sentence beside the
   button that makes pressing it the acceptance (legal/NOTICES.md). */
.gate-legal{font-size:13px;color:var(--grey);margin:8px 0 0}
.gate-legal a,.legal-line a,.legal-gate a{color:inherit;text-decoration:underline;text-underline-offset:2px}
.legal-line{font-size:13px;line-height:1.45;color:var(--mid);margin:4px 0 10px}

.fld{display:flex;flex-direction:column;gap:6px}
.fld span{font-size:13px;font-weight:700;color:var(--mid)}
/* A value that is not the person's to change - the address an invite was
   sent to. Text, so a long one wraps instead of hiding in a read-only box. */
.fld-fixed{margin:0;font-size:16px;font-weight:600;color:var(--ink);overflow-wrap:anywhere}
/* `.typerow input` rides along rather than getting its own copy. The two
   comments below record the same mistake happening to textarea and to
   select: a control styled only inside .fld renders as the browser's own
   square grey default everywhere else, next to a rounded cream field and
   obviously not part of the same form. Third time. */
.fld input,.typerow input,.gap input{border:1.5px solid var(--line);background:var(--cream-2);border-radius:14px;
  padding:14px 16px;font-size:15px;font-family:inherit;color:var(--ink);outline:none;
  font-weight:500;width:100%}
/* A textarea in a .fld was only ever styled inside .panel, so the same field
   on the booking page rendered as the browser's default: monospace, square,
   and obviously not part of the form above it. */
.fld textarea{border:1.5px solid var(--line);background:var(--cream-2);
  border-radius:14px;padding:13px 15px;font:inherit;font-size:15px;
  font-weight:600;color:var(--ink);outline:none;width:100%;resize:vertical;
  line-height:1.5}
.fld textarea:focus{border-color:var(--orange);background:var(--white)}
.fld input::placeholder,.typerow input::placeholder,.fld textarea::placeholder{color:var(--faint);font-weight:500}
.fld input:focus,.typerow input:focus,.gap input:focus{border-color:var(--orange);background:var(--white)}
/* And a select was never styled at all, anywhere — the comment above says
   the same thing happened to textarea. So every dropdown in the product has
   been the browser's own: square, grey, a native arrow, sitting next to a
   rounded cream input and obviously not part of the same form.

   appearance:none plus a drawn chevron. The padding leaves room for it so
   a long customer name does not run underneath. */
/* `.gap select` rides along too. It did not, and rendered as a 19px native
   dropdown beside a 50px input — the fourth time a control styled only
   inside .fld has shown up as the browser's own somewhere else. Both
   comments above describe the same thing happening to textarea and select.
   The pattern is the lesson: style the CONTROL, not the container. */
.fld select,.gap select{border:1.5px solid var(--line);background:var(--cream-2);
  border-radius:14px;padding:14px 40px 14px 16px;font:inherit;font-size:15px;
  font-weight:500;color:var(--ink);outline:none;width:100%;cursor:pointer;
  -webkit-appearance:none;appearance:none;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236F6459' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat:no-repeat;background-position:right 15px center}
.fld select:focus,.gap select:focus{border-color:var(--orange);background-color:var(--white)}

.linkish{font-size:14px;font-weight:700;color:var(--orange);text-decoration:underline;
  text-underline-offset:2px}

/* the gate's buttons are full width; the app's are not, so .big is redefined
   rather than shared */
.gate .big,.onb .big{width:100%;margin:0;padding:15px;border-radius:99px;background:var(--orange);
  color:#fff;font-size:15.5px;font-weight:800;display:flex;align-items:center;
  justify-content:center;gap:8px;letter-spacing:-.01em;border:0}
.gate .big:hover,.onb .big:hover{background:var(--orange-d)}
.gate .big:disabled{opacity:.55;cursor:default}
.gate .big.ghost,.onb .big.ghost{background:var(--cream);color:var(--ink)}
.gate .big.ghost:hover,.onb .big.ghost:hover{background:var(--line)}

/* ── onboarding ─────────────────────────────────────────────────────────── */
.onb-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:24px}
.onb-top .logo{padding:0}
.onb-top .logo b{color:var(--ink);font-size:22px}
.steps{display:flex;gap:6px}
.steps .dot{width:7px;height:7px;border-radius:50%;background:var(--line)}
.steps .dot.past{background:var(--faint)}
.steps .dot.on{background:var(--orange);width:22px;border-radius:99px}

.onb-nav{display:flex;gap:10px;margin-top:24px}
.onb-nav .big{flex:1}

.typegrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:9px;
  margin-top:22px}
.type{text-align:left;padding:15px 16px;border-radius:var(--r2);background:var(--cream-2);
  border:2px solid transparent;transition:.14s}
.type:hover{border-color:var(--line)}
.type.on{background:var(--orange);border-color:var(--orange)}
.type b{display:block;font-size:15px;font-weight:700;letter-spacing:-.015em}
.type span{display:block;font-size:12.5px;color:var(--grey);margin-top:3px;font-weight:500}
.type.on b,.type.on span{color:#fff}
.type.on span{opacity:.8}

/* NO INNER SCROLL. This was a 46vh box with its own scrollbar inside a card
   that does not scroll, so fourteen questions became five and a half — the
   sixth sliced through the middle of its own row, hard against the buttons.
   It reads as broken rather than as more, which is the worst outcome for a
   list somebody is supposed to go through.

   The page scrolls instead. A long page is a long page; a short window onto
   one is a puzzle. */
.qgroup{margin-top:22px}
.qgroup-h{font-size:12px;font-weight:800;letter-spacing:.09em;
  text-transform:uppercase;color:var(--faint);margin-bottom:9px}
.qlist{display:flex;flex-direction:column;gap:7px}
.q{display:flex;align-items:center;gap:13px;padding:13px 15px;border-radius:var(--r2);
  background:var(--cream-2);text-align:left;border:2px solid transparent;transition:.14s}
.q:hover{border-color:var(--line)}
.q.on{background:var(--orange-l);border-color:var(--orange)}
.q .box{width:26px;height:26px;border-radius:9px;background:var(--white);
  border:1.5px solid var(--line);display:flex;align-items:center;justify-content:center;flex:none}
.q .box svg{width:15px;height:15px;stroke-width:3;color:transparent}
.q.on .box{background:var(--orange);border-color:var(--orange)}
.q.on .box svg{color:#fff}
.q .t{flex:1;min-width:0}
.q .t b{display:block;font-size:14.5px;font-weight:700;letter-spacing:-.01em}
.q .t span{display:block;font-size:12.5px;color:var(--grey);margin-top:2px;font-weight:500}

/* ── how the work actually runs ─────────────────────────────────────────
   `.oq`, not `.ask`. system.css:3253 already has an `.ask` — a four-column
   grid for a row in the attention list — and it won, because system.css
   loads after app.css. The questions rendered as a heading, its explanation
   and its answers side by side in one row, with every heading broken one
   word per line. FIFTH TIME in this codebase that two screens have shared a
   class name; the measurement said "9 questions visible" and looked fine.

   A conversation, not a form. Each question is its own block with room
   around it, and the next one does not exist until this one is answered —
   so the screen is short on arrival and grows as somebody talks.

   Deliberately NOT .qlist: that is sixteen ticks in a scrolling box, which
   is the right shape for "which of these sound like you" and the wrong one
   for a question that deserves reading. */
.oq{margin-top:30px}
.oq:first-of-type{margin-top:24px}
.oq h2{font-size:18px;font-weight:800;letter-spacing:-.025em;line-height:1.3}
.oq-why{font-size:13.5px;line-height:1.55;color:var(--grey);font-weight:500;
  margin-top:6px;max-width:52ch}

/* A follow-up belongs to the question above it, so it is indented by a rule
   rather than separated by a heading. */
.oq--sub{margin-top:16px;padding-left:16px;border-left:2px solid var(--line)}
.oq--sub h2{font-size:15.5px;font-weight:700}

.picks{display:grid;grid-template-columns:repeat(auto-fit,minmax(196px,1fr));
  gap:8px;margin-top:14px}
.picks--two{grid-template-columns:1fr 1fr;max-width:280px}
.pick{text-align:left;padding:13px 15px;border-radius:var(--r2);
  background:var(--cream-2);border:2px solid transparent;transition:.14s}
.pick:hover{border-color:var(--line)}
.pick.on{background:var(--orange-l);border-color:var(--orange)}
.pick b{display:block;font-size:14.5px;font-weight:700;letter-spacing:-.01em}
.pick span{display:block;font-size:12.5px;color:var(--grey);margin-top:3px;
  font-weight:500;line-height:1.4}
.pick.on b{color:var(--orange-d)}

.oq-row{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:14px}

/* Every [ 6 ] [ months v ]. The sentence reads left to right and the two
   controls are part of it rather than a labelled form — a number box with
   "How many" above it and a dropdown with "Unit" above it is the same
   question asked like a tax return. */
.gap{display:flex;align-items:center;gap:9px;margin-top:14px;flex-wrap:wrap}
.gap-pre{font-size:15.5px;font-weight:600;color:var(--mid)}
.gap input{width:92px;flex:none;text-align:center;font-variant-numeric:tabular-nums}
/* Chrome puts spinners on a number input that are 12px of hit area each and
   do nothing a thumb can use. */
.gap input::-webkit-outer-spin-button,
.gap input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.gap input[type=number]{-moz-appearance:textfield;appearance:textfield}
/* Overrides the width:100% it inherits from .fld select, which is what
   pushed it onto its own line. */
.gap select{flex:0 1 auto;width:auto;min-width:138px}
.gap input:disabled,.gap select:disabled{opacity:.45}

.gap-varies{display:flex;align-items:center;gap:10px;margin-top:12px;
  font-size:13.5px;font-weight:600;color:var(--grey)}
.gap-varies .box{width:22px;height:22px;border-radius:7px;background:var(--cream-2);
  border:1.5px solid var(--line);display:flex;align-items:center;justify-content:center;flex:none}
.gap-varies .box svg{width:13px;height:13px;stroke-width:3;color:transparent}
.gap-varies.on{color:var(--ink)}
.gap-varies.on .box{background:var(--orange);border-color:var(--orange)}
.gap-varies.on .box svg{color:#fff}

.typelist{display:flex;flex-direction:column;gap:7px;margin-top:14px}
.typerow{display:flex;gap:8px;align-items:center}
.typerow input{flex:1;min-width:0}   /* shape comes from .fld input above */
.typex{width:40px;height:40px;flex:none;border-radius:12px;display:grid;
  place-items:center;color:var(--faint);background:var(--cream-2)}
.typex:hover{color:var(--orange-d);background:var(--orange-l)}
.typex svg{width:16px;height:16px}
.typeadd{display:flex;align-items:center;gap:7px;align-self:flex-start;
  padding:9px 14px;border-radius:99px;background:var(--cream-2);
  font-size:13.5px;font-weight:700;color:var(--ink)}
.typeadd:hover{background:var(--line)}
.typeadd svg{width:15px;height:15px}

@media (max-width:560px){
  /* One decision per row on a phone. Two 196px columns in a 390px viewport
     is two columns of clipped text, which is how a calm screen becomes a
     cramped one. */
  .picks,.oq-row{grid-template-columns:1fr;max-width:none}
  /* Yes and No stay side by side. Two 48px buttons fit at 390px with room
     to spare, and stacking them makes a binary answer take as much of the
     screen as a four-way choice. */
  .picks--two{grid-template-columns:1fr 1fr;max-width:none}
  .oq{margin-top:26px}
  .oq h2{font-size:17px}
}

/* ── the preview step ───────────────────────────────────────────────────── */
.pv{margin-top:22px;border-radius:var(--r2);background:var(--cream-2);overflow:hidden}
.pv-row{display:flex;justify-content:space-between;gap:14px;padding:13px 16px;
  border-bottom:1px solid var(--line)}
.pv-row:last-child{border-bottom:0}
.pv-k{font-size:13.5px;color:var(--grey);font-weight:600}
.pv-v{font-size:14px;font-weight:700;text-align:right}
.pv-lab{font-size:12px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  color:var(--faint);margin:22px 0 10px}
.pv-note{font-size:12.5px;color:var(--faint);font-weight:500;margin-top:9px}

.modgrid{display:flex;flex-wrap:wrap;gap:7px}
.mod{display:inline-flex;align-items:center;gap:7px;background:var(--ink);color:#fff;
  border-radius:99px;padding:8px 14px;font-size:13px;font-weight:700}
.mod svg{width:15px;height:15px}
/* comes with Base: shown, not promised as there today (1 Oct 2026) */
.mod.later{background:transparent;color:var(--mid);box-shadow:inset 0 0 0 1px var(--line)}

.rulelist{display:flex;flex-direction:column;gap:6px}
.rule{display:flex;align-items:center;gap:10px;background:var(--mint);color:var(--mint-d);
  border-radius:var(--r3);padding:11px 14px;font-size:13.5px;font-weight:600}
.rule svg{width:15px;height:15px;stroke-width:3;flex:none}
.rule.later{background:var(--paper, #f4efe8);color:var(--mid)}
.pv-later{font-style:normal;font-weight:500;color:var(--faint);font-size:12px;margin-left:6px}
.rule.later em{font-style:normal;font-weight:500;color:var(--faint);margin-left:auto}

/* ── app-only bits ──────────────────────────────────────────────────────── */
.emptyrow{padding:26px 16px;text-align:center;font-size:13.5px;color:var(--faint);
  font-weight:500;line-height:1.6}

.hello .r .chip{border:0;cursor:pointer}
.hello .r .chip svg{width:15px;height:15px;color:inherit}

.toast{position:fixed;left:50%;bottom:26px;transform:translate(-50%,14px);background:var(--ink);
  color:#fff;padding:13px 20px;border-radius:99px;font-size:14px;font-weight:600;z-index:200;
  opacity:0;transition:.22s;box-shadow:0 14px 34px -10px rgba(0,0,0,.4);max-width:90vw}
.toast.in{opacity:1;transform:translate(-50%,0)}
.toast.warn{background:var(--peach-d)}

@media (max-width:640px){
  .gate-card{padding:28px 22px}
  .typegrid{grid-template-columns:1fr}
}

/* ── the dialogue ───────────────────────────────────────────────────────── */
/* It was a full-height slide-over. The reasoning was that what you are
   editing usually refers to what is behind it — true of an invoice line,
   not true of "New sale", which is three fields stretched over 900px of
   empty column while the thing it supposedly refers to is hidden behind
   the sheet anyway.

   So: centred, and only as tall as what is in it. A three-field form is a
   small box in the middle of the screen; a long one grows to 86vh and
   scrolls its own body. Same component, same markup, one set of rules.

   A phone keeps the sheet. A centred box on a phone is a box that jumps
   when the keyboard opens, and from the bottom edge is where a thumb
   already is. §9 */
.panel-wrap{position:fixed;inset:0;z-index:150;display:flex;
  align-items:center;justify-content:center;padding:24px}
.panel-bg{position:absolute;inset:0;background:rgba(26,22,19,.42);opacity:0;transition:.22s;
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}
.panel-wrap.in .panel-bg{opacity:1}
.panel{position:relative;width:min(460px,100%);background:var(--cream);
  height:auto;max-height:min(86vh,760px);border-radius:18px;
  display:flex;flex-direction:column;transform:translateY(10px) scale(.99);opacity:0;
  transition:.2s cubic-bezier(.22,1,.36,1);
  box-shadow:0 30px 70px -24px rgba(26,22,19,.5)}
.panel-wrap.in .panel{transform:none;opacity:1}

.panel-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;
  padding:20px 22px 14px}
.panel-head h2{font-size:19px;font-weight:800;letter-spacing:-.02em;line-height:1.2}
.panel-head p{font-size:13px;color:var(--grey);font-weight:500;margin-top:4px;line-height:1.45}
.panel-x{width:36px;height:36px;border-radius:50%;background:var(--white);display:flex;
  align-items:center;justify-content:center;flex:none;color:var(--mid)}
.panel-x:hover{background:var(--ink);color:#fff}
.panel-x svg{width:17px;height:17px;transform:rotate(45deg)}

.panel-body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:2px 22px 20px;
  display:flex;flex-direction:column;gap:13px}
.panel-body::-webkit-scrollbar{width:8px}
.panel-body::-webkit-scrollbar-thumb{background:var(--line);border-radius:5px}

/* ── the wide panel: two columns ──────────────────────────────────────────
   A document has three optional text boxes and then the thing somebody opened
   it for — the lines, the totals, and the button that sends it. Stacked in a
   460px column, the totals sat below the fold behind two empty textareas and
   the send button was a scroll away. Side by side, the whole document is on
   one screen and nothing has to be hunted for. */
.panel--wide{ width:min(900px,100%) }
/* A document is one column, and a wide one. The lines are the body of it and
   they want room: a description, a quantity, a price and two buttons on a row
   that does not wrap. */
.panel--flow{ width:min(1040px,100%); max-height:min(90vh,900px) }
.panel--flow .panel-body{ display:flex; flex-direction:column; gap:0 }
/* 1:2. The left column holds three short optional fields; the right holds the
   lines, where a description and an amount and two buttons share a row and a
   name like "Cold room service" should not have to wrap to fit. */
/* Two columns only when the PANEL is doing the laying out. When `extra` is a
   function the caller has placed everything itself — a document puts a strip
   across the top and the lines under it — and a grid here would cut that in
   half and drop the second part into the right-hand column. */
.panel--wide:not(.panel--flow) .panel-body{ display:grid;
  grid-template-columns:minmax(0,4fr) minmax(0,8fr); gap:0 26px; align-items:start }
/* width:100% is belt and braces. A grid item should stretch to its track on
   its own, and here it did not — it sat at its shrink-to-fit width inside a
   553px column, so the lines and totals were cramped into a third of the
   panel while half of it stayed empty. */
.panel--wide .pcol{ display:flex; flex-direction:column; gap:13px;
  min-width:0; width:100% }
/* The left column is settings about the document; the right is the document.
   A rule between them says which is which without a heading on either. */
.panel--wide .pcol--main{ border-left:1px solid var(--line); padding-left:26px }

@media (max-width:820px){
  /* Back to one column before the two get too narrow to read. */
  .panel--wide{ width:min(460px,100%) }
  .panel--wide .panel-body{ display:flex; flex-direction:column; gap:13px }
  .panel--wide .pcol--main{ border-left:0; padding-left:0;
    border-top:1px solid var(--line); padding-top:16px }
}

.panel-foot{display:flex;gap:9px;padding:14px 22px;flex:none;
  border-top:1px solid var(--line);background:var(--cream);
  border-radius:0 0 18px 18px}
.panel-foot .big{flex:1;width:auto;margin:0;padding:11px;border-radius:99px;background:var(--orange);
  color:#fff;font-size:14px;font-weight:800;border:0;display:flex;align-items:center;
  justify-content:center;min-height:42px}
.panel-foot .big:hover{background:var(--orange-d)}
.panel-foot .big:disabled{opacity:.55;cursor:default}
.panel-foot .big.ghost{background:var(--white);color:var(--ink);flex:0 0 auto;padding:11px 20px}
.panel-foot .big.ghost:hover{background:var(--line)}
/* a sheet with nothing to save: Close is the only button, and a full-width one */
.panel-foot .big.ghost:only-child{flex:1}

.panel .fld span i{font-style:normal;color:var(--faint);font-weight:500;font-size:12px}
.panel .fld textarea,.panel .fld select{border:1.5px solid var(--line);background:var(--white);
  border-radius:14px;padding:14px 16px;font-size:15px;font-family:inherit;color:var(--ink);
  outline:none;font-weight:500;width:100%;resize:vertical}
.panel .fld input{background:var(--white)}
.panel .fld textarea:focus,.panel .fld select:focus{border-color:var(--orange)}
.panel .fld.bad input,.panel .fld.bad textarea,.panel .fld.bad select{border-color:var(--peach-d)}
.panel .hint{font-style:normal;font-size:12.5px;color:var(--faint);font-weight:500}

.money-in{position:relative;display:flex;align-items:center}
.money-in span{position:absolute;left:16px;font-size:15px;font-weight:700;color:var(--grey);
  pointer-events:none}
.money-in input{padding-left:44px !important}

/* A unit on the right instead of a currency on the left. Same box, mirrored,
   so a per cent field and a dollar field look like the same kind of thing. */
.money-in.suffixed input{padding-left:16px !important;padding-right:40px !important}
.money-in .suf{position:absolute;left:auto;right:16px;font-size:15px;font-weight:700;
  color:var(--grey);pointer-events:none}
/* The browser's own up/down arrows sit exactly where the suffix goes, so "days"
   and a spinner ended up on top of each other. The suffix is the label that
   matters; the arrows are not worth the collision, and the keyboard still
   steps the value. */
.money-in.suffixed input::-webkit-outer-spin-button,
.money-in.suffixed input::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0 }
.money-in.suffixed input{ appearance:textfield; -moz-appearance:textfield }

/* The deposit, on the totals block of a quote. Set apart from the sums above
   it because it is not part of the arithmetic — it is what happens next. */
.lsum .dep{margin-top:8px;padding-top:9px;border-top:1px dashed var(--line);
  color:var(--ink)}
.lsum .dep span{font-weight:600}

@media (max-width:640px){
  /* A sheet, from the edge the thumb is already on. The 24px the desktop
     uses to float the box has to go, or the sheet arrives inset on three
     sides looking like it failed to dock. */
  .panel-wrap{align-items:flex-end;padding:0}
  .panel{width:100%;height:auto;max-height:92vh;border-radius:26px 26px 0 0;
    transform:translateY(24px) scale(1)}
  .panel-head{padding:20px 20px 12px}
  .panel-body{padding:2px 20px 18px}
  .panel-foot{padding:14px 20px calc(18px + env(safe-area-inset-bottom));
    border-radius:0}
}

/* ── the till ───────────────────────────────────────────────────────────── */
/* Products to tap on the left, the basket beside them. On a phone the basket
   drops underneath, which is the same screen with less room rather than a
   different one. */
.tillwrap{display:grid;grid-template-columns:1fr 340px;gap:14px;align-items:start}
.tillgrid{min-height:220px}

.pgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:9px;
  padding:0 14px 16px}
.ptile{text-align:left;padding:15px 16px;border-radius:var(--r2);background:var(--cream-2);
  border:2px solid transparent;transition:.12s}
.ptile:hover{border-color:var(--orange);background:var(--orange-l)}
.ptile:active{transform:scale(.985)}
.ptile b{display:block;font-size:14.5px;font-weight:700;line-height:1.25;letter-spacing:-.012em}
/* A tile with a photo on it. Wide, not square — a bottle cropped to a square
   loses its label, and the label is the thing being looked for. */
.ptile .ppic{display:block;aspect-ratio:16/10;border-radius:9px;overflow:hidden;
  background:var(--line-2);margin:-4px 0 10px}
.ptile .ppic img{width:100%;height:100%;object-fit:cover;display:block}
.ptile.out .ppic{opacity:.45}
.ptile .pr{display:block;font-size:19px;font-weight:800;margin-top:9px;letter-spacing:-.03em}
.ptile .st{display:block;font-size:11.5px;color:var(--faint);font-weight:600;margin-top:3px;
  min-height:14px}
.ptile.low{background:var(--butter)}
.ptile.low .st{color:var(--butter-d)}
.ptile.out{opacity:.45;cursor:not-allowed}
.ptile.out:hover{border-color:transparent;background:var(--cream-2)}

.cart{position:sticky;top:14px;display:flex;flex-direction:column}
.cartlines{padding:0 14px;display:flex;flex-direction:column;gap:6px;max-height:44vh;
  overflow-y:auto}
.cartlines::-webkit-scrollbar{width:7px}
.cartlines::-webkit-scrollbar-thumb{background:var(--line);border-radius:5px}
.cline{display:flex;align-items:center;gap:11px;padding:11px 13px;border-radius:var(--r3);
  background:var(--cream-2)}
.cline .bd{flex:1;min-width:0}
/* the price for this sale, tappable by whoever may set prices (till.js reprice) */
.cline .cprice{display:block;padding:0;background:none;border:0;font-size:12px;color:var(--mid);
  text-align:left;text-decoration:underline dotted;text-underline-offset:3px;cursor:pointer}
@media (pointer:coarse), (max-width:699px){ .cline .cprice{min-height:44px} .cart .cline .qty button{width:44px;height:44px} }
.cline .bd b{display:block;font-size:13.5px;font-weight:700;line-height:1.25}
.cline .bd span{display:block;font-size:11.5px;color:var(--faint);font-weight:600;margin-top:2px}
.cline .qty{display:flex;align-items:center;gap:2px;flex:none;background:var(--white);
  border-radius:99px;padding:2px}
.cline .qty button{width:26px;height:26px;border-radius:50%;font-size:15px;font-weight:800;
  color:var(--mid);display:flex;align-items:center;justify-content:center;line-height:1}
.cline .qty button:hover{background:var(--orange);color:#fff}
.cline .qty span{min-width:20px;text-align:center;font-size:13.5px;font-weight:700}
.cline .amt{font-size:14px;font-weight:700;flex:none;min-width:62px;text-align:right}

.carttotal{display:flex;justify-content:space-between;align-items:baseline;
  padding:16px 20px 14px;margin-top:12px;border-top:1.5px solid var(--line)}
.carttotal span{font-size:14px;font-weight:700;color:var(--grey)}
.carttotal b{font-size:27px;font-weight:800;letter-spacing:-.035em}

.paybtn{margin:0 16px 18px;padding:16px;border-radius:99px;background:var(--orange);color:#fff;
  font-size:15.5px;font-weight:800;display:flex;align-items:center;justify-content:center;gap:9px;
  border:0}
.paybtn:hover{background:var(--orange-d)}
.paybtn:disabled{background:var(--line);color:var(--faint);cursor:default}
.paybtn svg{width:18px;height:18px}

@media (max-width:1000px){
  .tillwrap{grid-template-columns:1fr}
  .cart{position:static}
}

.cartgst{padding:0 20px 12px;margin-top:-8px;font-size:12.5px;color:var(--grey);
  font-weight:600}
.cartgst b{color:var(--ink)}

/* ── the extra pieces the new screens need ──────────────────────────────── */
/* .cardnote was defined twice in this file — this one, and again further down
   with different padding, colour, weight and a max-width. The second silently
   won every time. One definition now, further down, where the report styles
   that use it live. */
.card.stacked{margin-bottom:var(--gap)}
.cardnote b{color:var(--mid)}
.cardnote.standalone{background:var(--white);border-radius:var(--r);padding:18px 22px;
  margin-top:14px}
/* A card's action - Edit, Change these answers, Which of these happen - is
   the thing somebody actually came to the card to press, and it was SIXTEEN
   PIXELS TALL. Text with no padding, in the card's heading row, which is
   fine with a mouse pointer and not a target a thumb can find. Every card in
   Settings had one.

   The padding is negative-margined back out so nothing moves: the button
   grows into the space the heading row already had, rather than pushing the
   title around. */
.ch .all{font-size:12.5px;font-weight:700;color:var(--orange);display:flex;
  align-items:center;gap:4px;background:none;border:0;
  padding:12px 4px;margin:-12px -4px;border-radius:10px;
  /* one line: "Change these answers" broke over two beside the title on a
     phone and read as two buttons */
  white-space:nowrap;flex:none}
.ch .all:hover{background:var(--orange-l)}
.ch .all svg{width:14px;height:14px}

.srow .av{width:34px;height:34px;font-size:12px;flex:none;background:var(--lav);
  color:var(--lav-d)}
.srow .cv{width:16px;height:16px;color:var(--faint);flex:none;display:flex}
.srow .cv svg{width:16px;height:16px}
.srow.warn{background:#FDF1EC}
.srow.warn:before{background:var(--peach-d)}
.srow[data-open],.srow[data-cust],.srow[data-work]{cursor:pointer}

/* history inside a panel: how a number got to be that number */
.hist{margin-top:20px;background:var(--white);border-radius:var(--r2);padding:4px 0 6px}
.hist-lab{font-size:11.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  color:var(--faint);padding:13px 16px 8px}
.hist-row{display:flex;align-items:baseline;gap:12px;padding:8px 16px;font-size:13px}
.hist-row .d{color:var(--faint);font-weight:600;font-size:11.5px;min-width:52px;flex:none}
.hist-row .w{flex:1;min-width:0;color:var(--mid);font-weight:500}
.hist-row .w b{color:var(--ink);font-weight:700}
.hist-row .q{font-weight:700;flex:none}
.hist-row .q.neg{color:var(--peach-d)}
.hist-row .q.pos{color:var(--mint-d)}
.hist-row.total{border-top:1px solid var(--line);margin-top:4px;padding-top:11px}

.minifigs{display:flex;gap:8px;margin-top:18px}
.minifigs > div{flex:1;background:var(--white);border-radius:var(--r2);padding:13px 14px}
.minifigs span{display:block;font-size:11.5px;color:var(--grey);font-weight:600}
.minifigs b{display:block;font-size:18px;font-weight:800;letter-spacing:-.025em;margin-top:5px}

/* ── variants, categories, modifiers ─────────────────────────────────────── */

/* A product that is sold several ways: a header row, then its variants
   indented under it. The indent is the whole explanation. */
.srow.head{background:var(--cream-2);margin-top:6px}
.srow.head .bd b{font-size:14.5px}
.srow.sub{margin-left:20px;padding-left:12px}
.srow.sub:before{left:-20px;background:var(--line)}
.srow.sub .bd b{font-size:13.5px;font-weight:700;color:var(--mid)}

/* Category filter above the till grid. */
.cats{display:flex;gap:7px;flex-wrap:wrap;padding:0 20px 14px}
.cat{padding:7px 15px;border-radius:99px;background:var(--cream-2);font-size:12.5px;
  font-weight:700;color:var(--mid);border:1.5px solid transparent}
.cat:hover{border-color:var(--line)}
.cat.on{background:var(--ink);color:#fff;border-color:var(--ink)}

/* The scanner box. A scanner is a keyboard, so this is just an input. */
.scan{position:relative;display:flex;align-items:center;margin-left:auto}
.scan svg{position:absolute;left:13px;width:15px;height:15px;color:var(--faint);
  pointer-events:none}
.scan input{width:186px;border:1.5px solid var(--line);background:var(--white);
  border-radius:99px;padding:9px 14px 9px 34px;font-size:12.5px;font-family:inherit;
  font-weight:600;color:var(--ink);outline:none}
.scan input::placeholder{color:var(--faint);font-weight:600}
.scan input:focus{border-color:var(--orange);width:220px}

/* Extras chosen on a basket line. */
.cline .bd .mods{font-size:11px;color:var(--orange);font-weight:700;margin-top:3px}

/* What is left after GST and cost. The number Square charges extra for, on
   the screen where the sale is happening. */
.cartmargin{padding:0 20px 14px;margin-top:-6px;font-size:12.5px;color:var(--mint-d);
  font-weight:700}
.cartmargin b{color:var(--mint-d)}

/* Price, cost, and what is actually kept. */
.marginbox{margin-top:20px;background:var(--mint);border-radius:var(--r2);padding:14px 18px}
.marginbox.bad{background:var(--peach)}
.marginbox div{display:flex;justify-content:space-between;align-items:baseline;
  padding:4px 0;font-size:13px;font-weight:600;color:var(--mint-d)}
.marginbox.bad div{color:var(--peach-d)}
.marginbox .tot{border-top:1.5px solid rgba(31,122,76,.22);margin-top:5px;padding-top:9px}
.marginbox.bad .tot{border-top-color:rgba(176,62,20,.22)}
.marginbox .tot b{font-size:19px;font-weight:800;letter-spacing:-.03em}
.marginbox .tot i{font-style:normal;font-size:13px;font-weight:700;opacity:.72}

/* Checkbox rows inside a panel, for picking which extras a product offers. */
.hist-row.pick{cursor:pointer;gap:10px}
.hist-row.pick:hover{background:var(--cream)}
.hist-row.pick input{width:16px;height:16px;accent-color:var(--orange);flex:none}
.hist-row .w{flex:1;min-width:0}
.hist .linkish{font-size:12.5px;margin:6px 16px 4px;display:inline-block}
.linkish.danger{color:var(--peach-d)}

/* Said once, on the screen where it can be acted on, and only while it is
   still true. */
.nudge{margin-bottom:16px;background:var(--butter);display:flex;gap:14px;
  align-items:flex-start;padding:18px 20px}
.nudge-b b{display:block;font-size:14.5px;font-weight:800;color:var(--butter-d);
  letter-spacing:-.012em}
.nudge-b span{display:block;font-size:13px;color:var(--butter-d);font-weight:600;
  margin-top:4px;max-width:70ch;line-height:1.5}

@media (max-width:900px){
  .scan input{width:130px}
  .scan input:focus{width:150px}
  .srow.sub{margin-left:12px}
}

/* ── needs attention ─────────────────────────────────────────────────────── */

/* A row that needs somebody today reads differently from one that can wait.
   The edge does it, not colour alone — the tone chip is already carrying a
   meaning and two signals in one place is one too many. */
.trow.urgent{background:var(--cream-2);position:relative}
.trow.urgent:before{content:'';position:absolute;left:0;top:9px;bottom:9px;width:3px;
  border-radius:3px;background:var(--orange)}
.trow .amt{font-size:13.5px;font-weight:700;flex:none;margin-right:2px}
/* Whose row it is, on a list that has been reordered to put it first. A word
   rather than a colour: a colour would compete with severity, which is the
   one thing on this card allowed to shout. */
.trow .yours,.need h3 .yours{font-style:normal;font-size:11px;font-weight:600;
  margin-left:8px;padding:1px 7px;border-radius:999px;background:var(--cream-2);
  color:var(--text-2);vertical-align:2px;white-space:nowrap}
.trow .bd span{line-height:1.35}

.morerow{padding:10px 16px 4px;font-size:12.5px;font-weight:600;color:var(--faint)}

/* Nothing wrong is a real answer and gets a real panel. */
.allclear{padding:26px 20px;text-align:center}
.allclear svg{width:26px;height:26px;color:var(--mint-d);stroke-width:2.6;margin-bottom:8px}
.allclear b{display:block;font-size:15px;font-weight:800;letter-spacing:-.015em}
.allclear span{display:block;font-size:13px;color:var(--grey);font-weight:600;margin-top:4px;
  max-width:34ch;margin-left:auto;margin-right:auto;line-height:1.45}

/* ── what Noti handled ───────────────────────────────────────────────────── */
/* A skip is not a failure and must not read as one. Acted is green, skipped is
   a neutral blue, asked is amber, failed is the only one that looks like a
   problem — because it is the only one that is. */
.drow .tk.t-mint{color:var(--mint-d)}
.drow .tk.t-sky{color:var(--sky-d)}
.drow .tk.t-butter{color:var(--butter-d)}
.drow .tk.t-peach{color:var(--peach-d)}
.drow.skipped .bd b{color:var(--mid);font-weight:600}
.drow.failed{background:#FDF3EF;border-radius:var(--r3)}

/* A line of explanation under a settings card, where the reason for a setting
   matters as much as the value. */
.note{padding:2px 22px 18px;font-size:12.5px;color:var(--grey);font-weight:600;
  line-height:1.5;max-width:62ch}
.sched em{font-style:normal;color:var(--peach-d);font-weight:700}

/* Report range picker — the same chips as the till's categories. */
.ranges{display:flex;gap:7px;flex-wrap:wrap}
.cardnote{padding:2px 22px 18px;font-size:12.5px;color:var(--grey);font-weight:600;
  line-height:1.5;max-width:64ch}

/* Arriving from an invite. Says what this is before anybody types a password. */
.invite-chip{display:inline-block;background:var(--lav);color:var(--lav-d);
  font-size:11.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  padding:6px 12px;border-radius:99px;margin-bottom:14px}

/* ── pairing a device ────────────────────────────────────────────────────── */
/* Read across a room, or through somebody's phone camera. Big on purpose. */
.paircode{text-align:center;padding:8px 0 4px}
.paircode .code{font-size:40px;font-weight:600;letter-spacing:.16em;
  background:var(--cream-2);border-radius:var(--r2);padding:18px 10px}
.paircode .dies{font-size:12.5px;font-weight:600;color:var(--grey);margin-top:10px}
.paircode .qr{margin:18px auto 6px;width:180px;height:180px;background:#fff;
  border-radius:var(--r2);padding:10px;box-sizing:content-box}
.paircode .qr svg{width:100%;height:100%;display:block}
/* The bare svg rule at the top of this file is for the icon set — hairline,
   round, no fill. A QR is the opposite: a grid of exact one-unit squares. Left
   alone it gets drawn with a 1.9-unit rounded stroke on a 31-unit grid, which
   smears the modules into each other and no scanner can read it.
   Every QR in the app needs these three lines: the payment handover's went
   without them and rendered as one black square. */
.paircode .qr svg path,.ho-qr svg path{stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter}
.paircode .qr svg path[fill],.ho-qr svg path[fill]{fill:#fff;stroke:none}
.paircode .qr svg path[stroke],.ho-qr svg path[stroke]{stroke:#000}
.paircode .orlink{font-size:12px;font-weight:600;color:var(--faint);line-height:1.6}
.paircode .small{font-size:11px;word-break:break-all;color:var(--grey)}

/* ── the first screen, and the lock screen ────────────────────────────────
   Two doors, a code box and a PIN pad. All of it is touched with a thumb on a
   counter more often than clicked with a mouse, so the targets are large and
   the type is big enough to read at arm's length. */

.doors{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:26px}
.door{display:flex;flex-direction:column;align-items:flex-start;gap:7px;
  text-align:left;background:var(--cream);border:1.5px solid transparent;
  border-radius:var(--r2);padding:22px 20px 18px;cursor:pointer;
  transition:border-color .12s,background .12s}
.door:hover{background:var(--white);border-color:var(--ink)}
.door .di{display:grid;place-items:center;width:38px;height:38px;border-radius:12px;
  background:var(--white);color:var(--ink);margin-bottom:4px}
.door:hover .di{background:var(--orange);color:#fff}
.door .di svg{width:19px;height:19px}
.door b{font-size:16.5px;font-weight:800;letter-spacing:-.02em}
.door>span{font-size:13.5px;font-weight:500;color:var(--grey);line-height:1.5}
.door em{font-style:normal;font-size:13.5px;font-weight:800;color:var(--orange-d);
  margin-top:6px}

/* ── the first door ───────────────────────────────────────────────────────
   Three doors, not three buttons. Two of them are ways BACK in for somebody
   who already knows the way; only one of them is for a person who could be
   stuck, so only one of them gets the width, the colour and the lit chip.
   The weight is the answer to "which of these is me?" — which is the whole
   job of this screen, and it should be answerable without reading. */
.doors--lead{grid-template-columns:1fr;margin-bottom:14px}
.door--first{background:var(--orange-l);padding:24px 22px 20px}
.door--first .di{background:var(--orange);color:#fff}
.door--first b{font-size:19px}
.door--first>span{color:var(--ink);opacity:.72}
.door--first:hover{background:var(--orange-l);border-color:var(--orange)}

/* The gap between the primary and the pair is doing the separating, so the
   pair does not need a heading to say "or, if you have been here before". */
.doors + .doors{margin-top:0}

.codein{text-align:center;font-size:30px;font-weight:700;letter-spacing:.28em;
  text-transform:uppercase;padding:16px 10px 16px 20px}

.lockgate .gate-card{padding-top:26px}
.lockhead{display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding-bottom:20px}
.lockhead .gate-logo{padding:0;justify-content:flex-start}
.lockwhere{text-align:right;line-height:1.35}
.lockwhere b{display:block;font-size:13.5px;font-weight:800}
.lockwhere span{font-size:12.5px;font-weight:600;color:var(--faint)}
.backish{display:inline-flex;align-items:center;gap:5px;font-size:13.5px;
  font-weight:700;color:var(--grey);background:none;cursor:pointer}
.backish:hover{color:var(--ink)}
.backish svg{width:15px;height:15px}

/* Scoped to the lock screen on purpose. noti.css already owns bare .person for
   the calendar's staff columns, and an unscoped rule here silently repainted
   every column on the calendar cream and stacked it — which is exactly the
   class of bug .col and .rule caused on the same screen. A name that already
   exists in the design system gets a parent, not a redefinition. */
.people{display:grid;grid-template-columns:repeat(auto-fill,minmax(132px,1fr));
  gap:12px;margin-top:22px}
.lockgate .person{display:flex;flex-direction:column;align-items:center;gap:8px;
  background:var(--cream);border:1.5px solid transparent;border-radius:var(--r2);
  padding:20px 12px;cursor:pointer;transition:border-color .12s,background .12s;
  min-width:0;text-align:center}
.lockgate .person:hover{background:var(--white);border-color:var(--ink)}
.pav{display:grid;place-items:center;width:52px;height:52px;border-radius:50%;
  background:var(--orange);color:#fff;font-size:21px;font-weight:800}
.lockgate .person b{font-size:15px;font-weight:800;letter-spacing:-.02em}
.lockgate .person .prole{font-size:12px;font-weight:600;color:var(--faint);
  text-transform:capitalize}
.lockgate .person[data-nopin] .pav{background:var(--line);color:var(--faint)}

.dots{display:flex;justify-content:center;gap:11px;margin:26px 0 22px}
.dots i{width:12px;height:12px;border-radius:50%;background:var(--line)}
/* The first four are outlined, so eight circles do not read as "type eight". */
.dots i.need{box-shadow:inset 0 0 0 1.5px var(--faint);background:transparent}
.dots i.on{background:var(--ink);box-shadow:none}

.pad{display:grid;grid-template-columns:repeat(3,1fr);gap:11px;max-width:290px;
  margin:0 auto}
.key{height:62px;border-radius:16px;background:var(--cream);font-size:24px;
  font-weight:700;cursor:pointer;transition:background .1s;
  -webkit-tap-highlight-color:transparent}
.key:hover{background:var(--line)}
.key:active{background:var(--ink);color:var(--white)}
.key.ghosty{background:transparent;color:var(--grey)}
.key.ghosty:hover{background:var(--cream);color:var(--ink)}
.key.go{background:var(--orange);color:#fff}
.key.go:hover{background:var(--orange-d)}
.key.go.working{opacity:.6}
.key svg{width:21px;height:21px;margin:0 auto}

@media (max-width:560px){
  .doors{grid-template-columns:1fr}
  .door{padding:18px 16px 15px}
}

/* ── categories ───────────────────────────────────────────────────────────
   A category is a coloured button on the till and a heading in the catalogue.
   Both live here so they cannot drift apart. */

.chipn{display:inline-grid;place-items:center;min-width:17px;height:17px;padding:0 5px;
  border-radius:99px;background:var(--line);color:var(--mid);font-size:11px;
  font-weight:800;margin-left:3px}
.chip.dark .chipn{background:rgba(255,255,255,.22);color:#fff}

.cats .cat span{display:inline-block;margin-left:5px;color:var(--faint);font-weight:800}
.cat.on span{color:rgba(255,255,255,.62)}
.cat.c-lav{background:var(--lav);color:var(--lav-d)}
.cat.c-mint{background:var(--mint);color:var(--mint-d)}
.cat.c-sky{background:var(--sky);color:var(--sky-d)}
.cat.c-butter{background:var(--butter);color:var(--butter-d)}
.cat.c-peach{background:var(--peach);color:var(--peach-d)}
.cat.c-lav span,.cat.c-mint span,.cat.c-sky span,
.cat.c-butter span,.cat.c-peach span{color:inherit;opacity:.62}
.cat.on.c-lav,.cat.on.c-mint,.cat.on.c-sky,
.cat.on.c-butter,.cat.on.c-peach{background:var(--ink);color:#fff}

/* A heading inside a card, between groups. Quieter than the card's own title,
   because it is a divider rather than a new subject. */
.grouphead{display:flex;align-items:center;gap:7px;padding:14px 22px 6px;
  font-size:12px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  color:var(--faint)}
.grouphead span{font-size:11px;color:var(--mid);background:var(--cream);
  border-radius:99px;padding:1px 7px;letter-spacing:0}

.catlist{display:flex;flex-direction:column;gap:7px}
.catitem{display:flex;align-items:center;gap:11px;background:var(--cream-2);
  border-radius:var(--r2);padding:11px 13px;cursor:grab}
.catitem.lifting{opacity:.4;cursor:grabbing}
/* Six dots, drawn rather than fetched. A chevron here read as "open me". */
.catitem .grip{flex:none;width:9px;height:14px;
  background-image:radial-gradient(var(--faint) 42%, transparent 45%);
  background-size:4.5px 4.5px;opacity:.75}
.catitem .bd{flex:1;min-width:0;line-height:1.3}
.catitem .bd b{display:block;font-size:14.5px;font-weight:700}
.catitem .bd span{font-size:12px;font-weight:600;color:var(--faint)}
.swatch{width:15px;height:15px;border-radius:5px;flex:none}
.swatch.s-lav{background:var(--lav-d)}
.swatch.s-mint{background:var(--mint-d)}
.swatch.s-sky{background:var(--sky-d)}
.swatch.s-butter{background:var(--butter-d)}
.swatch.s-peach{background:var(--peach-d)}

.mini{padding:5px 9px;border-radius:8px;background:var(--white);font-size:12px;
  font-weight:700;color:var(--mid);cursor:pointer;flex:none}
.mini:hover{background:var(--line);color:var(--ink)}
/* A thumb, not a cursor: Edit, x and Add on a quote's lines were 25px tall
   on a phone (trade run-through, 1 Oct). */
@media (pointer:coarse), (max-width:699px){
  .mini{min-height:44px;min-width:44px;padding:10px 12px;font-size:13px}
}

/* Removing is not saving, so it does not look like the save button. */
.dangerish{width:100%;margin-top:18px;padding:12px;border-radius:99px;
  background:var(--peach);color:var(--peach-d);font-size:14px;font-weight:700;
  cursor:pointer}
.dangerish:hover{background:var(--peach-d);color:#fff}
.fineprint{font-size:12.5px;font-weight:500;color:var(--grey);line-height:1.55;
  margin-top:10px}

/* ── settings ─────────────────────────────────────────────────────────────
   A settings screen grows forever, and eight stacked cards means scrolling to
   find the one thing you came for. A rail down the side turns that into one
   click, and gives anything that is not set somewhere to shout from. */

.setwrap{display:grid;grid-template-columns:214px 1fr;gap:18px;align-items:start}
.setnav{display:flex;flex-direction:column;gap:2px;position:sticky;top:18px}
.si{display:flex;align-items:center;gap:10px;width:100%;padding:10px 13px;
  border-radius:var(--r2);background:none;font-size:14px;font-weight:600;
  color:var(--mid);cursor:pointer;text-align:left}
.si svg{width:16px;height:16px;flex:none;color:var(--faint)}
.si span{flex:1;min-width:0}
.si:hover{background:var(--cream-2);color:var(--ink)}
.si.on{background:var(--white);color:var(--ink);font-weight:700}
.si.on svg{color:var(--orange)}
/* Not a count and not a badge — just "there is something here". */
.needs{width:7px;height:7px;border-radius:50%;background:var(--attn);flex:none}

/* A MEASURE. Settings is a reading-and-deciding surface, not a table, and
   at 1680px every row stretched its label and its value about fourteen
   hundred pixels apart: "Name . . . . . . . . . . . Aquadec Plumbing". Nine
   sections of that is what "accumulated controls" looks like.

   1040px, not the 1600 the Subject page uses — that one carries a timeline
   and a history beside each other, and this carries one column of decisions.
   Left-aligned rather than centred, so the section heading above it and the
   cards below it start on the same line. */
.setbody{display:flex;flex-direction:column;gap:16px;min-width:0;
  width:100%;max-width:1040px}
/* From a desktop up the cards sit two across (LAYOUT.md): one 1,040px column
   left half of a 1440 screen empty and put a row's control a long way from
   its name. */
@media (min-width:1440px){
  .setbody{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start;max-width:none}
}
.setbody .card{margin:0}

/* A row you can click opens whatever changes it. */
.srow.clicky{cursor:pointer}
.srow.clicky:hover{background:var(--cream)}
.rowchev{color:var(--faint);display:flex;flex:none}
.rowchev svg{width:15px;height:15px}
.srow.clicky:hover .rowchev{color:var(--ink)}
.unset{font-style:italic;color:var(--attn);font-weight:700}

@media (max-width:900px){
  .setwrap{grid-template-columns:1fr}
  .setnav{position:static;flex-direction:row;overflow-x:auto;gap:6px;
    padding-bottom:4px;-webkit-overflow-scrolling:touch}
  .si{width:auto;flex:none;padding:9px 14px;border-radius:99px;
    background:var(--cream-2);white-space:nowrap}
  .si span{flex:none}
  .si svg{display:none}
}

/* ── the bar chart ────────────────────────────────────────────────────────
   Divs, not a library. The only things read off a chart this size are which
   bar is tallest and whether the right-hand end is going up. */

.bars{display:flex;align-items:flex-end;gap:4px;height:152px;padding:8px 22px 14px}
.bar{flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;
  gap:7px;height:100%}
/* No track behind each bar. Thirty pale rectangles for thirty quiet days read
   as stripes, and the one day something happened disappears into them. A
   baseline and the bars themselves are the whole chart. */
/* .stack, not .col — noti.css already owns .col for the day columns in the
   schedule, and inheriting its 150px minimum and left border turned this chart
   into a faint grid with eleven bars in it. */
.bar .stack{flex:1;width:100%;display:flex;align-items:flex-end;
  border-bottom:1.5px solid var(--line)}
.bar i{display:block;width:100%;min-height:1.5px;background:var(--orange-l);
  border-radius:4px 4px 0 0;transition:height .25s,background .12s}
.bar:hover i{background:var(--orange)}
/* An empty day is a flat mark on the baseline, not a gap: the run of quiet
   days is the thing worth seeing. */
.bar i[style*="height:0%"]{background:var(--line);min-height:2px}
.bar span{font-size:10.5px;font-weight:700;color:var(--faint);white-space:nowrap;
  overflow:hidden;height:12px}
@media (max-width:760px){ .bars{height:120px;padding-inline:14px} }

/* ── the calendar ─────────────────────────────────────────────────────────
   noti.css already owns the calendar vocabulary — .calwrap, .calhead,
   .person, .gutter, .col, .block and the b-* palette, on a 48px hour. This
   file only adds the three things that vocabulary does not have, because Noti
   keeps time in four ways and a design system built around appointments only
   models one of them:

     the week view      seven columns of chips, for a trade whose answer is
                        "Tuesday" rather than "Tuesday at 2:15"
     the all-day band   work that is a day, not a time, sitting above the grid
     off-hours shading  outside trading hours, shaded but never blocked
   ───────────────────────────────────────────────────────────────────────── */

.daynav{display:flex;align-items:center;gap:6px}
.daynav .rnd{width:32px;height:32px}
.daynav .rnd svg{width:15px;height:15px}
.daynav .chip{padding:7px 14px}

/* A column wider than about 280px is wasted: a calendar is read down, not
   across, and a two-hour booking stretched over 800 pixels reads as a banner
   with a word in the corner rather than as an appointment. --cols is set by
   the pane, so three staff fill more of the card than two and twelve staff
   scroll sideways the way they should. */
/* The cap goes on the card, not on the grid inside it. Capping the grid alone
   left a field of empty white to the right of the last column, which reads as
   something that failed to load. */
/* The calendar runs the full width of the page.

   It used to be capped at `62px + cols x 280px` on the reasoning that a
   column wider than about 280px is wasted, which is true of a column in a
   grid of eight staff and false of the screen as a whole: at --cols:1 the cap
   produced a 344px calendar with 1300px of empty cream beside it, and the
   day's other cards pushed a thousand pixels below the fold. They are a
   header strip now, and the calendar gets the page. */
.card.cal, .sys-card.cal{ max-width:100% }

.calbody, .calhead{min-width:calc(62px + var(--cols,6) * 150px)}

/* Shaded, not blocked. Plenty of work happens outside the hours a shop says it
   keeps, and a calendar's job is to show it rather than argue. */
.col .offhours{position:absolute;left:0;right:0;background:var(--cream);
  opacity:.7;pointer-events:none}
.col .nowline{position:absolute;left:0;right:0;height:2px;background:var(--orange);
  z-index:3;pointer-events:none}
/* One dot, on the first column. Every column drawing its own turned the
   time-now line into a dotted ruler across the day. */
.col:not(:first-of-type) .nowline::before{display:none}
.col .nowline::before{content:"";position:absolute;left:-3px;top:-3px;width:8px;
  height:8px;border-radius:50%;background:var(--orange)}

/* Cancelled is not a colour in the palette, because it is an absence. */
.block.b-gone{background:var(--cream-2);color:var(--faint)}
.block.b-gone b{text-decoration:line-through}
/* A window is not an appointment and should not be drawn as one. */
.block.b-window{background:repeating-linear-gradient(135deg,var(--sky) 0 11px,
  #E9F1FA 11px 22px);color:var(--sky-d)}
.block.tight{padding:4px 10px}
.block.tight span,.block.tight .t{display:none}
.block.tight b{margin-top:0}

/* The week. Seven columns of chips, not seven grids — at this width a week
   grid is unreadable, and it was never the right shape for a trade. */
.week{display:grid;grid-template-columns:repeat(7,1fr);padding:0;overflow:hidden}
.wday{border-left:1px solid var(--line-2);min-height:300px;display:flex;
  flex-direction:column;cursor:pointer}
.wday:first-child{border-left:0}
.wday:hover{background:var(--cream-2)}
.wday.now .wday-h{background:var(--ink);color:#fff}
.wday.now .wday-h i{color:rgba(255,255,255,.7)}
.wday.shut{background:#FCFAF7}
.wday-h{display:flex;align-items:baseline;gap:6px;padding:11px 12px;
  border-bottom:1px solid var(--line-2)}
.wday-h b{font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.05em}
.wday-h span{font-size:15px;font-weight:800}
.wday-h i{margin-left:auto;font-style:normal;font-size:10.5px;font-weight:700;
  color:var(--faint)}
.wday-b{flex:1;padding:8px;display:flex;flex-direction:column;gap:6px}
.wnone{font-size:11px;font-weight:700;color:var(--faint);padding:4px}

/* A chip is a block that is not on a grid. Same palette, so a job is the same
   colour whichever view you are looking at it in. */
.wchip{border-radius:9px;padding:7px 9px;cursor:pointer;line-height:1.32;
  background:var(--cream);color:var(--mid)}
.wchip:hover{filter:brightness(.97)}
.wchip .wt{display:block;font-style:normal;font-size:10.5px;font-weight:700;opacity:.75}
.wchip b{display:block;font-size:12.5px;font-weight:800;letter-spacing:-.01em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wchip span{display:block;font-size:11px;font-weight:600;opacity:.78;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wchip.b-lav{background:var(--lav);color:var(--lav-d)}
.wchip.b-sky,.wchip.b-window{background:var(--sky);color:var(--sky-d)}
.wchip.b-mint{background:var(--mint);color:var(--mint-d)}
.wchip.b-butter{background:var(--butter);color:var(--butter-d)}
.wchip.b-peach{background:var(--peach);color:var(--peach-d)}
.wchip.b-gone{background:var(--cream-2);color:var(--faint)}
.wchip.b-gone b{text-decoration:line-through}

/* A landscaper's Tuesday is not nine to five, it is Tuesday. */
.allday{display:flex;align-items:flex-start;gap:14px;padding:12px 16px;
  margin-bottom:12px;max-width:calc(62px + var(--cols,6) * 280px + 2px)}
.allday-l{flex:none;width:62px;font-size:11px;font-weight:800;color:var(--faint);
  text-transform:uppercase;letter-spacing:.05em;padding-top:6px}
.allday-r{flex:1;display:flex;flex-wrap:wrap;gap:7px}
.allday-r .wchip{min-width:150px}

@media (max-width:900px){
  .week{grid-template-columns:1fr}
  .wday{min-height:0;border-left:0;border-top:1px solid var(--line-2)}
}

/* A circle with a face in it. .av already sets the size and the shape wherever
   it is used, so this only has to stop the image escaping it. */
.av.has-img{padding:0;overflow:hidden;background:var(--line)}
.av.has-img img{width:100%;height:100%;object-fit:cover;display:block}

/* The picker: the photo as it will look, and one button. Somebody setting a
   staff photo wants to see the crop, not a filename. */
.avpick{display:flex;align-items:center;gap:16px;padding:4px 0 2px}
.avpick .av{width:74px;height:74px;font-size:23px;flex:none}
.avpick-b{display:flex;flex-direction:column;gap:7px;align-items:flex-start}
.avpick-b .btn{padding:8px 14px}
.avpick-n{font-size:12px;font-weight:600;color:var(--faint);line-height:1.5}

/* A product photo is a thing on a shelf, so it is not a circle. */
.propic{width:100%;aspect-ratio:16/10;border-radius:var(--r2);overflow:hidden;
  background:var(--cream);display:grid;place-items:center;margin-bottom:4px}
.propic img{width:100%;height:100%;object-fit:cover;display:block}
.propic .none{font-size:12.5px;font-weight:700;color:var(--faint)}

/* The countdown on a live offer. Deliberately the only ticking thing in the
   product: it is the one number that is wrong the instant it is drawn, and it
   is what makes somebody believe the slot is really being held. */
.countdown{font-variant-numeric:tabular-nums}
/* .qn, not .tm — noti.css already uses .srow .tm for a 64px time column, and
   redefining it here would have silently narrowed every time in the app. */
.srow .qn{display:grid;place-items:center;width:22px;height:22px;border-radius:7px;
  background:var(--cream);color:var(--faint);font-size:11px;font-weight:800;flex:none}
.btn.wide{width:100%;justify-content:center;padding:12px;margin-bottom:4px}

/* ── the search box, which until now was a picture of a search box ────────
   noti.css positions `.search svg` absolutely for the magnifying glass. The
   results live inside .search too, so their icons have to be pulled back out
   of that rule — the same collision that has bitten this session four times,
   caught this time before it shipped. */
.results svg{position:static;transform:none;width:15px;height:15px;color:inherit}

.search kbd{position:absolute;right:14px;top:50%;transform:translateY(-50%);
  font:inherit;font-size:11px;font-weight:700;color:var(--faint);
  background:var(--cream);border-radius:6px;padding:2px 7px;pointer-events:none}
/* Gone once the box is in use — on focus, and also whenever there is text in
   it, because a hint you have already taken is just clutter over your typing. */
.search input:focus ~ kbd,
.search input:not(:placeholder-shown) ~ kbd{display:none}

.results{position:absolute;top:calc(100% + 8px);left:0;right:0;z-index:40;
  background:var(--white);border-radius:var(--r2);padding:6px;
  box-shadow:0 12px 34px rgba(26,22,19,.14),0 0 0 1px var(--line-2);
  max-height:min(62vh,420px);overflow-y:auto}
.rrow{display:flex;align-items:center;gap:11px;width:100%;padding:9px 10px;
  border-radius:10px;background:none;cursor:pointer;text-align:left}
.rrow:hover,.rrow.on{background:var(--cream)}
.rk{display:grid;place-items:center;width:28px;height:28px;border-radius:9px;flex:none}
.rk-customer{background:var(--lav);color:var(--lav-d)}
.rk-work{background:var(--sky);color:var(--sky-d)}
.rk-product{background:var(--mint);color:var(--mint-d)}
.rb{flex:1;min-width:0;line-height:1.3}
.rb b{display:block;font-size:14px;font-weight:700;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.rb span{display:block;font-size:12px;font-weight:600;color:var(--faint);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rt{font-size:11px;font-weight:700;color:var(--faint);flex:none}
.rnone{padding:16px 12px;font-size:13px;font-weight:600;color:var(--faint);
  text-align:center}

/* ── a document, inside the panel ─────────────────────────────────────────
   All scoped, because .lb and .tot already mean something elsewhere and this
   session has spent enough time on that lesson. */

.lines{display:flex;flex-direction:column;gap:2px;margin-bottom:14px}
.lrow{display:flex;align-items:center;gap:10px;padding:10px 12px;
  background:var(--cream-2);border-radius:var(--r3,10px)}
.lines .lb{flex:1;min-width:0;line-height:1.3}
.lines .lb b{display:block;font-size:13.5px;font-weight:700}
.lines .lb span{font-size:11.5px;font-weight:600;color:var(--faint)}
.lrow .amt{font-size:13.5px;font-weight:700}

/* ── the strip across the top of a document ───────────────────────────────
   What it is and what it is against: our number, their order number, the
   terms and the date it falls due. Four small things on one line, so the
   lines below start near the top of the panel instead of under two textareas.
   The fields here are ordinary panel fields placed by the caller — see the
   `extra` function in panel.js. */
/* A wrapping row rather than a grid with a counted number of columns. The
   strip holds a different set of fields on a quote and on an invoice, and
   payment terms is two boxes rather than one because "30 days" and "30 days
   EOM" are different arrangements — so the count is not a constant and the
   CSS should not contain one.
   (It was briefly `repeat(auto-fit, …)` beside an `auto` track, which is not
   allowed next to an intrinsically sized one: the whole declaration is
   dropped and every field stacks in a single column.) */
.dochead{ display:flex; flex-wrap:wrap; align-items:flex-end;
  gap:10px 18px;
  padding-bottom:16px; margin-bottom:14px; border-bottom:1px solid var(--line) }
.dochead .dh{ display:flex; flex-direction:column; gap:2px; padding-bottom:9px }
.dochead .dh span{ font-size:11px; font-weight:800; letter-spacing:.12em;
  text-transform:uppercase; color:var(--faint) }
.dochead .dh b{ font-size:17px; font-weight:800; letter-spacing:-.02em }
/* The number is theirs to set while it is a draft — a business carrying on
   from another system needs to say 4313, not start again at 1001. The prefix
   is a settings decision so it sits beside the box, not in it, and the two
   read as one reference. */
.dochead .dh-ref{ display:flex; align-items:baseline; gap:1px }
.dochead .dh-pre{ font-size:17px; font-weight:800; letter-spacing:-.02em;
  color:var(--ink) }
.dochead .dh-num{ font:inherit; font-family:var(--m); font-size:17px;
  font-weight:800; letter-spacing:-.02em; color:var(--ink);
  width:5.2ch; min-width:0; padding:2px 4px;
  border:1.5px solid transparent; border-radius:7px; background:transparent;
  transition:border-color .12s, background .12s }
.dochead .dh-num:hover{ background:var(--cream-2) }
.dochead .dh-num:focus{ outline:none; background:var(--white);
  border-color:var(--orange); box-shadow:0 0 0 3px var(--orange-l) }
/* The spinners would sit on top of a four-digit number in a five-character
   box, and nobody nudges an invoice number up by one with a mouse. */
.dochead .dh-num::-webkit-outer-spin-button,
.dochead .dh-num::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0 }
.dochead .dh-num{ -moz-appearance:textfield; appearance:textfield }
/* The fields in the strip lose their bottom margin so the row lines up. */
.dochead .fld{ margin:0; flex:0 1 168px; min-width:118px; max-width:210px }
/* The due date and the note under it travel together, so the note sits with
   its own field rather than wherever the row happens to wrap to. */
.dochead .dh-due{ flex:0 1 168px; min-width:118px; max-width:210px }
.dochead .dh-due .fld{ flex:none; max-width:none; width:100% }
/* Quiet. A one-off due date is a normal thing to want, not a mistake, so this
   is the same grey as a field's own hint and carries no colour at all. */
.dh-hand{ display:block; margin-top:5px; font-size:11.5px; font-weight:600;
  color:var(--faint) }
.dh-hand[hidden]{ display:none }
.dochead .fld .hint{ display:none }
@media (max-width:820px){
  .dochead .dh{ flex:1 0 100%; padding-bottom:0 }
  .dochead .fld{ flex:1 1 calc(50% - 9px); max-width:none }
}

/* ── the wording, folded away ─────────────────────────────────────────────
   Notes and terms are written once and read never. They are not worth the two
   textareas of vertical space they were taking above the thing somebody opened
   the document to see. */
.docmore{ margin-top:16px; border-top:1px solid var(--line); padding-top:12px }
.docmore > summary{ cursor:pointer; list-style:none; font-size:13.5px;
  font-weight:700; color:var(--mid); display:flex; align-items:center; gap:8px;
  padding:4px 0 }
.docmore > summary::-webkit-details-marker{ display:none }
.docmore > summary::before{ content:"›"; font-size:17px; color:var(--faint);
  transition:transform .15s; display:inline-block }
.docmore[open] > summary::before{ transform:rotate(90deg) }
.docmore > summary:hover{ color:var(--ink) }
.docmore .fld{ margin-top:12px }

/* ── the lines on a document ──────────────────────────────────────────────
   A table, because §8 says so and because it is right: one set of columns,
   figures right-aligned on the same edge the totals use, hairlines instead of
   a background per row. The previous version was a stack of rounded cards
   with a dashed box under them, which is the "fifty rounded rectangles"
   §8 opens by warning about.

   One template, shared by the header, every row, the add row and the totals,
   so every number in the column lines up with every other one. */
/* Six columns, not five. The add row was spending its fifth slot on the tax
   select while every row above spent theirs on the amount, so the select sat
   under AMOUNT and nothing in the add row lined up with anything above it.
   Tax gets its own column and the add row shows a live amount, so both rows
   are the same shape. */
/* --dl-pad is the one reason a figure sits where it sits. An <input> carries
   its own padding and a <div> carries none, so a read row, an edit row and
   the totals each landed on a different right edge — 1044, 1036 and 1048 —
   and the column looked crooked on a screen built to be read down. Every
   numeric cell now takes the same value, whatever element it is. */
/* AMOUNT is the last thing on the row, as it is on every invoice anybody has
   ever been handed. It used to sit fourth of six, with TAX and the row's
   buttons to its right, which left the totals underneath it stranded in the
   middle of the panel with a column and a half of nothing beside them. Tax
   belongs before the figure it applies to. */
.lines{ --dl: minmax(0,1fr) 58px 96px 86px 108px 84px;
  --dl-gap:14px;
  /* An <input> holds its text inside a border and a padding; a <div> holds it
     against the cell edge. That difference is why the same figure sat in two
     places depending on whether the row was being read or edited — a whisker
     on the numbers, nine and a half pixels on the description, and a column
     that looked crooked on a screen whose whole job is being read down.
     --dl-inset is the one inset every cell honours, and the read cells pad
     themselves by the input's border AND padding so that clicking Edit moves
     nothing at all. */
  --dl-bd:1.5px; --dl-pad:8px; --dl-inset:calc(var(--dl-pad) + var(--dl-bd));
  margin-top:4px }
/* Nothing can be done to a line on a document that has been sent, so the
   column that would hold the buttons is not there at all — which is what puts
   the amounts, and the totals under them, hard against the right edge. */
.lines--read{ --dl: minmax(0,1fr) 58px 96px 86px 108px }
.lines--read .dl-act{ display:none }

.dl-head, .dl-row{ display:grid; grid-template-columns:var(--dl);
  gap:var(--dl-gap); align-items:center }
.dl-head{ padding:0 4px 9px; font-size:10.5px; font-weight:800;
  letter-spacing:.11em; text-transform:uppercase; color:var(--text-2);
  border-bottom:1.5px solid var(--line) }
.dl-num{ text-align:right }
/* Every cell that is read rather than typed into, padded to the input's inner box. */
.dl-row > :not(input):not(select){ padding:0 var(--dl-inset) }
.dl-head > *{ padding:0 var(--dl-inset) }

.dl-row{ padding:11px 4px; border-bottom:1px solid var(--line-2) }
.dl-row:hover{ background:var(--cream-2) }
/* Readability: this is a document somebody checks a figure on, not a caption.
   The description carries the weight, every figure is ink rather than grey,
   and nothing in the table is set below 13.5px. */
.dl-desc{ font-size:15px; font-weight:600; letter-spacing:-.01em; color:var(--ink);
  min-width:0; overflow-wrap:anywhere }
.dl-row .mono{ font-size:14px; color:var(--ink) }
.dl-row .strong{ font-weight:700 }
.dl-tax{ font-size:11.5px; font-weight:700; color:var(--text-2);
  text-align:right; white-space:nowrap }
.dl-empty{ padding:18px 4px; font-size:14px; color:var(--faint) }

/* The actions are there when the row is, not on hover — a control that
   appears only when the mouse finds it is a control a keyboard never does. */
.dl-act{ display:flex; gap:6px; justify-content:flex-end }

/* ── the add row is the last row of the table ─────────────────────────────
   Not a dashed box below it. The inputs are transparent until they are being
   used, so the row reads as part of the table and not as a form stapled on. */
/* The row that adds one and the row being changed are the same six columns
   with the same fields in them, so they are the same rules. Changing a line
   in place rather than in a modal is only worth anything if the row does not
   jump when it happens. */
.dl-new, .dl-edit{ border-bottom:1px solid var(--line) }
.dl-new:hover, .dl-edit:hover{ background:transparent }
.dl-new input, .dl-new select,
.dl-edit input, .dl-edit select{ font:inherit; font-size:14px;
  border:1.5px solid transparent;
  background:transparent; border-radius:8px; padding:7px var(--dl-pad); min-width:0;
  width:100%; transition:border-color .12s, background .12s }
.dl-new input::placeholder{ color:var(--faint) }
.dl-new input.dl-num, .dl-edit input.dl-num{ font-family:var(--m); font-size:14px;
  text-align:right }
/* The rule that used to live here gave the non-input cells in these two rows
   8px to match the inputs. .dl-num does that for every row now, so matching
   one row to another is no longer a thing anybody has to remember. */
.dl-new #nl-amt{ color:var(--faint) }
.dl-new input:hover, .dl-new select:hover,
.dl-edit input:hover, .dl-edit select:hover{ background:var(--cream-2) }
.dl-new input:focus, .dl-new select:focus,
.dl-edit input:focus, .dl-edit select:focus{ outline:none; background:var(--white);
  border-color:var(--orange); box-shadow:0 0 0 3px var(--orange-l) }
.dl-new select, .dl-edit select{ font-size:12.5px; color:var(--text-2) }

/* Open for changing. A tint rather than an outline, because an outline on a
   row inside a bordered table is one line too many — and the amount stays
   black, because it is a real figure and not a preview like the new row's. */
.dl-edit{ background:var(--cream-2) }
.dl-edit:hover{ background:var(--cream-2) }

/* ── the totals, in the amount column ─────────────────────────────────────
   Right-aligned on the same edge as the line amounts above them, which is the
   whole reason to share a template. */
.dl-totals{ margin-top:6px }
/* The totals used to be pushed under the amount column by a measured offset —
   "86 + 84 + two 14px gaps" — which is a sum that is right until one column
   width changes and then is quietly wrong. They are on the same grid as the
   rows now, in the same column as the amounts, so they line up because they
   are in the same column rather than because someone added it up correctly. */
.dl-sum{ display:grid; grid-template-columns:var(--dl); gap:var(--dl-gap);
  align-items:baseline; padding:5px 4px; font-size:14px }
/* The label sits in the EACH column rather than spanning everything to its
   left, so the rule under the Total is as wide as the two things it is ruling
   off and not the width of the table. Nowrap because a wrapped "Still owing"
   would push the figure off its baseline; there is nothing to its left, so a
   long one simply reaches further. */
.dl-sum > span{ grid-column:4; color:var(--text-2); font-weight:600;
  text-align:right; white-space:nowrap }
.dl-sum > b{ grid-column:5; text-align:right; font-weight:700;
  padding-right:var(--dl-inset) }
 /* Named .tot, not .big — .big is this app's large-button class, and reusing
    it painted the Total row as an orange button. */
/* The rule belongs over the figure it is totalling, not across the whole
   table — so it runs from the start of the label column to the end of the
   amount column and stops there.

   It is ONE element. Drawing it as a border-top on the label and again on the
   number gave two strokes: first with the grid gap notched out of the middle,
   and then — once the label was stretched across the gap to meet the number —
   still broken, because these cells are aligned on their baselines and a
   16px label and a mono figure do not have the same box top. Two strokes
   that have to agree about a gap AND a height is two chances to be wrong. A
   pseudo-element spanning both columns has neither: it crosses the gap
   because it is one box, and it sits where it is put because nothing else
   is holding it up. */
.dl-sum.tot{ font-size:16px; margin-top:4px; padding-top:14px }
.dl-sum.tot::before{ content:''; grid-column:4 / 6; grid-row:1;
  align-self:start; height:0; margin-top:-9px;
  border-top:1.5px solid var(--ink) }
/* Both cells named on row 1, or the pseudo-element would take the row and
   push them onto a second one. */
.dl-sum.tot > span, .dl-sum.tot > b{ grid-row:1 }
.dl-sum.tot span{ color:var(--ink); font-weight:700 }
.dl-sum.dep{ margin-top:6px; color:var(--text-2) }

@media (max-width:820px){
  /* Qty and unit price fold into the description; the amount keeps its column,
     because the amount is the thing being scanned down. */
  .lines{ --dl: minmax(0,1fr) 104px 64px }
  .lines--read{ --dl: minmax(0,1fr) 104px }
  /* Qty, each and tax fold away; the amount keeps its column because the
     amount is what is being scanned down. */
  .dl-head > :nth-child(2), .dl-head > :nth-child(3), .dl-head > :nth-child(4),
  .dl-row > :nth-child(2), .dl-row > :nth-child(3), .dl-row > :nth-child(4){ display:none }
  .dl-new{ grid-template-columns:minmax(0,1fr) 64px 84px 84px; row-gap:8px }
  .dl-new > *{ display:block !important }
  .dl-new > :nth-child(1){ grid-column:1 / -1 }
  .dl-new > :nth-child(5){ display:none !important }
  /* Narrow: the totals still ride the row grid, which has folded too. */
  .dl-sum > span{ grid-column:1 }
  .dl-sum > b{ grid-column:2 }
  .dl-sum.tot::before{ grid-column:1 / 3 }
}

}

.lsum{margin-top:10px;padding:12px 12px 4px;font-size:13px}
.lsum div{display:flex;justify-content:space-between;padding:4px 0}
.lsum span{color:var(--grey);font-weight:600}
.lsum b{font-weight:700}
.lsum .tot{border-top:1.5px solid var(--line);margin-top:5px;padding-top:9px}
.lsum .tot b{font-size:16px;font-weight:800;letter-spacing:-.02em}

.docacts{display:flex;flex-direction:column;gap:8px;margin-top:18px}
.docacts .big{width:100%}
.hist .small{font-size:11px;word-break:break-all}

/* The same question list onboarding uses, asked again from Settings. Reusing
   .q rather than restyling it, so a business that changes how it works sees
   exactly the screen it first answered on. */
.qlist{display:flex;flex-direction:column;gap:8px}
.qlist .q{width:100%;cursor:pointer}

/* The embed snippet, for copying. Wraps rather than scrolls, because somebody
   selecting it by hand should be able to see all of it. */
.snip{display:block;background:var(--white);border:1.5px solid var(--line);
  border-radius:10px;padding:11px 13px;margin:10px 0;font-size:11.5px;
  line-height:1.6;word-break:break-all;color:var(--mid);user-select:all}

/* ── messages ─────────────────────────────────────────────────────────────
   What went out, and what Noti decided not to send. The second half is the
   part nothing else in this market shows, so it gets the same weight rather
   than being tucked into a filter. */
.srow .mch{display:grid;place-items:center;width:28px;height:28px;flex:none;
  border-radius:9px;background:var(--cream);color:var(--faint)}
.srow .mch svg{width:15px;height:15px}
.srow.warn .mch{background:var(--peach);color:var(--peach-d)}

/* Exactly what was sent, character for character. Monospace on purpose: a
   business asked "what did you send me" should be able to read it back without
   wondering whether the screen has reflowed it. */
.msgbody{background:var(--cream-2);border-radius:var(--r2);padding:15px 17px;
  font-size:13px;line-height:1.65;white-space:pre-wrap;word-break:break-word;
  color:var(--ink);font-weight:500;margin-bottom:16px}

/* A few addresses to pick from, rather than one suggestion to accept or retype. */
.slugpick{display:flex;flex-wrap:wrap;gap:7px;margin:-4px 0 14px}

/* A booking nobody has confirmed. A question rather than a warning: "we have
   not heard" is not "they are not coming", and colouring it red would have a
   salon ringing people who are perfectly fine. */
.blk .unconf{position:absolute;top:5px;right:7px;width:15px;height:15px;
  border-radius:50%;background:rgba(255,255,255,.55);font-size:10px;
  font-weight:800;display:grid;place-items:center;line-height:1}

/* A line on the bill that is not billed — the card fees, which came out of
   each payment as it arrived. Present because somebody asking what the month
   cost means all of it, dimmed because Noti is not charging for it. */
.srow.quiet{opacity:.62}

/* ── a module the plan does not include ───────────────────────────────────
   Not an error and not a sales page. One card, centred, saying what the thing
   does, which plan it is on and what that costs — because "a plan above the
   one you are on" makes somebody go and look it up, and a product that
   answers a question with a chore has not answered it. */
.locked-page{display:flex;justify-content:center;padding:40px 18px 20px}
.lp-card{width:100%;max-width:420px;background:var(--white);border-radius:var(--r);
  padding:34px 30px 26px;text-align:center}
.lp-ic{display:inline-flex;align-items:center;justify-content:center;
  width:46px;height:46px;border-radius:14px;background:var(--cream-2);
  color:var(--mid)}
.lp-ic svg{width:22px;height:22px}
.lp-card h1{font-size:22px;font-weight:800;letter-spacing:-.03em;margin-top:16px}
.lp-does{font-size:14px;font-weight:500;color:var(--grey);line-height:1.6;
  margin:9px auto 0;max-width:34ch}

.lp-plan{margin-top:24px;padding:18px;border-radius:var(--r2);
  background:var(--cream-2)}
.lp-on{font-size:12.5px;font-weight:700;color:var(--grey);
  text-transform:uppercase;letter-spacing:.06em}
.lp-on b{color:var(--ink);font-weight:800}
.lp-price{font-size:28px;font-weight:800;letter-spacing:-.03em;margin-top:6px}
.lp-price span{font-size:13px;font-weight:600;color:var(--grey);
  letter-spacing:0;margin-left:6px;font-family:var(--f)}
.lp-with{font-size:12.5px;font-weight:600;color:var(--grey);margin-top:4px}

/* A module that is not built yet. Deliberately NOT a button — offering to sell
   something that does not exist is worse than saying so plainly. */
.lp-soon{margin-top:16px;padding:13px 16px;border-radius:var(--r2);
  background:var(--cream-2);border:1px dashed var(--line);
  font-size:13.5px;font-weight:600;color:var(--grey);line-height:1.45}

.lp-card .big{width:100%;margin-top:16px;padding:14px;border-radius:99px;
  background:var(--orange);color:#fff;font-size:14.5px;font-weight:700;
  border:0;cursor:pointer}
.lp-card .big:hover{background:var(--orange-d)}

.lp-fine{font-size:12.5px;font-weight:500;color:var(--faint);line-height:1.6;
  margin:20px auto 0;max-width:36ch}

/* ── the week ahead ───────────────────────────────────────────────────────
   A row per day, seven of them, so the shape of the week is legible before a
   single word is read. Deliberately quieter than "Needs attention" sitting
   above it: this is orientation, not a list of things that are wrong. */
.week { display:flex; flex-direction:column; }

.wrow {
  display:flex; align-items:center; gap:12px;
  padding:10px 16px; border-top:1px solid var(--line);
}
.wrow:first-child { border-top:0; }

/* Today is marked with a rule down the left rather than a fill, so the eye
   finds it without the row becoming an alert. */
.wtoday { box-shadow:inset 3px 0 0 var(--orange); }

.wday {
  display:flex; flex-direction:column; align-items:center;
  min-width:38px; line-height:1.15;
}
.wday b { font-size:11px; letter-spacing:.06em; text-transform:uppercase; color:var(--faint); }
.wday i { font-style:normal; font-size:16px; font-weight:700; color:var(--ink); }

.wrow .bd { flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.wrow .bd b { font-size:13.5px; font-weight:600; }
.wrow .bd span {
  font-size:12px; color:var(--faint); line-height:1.45;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* One word, and only when the business has enough history for it to mean
   something. `unknown` renders no badge at all — a grey badge still reads as
   a judgement, and there is no judgement to make yet. */
.wload {
  display:inline-block; margin-left:6px; padding:1px 7px; border-radius:99px;
  font-size:10.5px; font-weight:700; letter-spacing:.05em; text-transform:uppercase;
  vertical-align:1px;
}
.w-heavy { background:var(--peach); color:var(--peach-d); }
.w-light { background:var(--sky);   color:var(--sky-d); }
.w-empty { background:var(--line);  color:var(--faint); }

.wsum { font-size:12px; color:var(--faint); font-weight:600; white-space:nowrap; }

@media (max-width:760px) {
  .wrow { padding:10px 14px; gap:10px; }
  .wrow .bd span { white-space:normal; }
}

/* ── the customer read ────────────────────────────────────────────────────
   Sits at the top of the customer panel, above the figures, because the
   figures are what it is made of and the sentence is the point.

   Never red. Red means stop, and this never stops anybody — it is something
   worth knowing before a decision that stays theirs. Butter for watch, peach
   for careful, and nothing at all for fine. */
.cread {
  margin:0 0 14px; padding:12px 14px; border-radius:12px;
  background:var(--butter); border:1px solid rgba(0,0,0,.05);
}
.cread-careful { background:var(--peach); }

.cread-t { display:flex; align-items:center; gap:7px; margin-bottom:5px; }
.cread-t b { font-size:11.5px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; }
.cread-t .ic { width:15px; height:15px; display:block; opacity:.75; }
.cread-t .ic svg { width:15px; height:15px; }

.cread p { margin:0; font-size:13.5px; line-height:1.5; color:var(--ink); }

/* Where the opinion came from, in the smallest type on the panel. A person
   who wants to check can; a person who does not is not made to read it. */
.cread-src {
  display:block; margin-top:7px; font-size:11px; color:var(--faint);
  font-weight:600;
}

/* ── taking card payments ─────────────────────────────────────────────────
   The status word, in Noti's vocabulary. Not a payment provider's — a
   business with nothing left to do is not "restricted", and telling somebody
   who has done everything asked of them that they are restricted is how a
   support email gets written. */
.paystate { font-weight:700; }
.ps-ready       { color:var(--mint-d); }
.ps-collecting  { color:var(--butter-d); }
.ps-checking    { color:var(--sky-d); }
.ps-not_started { color:var(--faint); }
.ps-stopped     { color:var(--peach-d); }

/* What to have in front of you, as chips rather than a bulleted column. A
   list you cannot act on from here is anxiety, not information — none of these
   can be done on this card, the form asks for all of them — so it reads as one
   short line that wraps instead of eighteen rows that scroll. */
.paylist { padding:2px 16px 14px; }
.paylist-lab {
  font-size:11px; font-weight:800; letter-spacing:.06em; text-transform:uppercase;
  color:var(--faint); margin-bottom:9px;
}
.payneeds { display:flex; flex-wrap:wrap; gap:6px; }
.payneed {
  display:inline-block; padding:4px 10px; border-radius:99px;
  background:var(--cream); border:1px solid var(--line);
  font-size:12.5px; line-height:1.4; color:var(--mid); font-weight:600;
}
.pn-more { background:transparent; border-style:dashed; color:var(--faint); }

/* Stripe's onboarding renders in here, in iframes on their origin, so there is
   nothing to style but the room. Tall enough that the first screen does not
   arrive already scrolled. */
.payonboard { min-height:420px; margin-top:4px; }
/* Stripe's notification banner inside the Noti Payments card: empty unless
   Stripe needs something, with Noti's own line above it when it does. */
.paybanner:empty { display:none; }
.paybanner { margin:0 0 12px; }
.payasks { margin:0 0 8px; padding:10px 13px; border-radius:10px; background:var(--peach); color:var(--peach-d);
  font-size:13px; font-weight:600; line-height:1.45; }
.payasks[hidden] { display:none; }
.paywait { margin:16px 2px; font-size:14px; color:var(--grey); }

/* Who moves the money, on its own line under the explanation. Smaller and
   quieter than the sentence above it: it is context, not a warning. */
.rails { display:block; margin-top:6px; color:var(--faint); }

/* A refund button sits on the row rather than behind it. Money going back is
   rare enough that a hidden menu would mean nobody finds it, and common enough
   that it must not need explaining. */
.rowbtn {
  flex:none; padding:5px 12px; border-radius:99px;
  border:1px solid var(--line); background:var(--white);
  font:600 12px/1 var(--f); color:var(--mid); cursor:pointer;
  transition:border-color .14s, color .14s;
}
.rowbtn:hover { border-color:var(--orange); color:var(--orange-d); }

/* A payment given back in full still shows what it was. Struck through rather
   than removed, because the question six months later is what happened to the
   money, and a row that is not there answers nothing. */
.amt.struck { text-decoration:line-through; color:var(--faint); }

/* ── handing over a payment request ───────────────────────────────────────
   It has already been sent. This is for the moment somebody is standing in
   front of you and the text has not arrived yet. */
.handover { margin-top:4px; }
.ho-row { display:flex; gap:8px; }
.ho-row .big { flex:1; }

/* Big enough to be scanned across a counter from a phone held at arm's
   length, and no bigger — a QR that fills the screen reads as a gimmick. */
.ho-qr {
  margin-top:14px; padding:16px; border-radius:16px;
  /* literal white, not the token: a scanner needs white in dark mode too */
  background:#fff; border:1px solid var(--line);
  display:flex; justify-content:center;
}
.ho-qr svg { width:200px; height:200px; display:block; }
/* The server said it is paid - said once, plainly, where the QR was. */
.ho-paid{display:flex;flex-direction:column;gap:4px;padding:18px 16px;border-radius:16px;
  background:var(--mint);color:var(--mint-d)}
.ho-paid b{font-size:20px;font-weight:800;letter-spacing:-.02em}
.ho-paid span{font-size:13.5px;line-height:1.5}

/* The fallback when the clipboard refuses — a link to read out loud rather
   than a dead end. */
.ho-url {
  font:600 12px/1.6 var(--m); color:var(--mid); word-break:break-all;
  text-align:center;
}

@media (max-width:520px) {
  .ho-row { flex-direction:column; }
}

/* Days left on a dispute. The only countdown in the app where running out
   means nothing can be done at all, so it turns at three days rather than one. */
.dispdue {
  flex:none; padding:4px 10px; border-radius:99px;
  background:var(--butter); color:var(--butter-d);
  font:700 11.5px/1 var(--f); white-space:nowrap;
}
.dispdue.soon { background:var(--peach); color:var(--peach-d); }

/* ── choosing an address, in onboarding ───────────────────────────────────
   The milestone screen. The field reads as one thing with the suffix rather
   than a box next to some grey text, because what they are choosing is the
   whole address and not a fragment of one. */
.addrpick { margin:24px 0 6px; }

.addr-field {
  display:flex; align-items:center; gap:0;
  border:1.5px solid var(--line); border-radius:14px; background:var(--white);
  padding:0 14px 0 0; overflow:hidden;
  transition:border-color .14s;
}
.addr-field:focus-within { border-color:var(--orange); }

.addr-field input {
  flex:1; min-width:0; border:0; outline:0; background:transparent;
  padding:15px 4px 15px 16px;
  font:700 18px/1 var(--m); color:var(--ink); letter-spacing:-.01em;
}
.addr-suffix {
  flex:none; font:600 17px/1 var(--m); color:var(--faint); white-space:nowrap;
}

/* Says one thing at a time, and nothing at all until there is something true
   to say. A field that shouts while somebody is still typing is a field they
   stop reading. */
.addr-say { min-height:20px; margin-top:10px; font-size:13px; font-weight:600; }
.addr-say.ok { color:var(--mint-d); }
.addr-say.no { color:var(--peach-d); }

.addr-alts { display:flex; flex-wrap:wrap; gap:7px; margin-top:12px; }
.addr-alt {
  padding:6px 12px; border-radius:99px;
  border:1px solid var(--line); background:var(--white);
  font:600 13px/1 var(--m); color:var(--mid); cursor:pointer;
}
.addr-alt:hover { border-color:var(--orange); color:var(--orange-d); }

@media (max-width:520px) {
  .addr-field input { font-size:16px; padding-left:13px; }
  .addr-suffix { font-size:15px; }
}

/* Standing at a business address that is not yours. One button per business
   you DO work at, so the way out is a tap rather than a URL to remember.

   THIS USED TO BE CALLED .doors, and it sat a thousand lines after the gate's
   own .doors — so it quietly won, and the front door was a single stacked
   column at every width, grid and media query alike. Two screens, one class
   name, and the later one deciding the earlier one's layout. These are a
   stack of pill buttons, not a row of cards, so they get their own name. */
.waylist{ display:flex; flex-direction:column; gap:9px; margin-top:22px }

/* ── what a card payment costs ────────────────────────────────────────────
   In dollars. 2.40% + 30c is arithmetic; "on $100 you keep $97.45" is a fact,
   and PLATFORM.md is explicit that the rate is quoted in dollars because
   nobody reads percentages. */
.feesay { font-size:14px; font-weight:600; color:var(--ink); margin-bottom:10px; }

.feerow { display:flex; flex-wrap:wrap; gap:8px; }
.feeex {
  display:flex; flex-direction:column; gap:1px;
  padding:8px 12px; border-radius:12px;
  background:var(--cream); border:1px solid var(--line);
}
.feeex b { font:700 14px/1 var(--m); color:var(--ink); }
.feeex i { font-style:normal; font-size:11.5px; color:var(--faint); font-weight:600; }

/* The question every business asks second. */
.feecash { margin-top:10px; font-size:12px; color:var(--faint); font-weight:600; }

/* How far through the Free allowance, as one number. A bar would imply
   precision nobody needs and take a row to itself; the percentage sits where
   an amount normally does. */

/* ── what is on order ─────────────────────────────────────────────────────
   Lines under their order, indented, because "nine of twelve arrived" is a
   fact about a line and not about the order. Partial receiving is the normal
   case — a supplier sends most of it and the rest next week. */
.porow { border-top:1px solid var(--line); }
.porow:first-child { border-top:0; }

.poline {
  display:flex; align-items:center; gap:10px;
  padding:7px 16px 7px 30px; font-size:13px;
}
.poline .w { flex:1; min-width:0; color:var(--mid); }
.poline .q { flex:none; font-size:12.5px; color:var(--faint); font-weight:600; }
.poline.done .w { color:var(--faint); }
.ptick { width:16px; height:16px; color:var(--mint-d); display:block; }
.ptick svg { width:16px; height:16px; }


/* ── purchasing ──────────────────────────────────────────────────────────
   The order screen. The receiving card on Stock keeps .poline; this is the
   fuller row, where a line carries its own money and its own buttons. */
.postate { flex:none; font-size:11.5px; font-weight:800; letter-spacing:.01em;
  padding:3px 9px; border-radius:999px; white-space:nowrap; }
.postate.go   { background:var(--lav);    color:var(--lav-d); }
.postate.wait { background:var(--butter); color:var(--butter-d); }
.postate.done { background:var(--mint);   color:var(--mint-d); }
.postate.off  { background:var(--cream-2);color:var(--faint); }

.polines { display:flex; flex-direction:column; }
.poline2 {
  display:grid; align-items:center; gap:12px;
  grid-template-columns: minmax(0,1fr) auto auto auto auto;
  padding:11px 16px; border-top:1px solid var(--line-2); font-size:13px;
}
.poline2:first-child { border-top:0; }
.poline2 .w { min-width:0; }
.poline2 .w b { display:block; font-weight:700; }
/* Their code under the name, in the mono face, because a part number is read
   character by character and a proportional font makes 0 and O the same. */
.poline2 .w .sku { display:block; font-size:12px; color:var(--faint); margin-top:1px; }
.poline2 .q  { font-size:12.5px; color:var(--faint); font-weight:700; white-space:nowrap; }
.poline2 .ea { font-size:12.5px; color:var(--faint); white-space:nowrap; }
.poline2 .amt { font-weight:700; white-space:nowrap; }
.poline2 .ax { display:flex; gap:6px; }
.poline2.done .w b { color:var(--faint); }
.poline2.part .q { color:var(--butter-d); }
/* An overage is not an error state. It is butter, like anything else worth a
   second look, and never red — nothing went wrong. */
.poline2.over .q { color:var(--butter-d); }
.poline2 .q .over { font-style:normal; color:var(--butter-d); font-weight:800; }

.rowbtn.ghost { border-color:var(--line); color:var(--faint); }
.rowbtn.ghost:hover { border-color:var(--mid); color:var(--mid); }

/* The confirmation before an overage is taken in. Three rows, the last one
   the reason the dialog exists. */
.variance { display:flex; flex-direction:column; gap:0; margin:4px 0 14px;
  border:1px solid var(--line); border-radius:var(--r3); overflow:hidden; }
.variance > div { display:flex; justify-content:space-between; align-items:baseline;
  padding:10px 14px; border-top:1px solid var(--line-2); }
.variance > div:first-child { border-top:0; }
.variance span { font-size:13px; color:var(--text-2); }
.variance b { font-size:15px; font-weight:800; }
.variance .over { background:var(--butter); }
.variance .over span, .variance .over b { color:var(--butter-d); }

/* .nudge-b b is display:block, which is right for the headline and wrong for
   emphasis inside the sentence — a nested <b> broke the line and left the full
   stop on its own. <em> carries the weight without the break. */
.nudge-b span em { font-style:normal; font-weight:800; }

/* ── the forward position ────────────────────────────────────────────────
   Five numbers across a row, and the one that matters is last. The columns are
   deliberately mono and narrow: this is a table somebody scans down looking
   for the red one, not a sentence they read. */
.poline2.fwd { grid-template-columns: minmax(0,1fr) repeat(4, 4.5rem) minmax(0,12rem); }
.poline2.fwd .fnum { text-align:right; font-size:12.5px; font-weight:700;
  color:var(--mid); white-space:nowrap; }
.poline2.fwd .fnum.in  { color:var(--mint-d); }
.poline2.fwd .fnum.out { color:var(--faint); }
.poline2.fwd .fwhen { text-align:right; font-size:12.5px; white-space:nowrap; }
.poline2.fwd .fwhen .short { color:var(--butter-d); font-weight:800; }

@media (max-width:980px){
  .poline2.fwd { grid-template-columns: minmax(0,1fr) auto auto; row-gap:4px; }
  .poline2.fwd .fnum.in, .poline2.fwd .fnum.out { display:none; }
  .poline2.fwd .fwhen { grid-column:1 / -1; text-align:left; }
}

/* requests waiting on an answer, in the diary rail */
.qrow.req{ display:flex; flex-direction:column; gap:8px; align-items:stretch }
.qrow.req .qwho b{ display:block; font-size:13.5px }
.qrow.req .qwho span{ display:block; font-size:12px; color:var(--faint); line-height:1.45 }
.qrow.req .qwhen{ color:var(--mid) !important; font-weight:600 }
.qrow.req .qheld{ color:var(--butter-d) !important; font-weight:700 }
.qrow.req .qans{ color:var(--lav-d) !important }
.qrow.req .qax{ display:flex; gap:5px; flex-wrap:wrap }
.qrow.req .qax .rowbtn{ flex:1 1 auto; font-size:12px; padding:5px 8px }
.qnote{ font-size:12px; color:var(--faint); line-height:1.45; padding:8px 14px 12px }

/* the logo, in settings */
.brandrow{ display:flex; gap:18px; align-items:center; padding:16px 20px }
.brandbox{ width:150px; height:72px; flex:none; border:1px solid var(--line);
  border-radius:var(--r3); background:var(--cream-2); display:flex;
  align-items:center; justify-content:center; padding:10px; overflow:hidden }
/* Same trap as the booking page: a viewBox-only SVG needs a stated dimension
   or it collapses. Height here, because the box is wider than it is tall. */
.brandbox img{ max-width:100%; height:100%; width:auto; object-fit:contain }
.brandnone{ font-size:13px; font-weight:700; color:var(--faint); text-align:center;
  line-height:1.3 }
.brandsay b{ display:block; font-size:14px }
.brandsay span{ display:block; font-size:13px; color:var(--text-2);
  line-height:1.5; margin-top:3px; max-width:58ch }
@media (max-width:640px){
  .brandrow{ flex-direction:column; align-items:flex-start }
}

/* the form builder's question list */
.fieldrow{ grid-template-columns: minmax(0,1fr) auto }
.fieldrow .w b .req{ font-style:normal; font-size:11.5px; font-weight:700;
  color:var(--orange-d); background:var(--orange-l); padding:1px 6px;
  border-radius:999px; margin-left:6px; vertical-align:1px }
.svcform{ font:inherit; font-size:13px; padding:7px 10px; border:1px solid var(--line);
  border-radius:10px; background:var(--white); max-width:16rem }

.nudge.ok { background:var(--mint); }
.nudge.ok .nudge-b b, .nudge.ok .nudge-b span { color:var(--mint-d); }

@media (max-width:820px){
  .poline2 { grid-template-columns: minmax(0,1fr) auto; row-gap:6px; }
  .poline2 .ea { display:none; }
  .poline2 .ax { grid-column:1 / -1; }
}

/* ── waiting ──────────────────────────────────────────────────────────────
   The dot is the mark. So the dot is what moves, and nothing else does.

   It walks the width of the word and back — not a spin, because a spin says
   "working" and this is a wait of a few hundred milliseconds where the honest
   message is "nearly there". Slow enough to read as deliberate rather than
   frantic, and it keeps the word exactly where it is so nothing jumps when the
   app arrives.

   No text. "Loading…" underneath was a second thing saying what the animation
   already says, and on a fast connection it appeared and vanished so quickly
   it read as a flicker. */
.logo.waiting { position:relative; }

.logo.waiting i {
  animation: notiwalk 1.5s cubic-bezier(.65,0,.35,1) infinite;
}

@keyframes notiwalk {
  0%   { transform:translateX(0)      scale(1);   opacity:1 }
  /* Squashes very slightly at each end, the way something with weight does
     when it turns around. Two per cent — enough to feel, not enough to see. */
  22%  { transform:translateX(-46px)  scale(.92); opacity:.55 }
  50%  { transform:translateX(0)      scale(1);   opacity:1 }
  72%  { transform:translateX(4px)    scale(.92); opacity:.55 }
  100% { transform:translateX(0)      scale(1);   opacity:1 }
}

/* Somebody who has asked not to see movement gets a dot that breathes instead
   of one that travels. Still alive, nothing sliding across their screen. */
@media (prefers-reduced-motion: reduce) {
  .logo.waiting i {
    animation: notibreathe 1.6s ease-in-out infinite;
  }
  @keyframes notibreathe {
    0%, 100% { opacity:1 }
    50%      { opacity:.3 }
  }
}

/* ── a form that fills the screen ───────────────────────────────────────
   panel--wide was the wrong tool for a form: it is a two-column grid with
   the fields down one side and `extra` down the other, so a caller that put
   everything in `extra` got a tall empty column with a rule beside it. That
   is what a job form looked like.

   This is the other shape. One column, the width of something you read, and
   on a phone or a tablet the whole screen — which is where a completion
   checklist actually gets filled in. */
.panel--full{ width:min(760px,100%); max-height:100dvh }
.panel--full .panel-body{ display:block; padding-bottom:var(--s5) }

@media (max-width:820px){
  .panel--full{ width:100%; height:100dvh; max-height:100dvh;
                border-radius:0; margin:0 }
  .panel--full .panel-body{ max-height:none }
}

/* The questions themselves need room to breathe when there are twenty of
   them. Inside a dialog they were set for four. */
.panel--full .jf-form{ gap:22px }

/* Sign-in (25 Sep): the two ways that type something, as a choice; the
   password's Show; the six-digit code; the resend row. */
.gate-seg{display:grid;grid-template-columns:1fr 1fr;gap:4px;margin-top:22px;padding:4px;
  background:var(--cream);border-radius:14px}
.gseg{min-height:44px;border:0;border-radius:11px;background:none;font:inherit;font-size:14px;
  font-weight:700;color:var(--grey);cursor:pointer}
.gseg.on{background:#fff;color:var(--ink);box-shadow:0 1px 2px rgba(26,22,19,.08)}
.gate-form[hidden]{display:none}
.pw-wrap{position:relative;display:block}
.pw-wrap input{width:100%;padding-right:64px}
.pw-eye{position:absolute;right:6px;top:50%;transform:translateY(-50%);min-height:36px;min-width:52px;
  border:0;border-radius:10px;background:none;font:inherit;font-size:13px;font-weight:700;
  color:var(--grey);cursor:pointer}
.otp{font-size:26px !important;letter-spacing:.35em;text-align:center;font-variant-numeric:tabular-nums}
.gate-row{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:12px}
.gate-row .big{min-height:48px;font-size:14px}
.fld-hint{font-size:12.5px;color:var(--grey);margin:-4px 0 0;line-height:1.45}
@media (max-width:380px){ .gate-row{grid-template-columns:1fr} }
.gate-sub + .keyb, .gate-sub + .gate-seg{margin-top:22px}

/* Where a job is up to: one tap each (work.js stateSheet). */
.st-pick{display:grid;gap:8px;margin:4px 0 12px}
.st-pick .sys-b{width:100%;min-height:44px;justify-content:flex-start}

/* Take payment under the thumb on a phone (till.js) - like the job page's dock */
.tilldock{display:none}
@media (max-width:699px){
  .tilldock{display:block;position:sticky;z-index:30;bottom:calc(68px + env(safe-area-inset-bottom));
    margin:12px 0 0;padding:8px 0;background:var(--cream);border-top:1px solid var(--line)}
  .tilldock .paybtn{width:100%;min-height:48px;margin:0}
  .cart #cart-pay{display:none}
}
