/* ==========================================================================
   Dufour Job Book - the job as it runs (2026-08-01)
   Payment state, progress bars, the job header, the cost list where the money
   is, who is on the job, and the Money owed screen.

   A separate sheet from app.css deliberately: this is one round of work with
   one reason for existing (Jay's 31 July feedback), and keeping it together
   means it can be read, argued with or removed as a unit. Every colour is a
   token from app.css's :root; nothing here invents its own.
   ========================================================================== */

/* ---- payment state, as a pill ---- */
.pay-pill{
  display:inline-flex; align-items:center; padding:3px 10px; border-radius:var(--r-pill);
  font-size:11px; font-weight:700; letter-spacing:.02em; white-space:nowrap;
}
.pay-settled{ background:var(--won-bg); color:var(--won-text); }
.pay-part{ background:var(--due-bg); color:var(--due-text); }
.pay-unpaid{ background:var(--overdue-bg); color:var(--overdue); }
.pay-overpaid{ background:var(--due-bg); color:var(--due-text); }
.pay-unrecorded{ background:var(--info-bg); color:var(--text-muted); }

/* ---- three bars: start, middle, finish ----
   Deliberately a shape, not a percentage. Jay wants to read a row of jobs
   across a screen and know which are near the end; three filled blocks do
   that at a glance and "67%" does not. */
.pbars{ display:inline-flex; align-items:center; gap:3px; vertical-align:middle; }
.pbar{
  display:inline-block; width:16px; height:7px; border-radius:2px;
  background:var(--border-strong); transition:background .12s ease, transform .12s ease;
}
.pbars.small .pbar{ width:11px; height:5px; }
.pbar.on{ background:var(--brand-yellow); box-shadow:inset 0 0 0 1px rgba(17,24,39,.14); }
.pbar.tappable{ cursor:pointer; }
.pbar.tappable:hover{ transform:scaleY(1.45); background:var(--yellow-hover); }
.pbar.tappable:focus-visible{ outline:2px solid var(--chrome); outline-offset:2px; }
.pbars-label{ margin-left:8px; font-size:12px; font-weight:600; color:var(--text-muted); }
/* Named where they are a control. A tap target with no name is a guess. */
.pbars.captioned{ align-items:flex-start; gap:6px; }
.pbar-wrap{ display:inline-flex; flex-direction:column; align-items:center; gap:3px; }
.pbars.captioned .pbar{ width:34px; height:9px; }
.pbar-cap{ font-size:9.5px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:rgba(255,255,255,.55); }  /* .45 measured 4.48:1 at 9.5px */
.pbar-cap.on{ color:var(--brand-yellow); }
.pbars.captioned .pbars-label{ align-self:center; }

/* ---- the job header: what the job IS, before anything else ---- */
.job-header{
  background:var(--chrome); color:var(--text-invert); border-radius:var(--r-card);
  padding:16px 18px; margin-bottom:14px;
}
.jh-top{ display:flex; align-items:flex-start; justify-content:space-between; gap:14px; flex-wrap:wrap; }
.jh-title h2{ margin:0 0 7px; font-size:21px; line-height:1.2; color:var(--text-invert); }
/* Where the job is. On the dark header, so it is written against the header's
   own text colour rather than inherited: the additional-works block was white
   on near-white at contrast 1.06 on 11 August for exactly that reason. */
.jh-address{ margin:-3px 0 8px; font-size:13px; }
.jh-address a{ color:var(--brand-yellow); text-decoration:none; border-bottom:1px solid rgba(242,229,25,.4); }
.jh-address a:hover{ border-bottom-color:var(--brand-yellow); }
.jh-tags{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.jh-type{
  font-size:12.5px; font-weight:700; letter-spacing:.02em; color:var(--chrome);
  background:var(--brand-yellow); padding:3px 10px; border-radius:var(--r-pill);
}
.jh-actions{ display:flex; gap:8px; flex-wrap:wrap; }
.jh-progress{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  margin-top:14px; padding-top:13px; border-top:1px solid rgba(255,255,255,.14);
}
.jhp-label{ font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:rgba(255,255,255,.62); }
.jh-progress .pbar{ background:rgba(255,255,255,.22); }
.jh-progress .pbar.on{ background:var(--brand-yellow); }
.jh-progress .pbars-label{ color:rgba(255,255,255,.9); }
.jh-progress .hint{ color:rgba(255,255,255,.5); font-size:11.5px; }
.jh-figures{
  display:grid; grid-template-columns:repeat(6,1fr); gap:1px; margin-top:15px;
  background:rgba(255,255,255,.12); border-radius:8px; overflow:hidden;
}
.jhf{ background:var(--chrome-2); padding:11px 12px; display:flex; flex-direction:column; gap:2px; }
.jhf-label{ font-size:10.5px; font-weight:700; letter-spacing:.07em; text-transform:uppercase; color:rgba(255,255,255,.55); }
.jhf-value{ font-size:17px; font-weight:700; line-height:1.2; }
.jhf-sub{ font-size:10.5px; color:rgba(255,255,255,.5); }
.jhf.tone-good .jhf-value{ color:#7DDBA0; }
.jhf.tone-bad .jhf-value{ color:#FF9AA3; }
.jhf.tone-warn .jhf-value{ color:var(--brand-yellow); }
.jhf.tone-muted .jhf-value{ font-size:13px; color:rgba(255,255,255,.55); font-weight:600; }
.jh-note{
  margin-top:13px; padding:10px 12px; border-radius:8px; font-size:12.5px;
  background:rgba(255,255,255,.08); color:rgba(255,255,255,.86);
}
.jh-note.warn{ background:rgba(242,229,25,.14); color:var(--brand-yellow); }

/* ---- the cost list, sitting where the totals are ---- */
.form-3col{ display:grid; grid-template-columns:1fr 1fr 1fr; gap:13px; }
.cost-items{ display:flex; flex-direction:column; gap:1px; background:var(--border); border-radius:8px; overflow:hidden; }
.cost-item{
  display:grid; grid-template-columns:118px 1fr 96px 96px 30px; align-items:center; gap:10px;
  background:var(--surface); padding:9px 12px; font-size:13px;
}
.ci-cat{ font-size:11px; font-weight:700; letter-spacing:.02em; color:var(--text-muted); text-transform:uppercase; }
.ci-desc{ font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ci-date{ font-size:12px; color:var(--text-muted); }
.ci-amt{ text-align:right; font-weight:700; font-variant-numeric:tabular-nums; }
.ci-del{
  border:0; background:transparent; color:var(--text-muted); cursor:pointer;
  font-size:17px; line-height:1; padding:2px 5px; border-radius:4px;
}
.ci-del:hover{ background:var(--overdue-bg); color:var(--overdue); }
/* A saved cost has to be impossible to miss. The alternative is the same £200
   entered three times, which is what happened. */
.cost-item.just-added{ animation:costFlash 2.4s ease-out; }
@keyframes costFlash{
  0%{ background:var(--brand-yellow); }
  22%{ background:var(--yellow-soft); }
  100%{ background:var(--surface); }
}
.cost-add{ margin-top:14px; padding-top:14px; border-top:1px solid var(--border); }
.cost-add-foot{ display:flex; align-items:flex-end; gap:12px; margin-top:10px; }
.cost-add-foot .form-row{ margin:0; max-width:190px; }
.empty-state.small{ padding:18px 14px; font-size:12.5px; }
/* Each total opens the rows behind it. */
tr.cost-open, .cost-row.cost-open{ cursor:pointer; }
tr.cost-open:hover, .cost-row.cost-open:hover{ background:var(--yellow-soft); }
.open-hint{ color:var(--text-muted); font-weight:700; }
.kpi-card.clickable{ cursor:pointer; }
.kpi-card.clickable:hover{ box-shadow:var(--shadow-lift); }

/* ---- who is on the job ---- */
.crew-list{ display:flex; flex-direction:column; gap:1px; background:var(--border); border-radius:8px; overflow:hidden; }
.crew-row{
  display:grid; grid-template-columns:150px 1fr 1fr 30px; align-items:center; gap:10px;
  background:var(--surface); padding:9px 12px; font-size:13px;
}
.crew-trade{ font-weight:700; }
.crew-person.needed{ color:var(--due-text); font-style:italic; }
.crew-note{ font-size:12px; color:var(--text-muted); }
.crew-add{ margin-top:14px; padding-top:14px; border-top:1px solid var(--border); }
.crew-add .btn{ margin-top:10px; }

/* ---- Jay's own steps on the timeline ---- */
.step-add{ margin-top:14px; padding-top:14px; border-top:1px solid var(--border); }
.step-add .form-3col .btn{ width:100%; }
.tl-item.tl-mine{ border-left:3px solid var(--brand-yellow); padding-left:10px; }
.tl-item.tl-mine .tl-title{ display:flex; align-items:center; justify-content:space-between; gap:8px; }

/* ---- Money owed ---- */
.owed-group{ margin-bottom:16px; }
.owed-note{ margin:-4px 0 12px; }
.owed-list{ display:flex; flex-direction:column; gap:1px; background:var(--border); border-radius:8px; overflow:hidden; }
.owed-row{
  display:flex; align-items:center; gap:12px; width:100%; text-align:left;
  background:var(--surface); border:0; padding:12px 14px; cursor:pointer; font:inherit;
}
.owed-row:hover{ background:var(--yellow-soft); }
.or-main{ flex:1 1 auto; display:flex; flex-direction:column; gap:2px; min-width:0; }
.or-name{ font-weight:700; font-size:14px; }
.or-meta{ font-size:12px; color:var(--text-muted); }
.or-money{ display:flex; flex-direction:column; align-items:flex-end; gap:2px; }
.or-owed{ font-weight:700; font-size:15px; font-variant-numeric:tabular-nums; color:var(--due-text); }
.or-owed.bad{ color:var(--overdue); }
.or-owed.muted{ color:var(--text-muted); font-size:13.5px; }
.or-sub{ font-size:11.5px; color:var(--text-muted); }
/* The inc-VAT chase figure, under the ex-VAT one it is derived from. Smaller
   and quieter on purpose: ex VAT is still the number the business is measured
   on, this is what the customer has to hand over. */
.or-vat{ font-size:12px; font-weight:600; color:var(--text-muted); font-variant-numeric:tabular-nums; }
.pay-total .pay-inc{ font-size:11px; opacity:.75; font-variant-numeric:tabular-nums; }
.or-go{ color:var(--text-muted); font-size:20px; line-height:1; }

/* ---- payment modal ---- */
.pay-current{ display:grid; grid-template-columns:repeat(3,1fr); gap:12px; }
.pay-current > div{ background:var(--info-bg); border-radius:8px; padding:10px 12px; display:flex; flex-direction:column; gap:3px; }
.pay-current .label{ font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--text-muted); }
.pay-current strong{ font-size:16px; }
.pay-current .neg{ color:var(--overdue); }
.pay-current .pos{ color:var(--won-text); }
.pay-quick{ display:flex; gap:8px; flex-wrap:wrap; margin-top:6px; }

/* ---- the VAT question on the payment box (2026-08-31) ----
   Two questions live here and they are NOT the same question:
     .pay-ask #payAskVat   does this JOB charge VAT (asked once, on a job that
                           has never been told; neither answer pre-picked,
                           because a blank is not a no)
     .pay-ask in #payIncHost  does the figure he just typed INCLUDE the VAT
                           (only on a job that does charge it)
   Both are chunky tap targets: this is used one-handed on site. */
.pay-pills{ display:flex; gap:6px; flex-wrap:wrap; margin-bottom:10px; }
.pill-owed{ background:var(--info-bg); color:#4B5563; font-variant-numeric:tabular-nums; }
.pay-ask{
  margin-top:12px; padding:12px 14px; border-radius:8px;
  background:var(--info-bg); display:flex; flex-direction:column; gap:8px;
}
.pay-ask > label{
  font-size:11px; font-weight:700; letter-spacing:.06em;
  text-transform:uppercase; color:var(--text-muted);
}
.pay-ask .hint{ margin:0; }
.pay-choice{ display:flex; gap:8px; flex-wrap:wrap; }
.pay-choice .btn{ flex:1 1 150px; min-height:42px; }
.pay-split{ font-variant-numeric:tabular-nums; line-height:1.5; }
.pr-banked{ display:block; font-size:11px; color:var(--text-muted); font-variant-numeric:tabular-nums; }
/* On a phone the two answers stack rather than squeeze to unreadable widths.
   flex-basis is a WIDTH hint on a row and a HEIGHT one once the direction
   flips, which is how a 240px basis left a blank gap in the VAT layout on
   12 August: state the direction, then let the basis go. */
@media (max-width:560px){
  .pay-choice{ flex-direction:column; }
  .pay-choice .btn{ flex:0 0 auto; }
}
.finish-owed{
  margin-top:12px; padding:12px 14px; border-radius:8px;
  background:var(--due-bg); color:var(--due-text); font-size:13px;
}
.finish-owed.muted{ background:var(--info-bg); color:var(--text-muted); }

/* ---- counts, badges, inline links ---- */
.count-chip{
  display:inline-block; margin-left:8px; padding:2px 9px; border-radius:var(--r-pill);
  background:var(--info-bg); color:var(--text-muted); font-size:12px; font-weight:700; vertical-align:middle;
}
.inline-link{ color:inherit; font-weight:600; text-decoration:underline; text-underline-offset:2px; }
.nav-badge{
  display:inline-flex; align-items:center; justify-content:center; min-width:19px; height:19px;
  padding:0 5px; border-radius:var(--r-pill); background:var(--brand-yellow); color:var(--chrome);
  font-size:11px; font-weight:800; margin-left:auto;
}
.sidebar.collapsed .nav-badge{ display:none; }
.tabbar button{ position:relative; }
.tab-badge{
  position:absolute; top:3px; left:50%; transform:translateX(6px);
  min-width:17px; height:17px; padding:0 4px; border-radius:var(--r-pill);
  background:var(--brand-yellow); color:var(--chrome);
  font-size:10px; font-weight:800; line-height:17px; text-align:center;
}

/* ---- the running margin, above Jay's two charts ---- */
.margin-hero{ display:flex; align-items:flex-start; justify-content:space-between; gap:16px; flex-wrap:wrap; }
.mh-label{ font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--text-muted); }
.mh-value{ font-size:40px; font-weight:800; line-height:1.05; letter-spacing:-.02em; }
.mh-sub{ font-size:12.5px; color:var(--text-muted); }
.mh-chip{ display:inline-block; padding:5px 12px; border-radius:var(--r-pill); font-size:12px; font-weight:700; }
.mh-chip.good{ background:var(--won-bg); color:var(--won-text); }
.mh-chip.warn{ background:var(--due-bg); color:var(--due-text); }

/* ==========================================================================
   2026-08-03, Jay's 2 August feedback: the job's dates, the payment list, the
   settle button, the forward view and the overheads editor.
   ========================================================================== */

/* ---- ITEM A: the job's dates, in the header where the facts are ---- */
.jh-dates{
  display:flex; flex-direction:column; gap:8px;
  border-top:1px solid var(--border); margin-top:12px; padding-top:12px;
}
.jh-dates.overran{ border-top-color:var(--overdue); }
.jhd-fields{ display:flex; align-items:flex-end; gap:16px; flex-wrap:wrap; }
.jhd-field{ display:flex; flex-direction:column; gap:4px; }
.jhd-field > span,
.jhd-push > span{
  font-size:10.5px; text-transform:uppercase; letter-spacing:.06em;
  font-weight:700; color:var(--text-muted);
}
.jhd-field input{
  border:1px solid var(--border-strong); border-radius:8px; padding:7px 10px;
  font:inherit; font-size:13.5px; background:var(--surface); color:var(--text);
  min-height:38px;   /* a real tap target on a phone */
}
.jhd-field input:focus{ outline:2px solid var(--brand-yellow); outline-offset:1px; border-color:var(--brand-yellow); }
.jhd-push{ display:flex; flex-direction:column; gap:4px; }
.jhd-btns{ display:flex; gap:6px; flex-wrap:wrap; }
.jhd-btns .btn[disabled]{ opacity:.45; cursor:not-allowed; }
.jhd-sense{ font-size:12.5px; color:var(--text-muted); }
.jh-dates.overran .jhd-sense{ color:var(--overdue); font-weight:600; }

/* ---- ITEM D: the payment list ---- */
.pay-list{ display:flex; flex-direction:column; gap:1px; background:var(--border); border-radius:8px; overflow:hidden; }
.pay-row{
  display:grid; grid-template-columns:110px 1fr auto; align-items:center; gap:12px;
  background:var(--surface); padding:10px 14px; font-size:13.5px;
}
.pr-date{ font-size:12.5px; color:var(--text-muted); font-variant-numeric:tabular-nums; }
.pr-meta{ color:var(--text-muted); font-size:12.5px; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.pr-amt{ font-weight:700; font-variant-numeric:tabular-nums; }
.pay-row.pay-neg .pr-amt{ color:var(--overdue); }
/* The imported figures. Marked as what they are rather than given a date we
   would have had to invent. */
.pay-row.pay-opening{ background:var(--info-bg); }
.pay-row.pay-opening .pr-date{ font-style:italic; }
.pay-total{
  display:grid; grid-template-columns:repeat(3,1fr); gap:12px; margin-top:12px;
  background:var(--chrome); color:#fff; border-radius:8px; padding:12px 14px;
}
.pay-total > div{ display:flex; flex-direction:column; gap:3px; }
.pay-total .label{ font-size:10.5px; text-transform:uppercase; letter-spacing:.06em; opacity:.72; }
.pay-total strong{ font-size:16px; font-variant-numeric:tabular-nums; }
.pay-total .pos{ color:var(--brand-yellow); }
.pay-total .neg{ color:#FF8A93; }
.pay-foot{ display:flex; gap:8px; margin-top:12px; flex-wrap:wrap; }

/* ---- ITEM E: settle a job from the Money owed row ---- */
/* The row is a flex DIV holding the open button plus the settle button, so the
   two are siblings. It used to be a single <button>, and a button inside a
   button is un-nested by the parser. */
.owed-row > .or-open{
  display:flex; align-items:center; gap:12px; flex:1 1 auto; min-width:0;
  background:transparent; border:0; padding:0; text-align:left; cursor:pointer; font:inherit; color:inherit;
}
.or-settle{ flex:0 0 auto; white-space:nowrap; }

/* ---- ITEM J: the forward view ---- */
.cu-group{ margin-bottom:18px; }
.cu-group:last-of-type{ margin-bottom:12px; }
.cu-head{ display:flex; align-items:baseline; justify-content:space-between; gap:12px; margin-bottom:2px; }
.cu-head h4{ margin:0; font-size:14px; font-weight:800; }
.cu-note{ margin-bottom:8px; }
.rec-sub{ font-size:11px; color:var(--text-muted); white-space:nowrap; }
.rec-right{ flex-direction:column; align-items:flex-end; gap:2px !important; }

/* ---- ITEM I: the overheads editor ---- */
.ovh-list{ display:flex; flex-direction:column; gap:1px; background:var(--border); border-radius:8px; overflow:hidden; }
.ovh-row{ display:grid; grid-template-columns:1fr 130px 30px; gap:10px; align-items:center; background:var(--surface); padding:8px 12px; }
.ovh-row input{
  border:1px solid transparent; border-radius:6px; padding:6px 8px; font:inherit; font-size:13.5px;
  background:transparent; color:var(--text); min-width:0; min-height:34px;
}
.ovh-row input:hover{ border-color:var(--border-strong); background:var(--surface); }
.ovh-row input:focus{ outline:2px solid var(--brand-yellow); outline-offset:0; border-color:var(--brand-yellow); background:var(--surface); }
.ovh-amt{ text-align:right; font-variant-numeric:tabular-nums; font-weight:600; }
.ovh-add{ display:grid; grid-template-columns:1fr 130px auto; gap:10px; margin-top:12px; }
.ovh-add input{ border:1px solid var(--border-strong); border-radius:8px; padding:8px 10px; font:inherit; font-size:13.5px; min-height:38px; }

/* ---- the assistant's "nothing matched" card (item G) ---- */
.draft-card.draft-empty{ border-color:var(--due); background:var(--due-bg); }
.draft-none{ font-size:13px; line-height:1.5; padding:4px 0 2px; }

/* ---- a tick box that reads as a row, for the confirm forms ---- */
.check-row{ display:flex; align-items:center; gap:9px; margin-top:10px; font-size:13.5px; cursor:pointer; }
.check-row input{ width:17px; height:17px; accent-color:var(--brand-yellow); flex:0 0 auto; }

@media (max-width:1100px){
  .jh-figures{ grid-template-columns:repeat(3,1fr); }
}
@media (max-width:820px){
  .form-3col{ grid-template-columns:1fr; }
  .jh-figures{ grid-template-columns:repeat(2,1fr); }
  .jh-top{ flex-direction:column; }
  .jh-actions{ width:100%; }
  .jh-actions .btn{ flex:1 1 auto; }
  /* On a phone a cost row stacks rather than scrolling sideways: type and date
     on one line, what it was for below, amount on the right. */
  .cost-item{
    grid-template-columns:1fr auto 30px;
    grid-template-areas:"cat date del" "desc amt amt";
    row-gap:3px;
  }
  .ci-cat{ grid-area:cat; }
  .ci-date{ grid-area:date; text-align:right; }
  /* SCOPED, and it has to be. A bare `.ci-del{grid-area:del}` reaches every
     other grid that reuses this button, and none of them declares a `del`
     area, so the button is placed on an implicit row and drops below the row
     it belongs to. That was already happening on the monthly-overheads sheet
     (.ovh-row) at phone width before round 28 went near it, and it did it again
     to .ovm-row on the first screenshot of the new screen. A rule written for
     one component belongs to that component. */
  .cost-item .ci-del{ grid-area:del; }
  .ci-desc{ grid-area:desc; white-space:normal; }
  .ci-amt{ grid-area:amt; }
  .crew-row{
    grid-template-columns:1fr auto 30px;
    grid-template-areas:"trade person del" "note note note";
    row-gap:3px;
  }
  .crew-trade{ grid-area:trade; }
  .crew-person{ grid-area:person; text-align:right; }
  .crew-note{ grid-area:note; }
  .crew-row .ci-del{ grid-area:del; }
  .cost-add-foot{ flex-direction:column; align-items:stretch; }
  .cost-add-foot .form-row{ max-width:none; }
  .pay-current{ grid-template-columns:1fr; }
  .mh-value{ font-size:32px; }
  /* The dates go full width on a phone rather than squeezing two date pickers
     and three buttons onto one line. */
  .jhd-fields{ flex-direction:column; align-items:stretch; gap:12px; }
  .jhd-field input{ width:100%; }
  .jhd-btns .btn{ flex:1 1 auto; }
  .pay-row{ grid-template-columns:1fr auto; grid-template-areas:"date amt" "meta meta"; row-gap:3px; }
  .pr-date{ grid-area:date; }
  .pr-amt{ grid-area:amt; text-align:right; }
  .pr-meta{ grid-area:meta; white-space:normal; }
  .pay-total{ grid-template-columns:1fr; gap:8px; }
  /* Settle drops under the row on a phone so the tap target is full width and
     cannot be hit by accident while reaching for the row itself. */
  .owed-row{ flex-wrap:wrap; }
  .owed-row > .or-open{ flex:1 1 100%; }
  .or-settle{ flex:1 1 100%; margin-top:8px; }
  .ovh-row{ grid-template-columns:1fr 110px 28px; }
  .ovh-add{ grid-template-columns:1fr; }
}
@media (prefers-reduced-motion:reduce){
  .cost-item.just-added{ animation:none; box-shadow:inset 0 0 0 2px var(--brand-yellow); }
  .pbar{ transition:none; }
}

/* --------------------------------------------------------------------------
   THE SAVE BUTTON ON A JOB'S DATES (2026-08-05)
   Jay asked for a Save button on anything he types into: "otherwise it feels
   like nothing's gone". The state label beside it is the other half of that,
   because a disabled button on its own does not say whether the last thing
   typed reached the server.
   -------------------------------------------------------------------------- */
.jhd-save{ display:flex; align-items:center; gap:10px; align-self:flex-end; padding-bottom:2px; }
.jhd-state{ font-size:12px; color:var(--text-muted); white-space:nowrap; }
/* On the dark header, where the light-mode semantic colours do not carry:
   --overdue #DC3545 measures 3.92:1 there and --won-text #157C3A 3.40:1. These
   two are the pair the figures row already uses on-dark. */
.jhd-state.unsaved{ color:#FF9AA3; font-weight:600; }      /* 8.79:1 */
.jhd-state.just-saved{ color:#7DDBA0; font-weight:600; }   /* 10.57:1 */
.jhd-save .btn[disabled]{ opacity:.45; cursor:default; }
@media (max-width:760px){
  .jhd-save{ align-self:stretch; justify-content:space-between; padding-top:4px; }
}

/* --------------------------------------------------------------------------
   IS THIS A VAT JOB (2026-08-06)
   Jay: "this job here is a VAT job ... but it doesn't say here that it is."
   Same shape as the dates row directly above it, because it answers the same
   kind of question: a fact about the job, readable and changeable where it is
   read. The pill in the header is what makes it visible without scrolling.
   -------------------------------------------------------------------------- */
/* ---------------------- ADDITIONAL WORKS ON A JOB ------------------------
   "This is the main job, and then we've got a list of additional works, and
   then we can track it." Laid out as he described it: the main job on the top
   line, the extras under it, the new total in bold, then one row to add
   another.

   It lives ON the job header, which is dark chrome with inverted text, so it
   is styled like its siblings (dates, VAT, progress) rather than as a white
   card dropped on top. The first attempt gave it a light background and left
   the text inheriting --text-invert: white on #f7f8f8, contrast 1.06, which
   is invisible. Both vision providers caught it and measuring confirmed it.
   Every colour below is therefore an explicit value ON DARK. */
/* 13 August: this block moved OFF the dark job header and onto the light panel
   as its own `.card`, beside the cost card (Jay: "it's right at the top of the
   page ... needs to be moved down"). Every colour below was an on-dark
   rgba(255,255,255,...) value, which on #f7f8f8 is the contrast-1.06 invisible
   text this same component already shipped once. They are all re-stated for a
   light background here, and the on-dark set is gone rather than left behind to
   be inherited by accident. */
.jx-card .jx-lines{ display:flex; flex-direction:column; }
.jx-card .jx-row{
  display:grid; grid-template-columns:1fr auto 104px 26px; align-items:center; gap:10px;
  padding:8px 0; border-bottom:1px solid var(--border);
  font-size:13.5px; color:var(--text);
}
.jx-card .jx-row:last-child{ border-bottom:0; }
/* The main job is the term of the sum, not an item in the list, so it is
   quieter than the extras that follow it. */
.jx-card .jx-base .jx-desc,.jx-card .jx-base .jx-amt{ color:var(--text-muted); }
.jx-card .jx-desc{ min-width:0; overflow-wrap:anywhere; }
.jx-card .jx-date{ font-size:11.5px; color:var(--text-muted); white-space:nowrap; }
.jx-card .jx-amt{ text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }
.jx-card .jx-del{
  background:none; border:0; cursor:pointer; font-size:17px; line-height:1;
  color:var(--text-muted); padding:2px 4px; border-radius:5px;
}
.jx-card .jx-del:hover{ background:rgba(0,0,0,.07); color:var(--text); }

.jx-card .jx-total{
  display:flex; justify-content:space-between; align-items:baseline;
  margin-top:10px; padding-top:10px; border-top:2px solid var(--border-strong);
  font-size:13px; color:var(--text-muted);
}
/* Near-black, NOT brand yellow: yellow on white measures about 1.5:1, which is
   the same fault the report's logo was fixed for on 13 August. */
.jx-card .jx-total strong{ font-size:18px; color:var(--text); font-variant-numeric:tabular-nums; }
.jx-card .jx-empty{ margin-top:6px; font-size:12.5px; color:var(--text-muted); }

.jx-card .jx-add{ display:flex; gap:8px; margin-top:12px; flex-wrap:wrap; }
.jx-add input{
  padding:8px 10px; border:1px solid var(--border); border-radius:7px;
  font-family:var(--font); font-size:13.5px; background:var(--surface); color:var(--text);
}
.jx-add input:first-child{ flex:1 1 190px; min-width:0; }
.jx-add input[type="number"]{ width:110px; }
.jx-add input[type="date"]{ width:150px; }

@media (max-width:820px){
  /* At 390px a description column and a money column cannot both be generous,
     and the money is the half he is checking, so the date drops out.
     Scoped to .jx-card like the rules above it: the base rules gained that
     prefix when the block moved to the light panel, and an unprefixed override
     here would lose on specificity (0,1,0 against 0,2,0) and silently leave
     the phone on the four-column desktop grid. */
  .jx-card .jx-row{ grid-template-columns:1fr 104px 26px; }
  .jx-card .jx-date{ display:none; }
  .jx-card .jx-add input:first-child{ flex:1 1 100%; }
  .jx-card .jx-add input[type="number"],.jx-card .jx-add input[type="date"]{ flex:1 1 calc(50% - 4px); width:auto; }
  .jx-card .jx-add .btn{ flex:1 1 100%; }
}

.jh-vat{
  display:flex; align-items:baseline; gap:16px; flex-wrap:wrap;
  border-top:1px solid var(--border); margin-top:12px; padding-top:12px;
}
.jhv-field{ display:flex; flex-direction:column; gap:4px; }
.jhv-field > span{
  font-size:10.5px; text-transform:uppercase; letter-spacing:.06em;
  font-weight:700; color:var(--text-muted);
}
.jhv-field select{
  border:1px solid var(--border-strong); border-radius:8px; padding:7px 10px;
  font:inherit; font-size:13.5px; background:var(--surface); color:var(--text);
  min-height:38px;   /* a real tap target on a phone */
}
.jhv-field select:focus{ outline:2px solid var(--brand-yellow); outline-offset:1px; border-color:var(--brand-yellow); }
.jhv-sense{ font-size:13px; color:var(--text); flex:1 1 240px; }
.jhv-sense strong{ font-weight:700; }
.jhv-note{ font-size:12px; color:var(--text-muted); flex:1 1 100%; }
/* AT COURT (round 29). Its own full-width row below the VAT line, separated by
   a rule, because it is a different question from the VAT rate and reading as
   part of it would be worse than a second box. Scoped to .jhv-dispute rather
   than styling a bare `input[type=checkbox]`: a bare class or element rule
   inside a component is a global, which is how .ci-del leaked into every grid
   that reused that button (round 28). */
.jhv-dispute{
  flex:1 1 100%; margin-top:10px; padding-top:10px;
  border-top:1px solid var(--border); display:flex; flex-direction:column; gap:5px;
}
.jhv-dispute .jhv-check{ display:flex; align-items:center; gap:9px; cursor:pointer; }
.jhv-dispute .jhv-check input{ width:18px; height:18px; accent-color:var(--danger, #A12828); flex:0 0 auto; }
.jhv-dispute .jhv-check span{ font-size:13.5px; font-weight:600; color:var(--text); }
/* Overrides the flex-basis inherited from the .jhv-sense rule above: inside
   this column the basis would be read as a minimum HEIGHT, which is the same
   trap the media query below already documents for the VAT sentence. */
.jhv-dispute .jhv-sense{ flex:0 0 auto; font-size:12.5px; color:var(--text-muted); }
.pill-vat{ background:var(--brand-yellow); color:#111827; }
.pill-novat{ background:var(--info-bg); color:#4B5563; }
@media (max-width:760px){
  .jh-vat{ flex-direction:column; align-items:stretch; gap:8px; }
  .jhv-field select{ width:100%; }
  /* flex:1 1 240px on .jhv-sense means WIDTH on the desktop row layout above,
     but the SAME rule means HEIGHT once .jh-vat switches to a column here, so
     the VAT sentence sat in a 240px-tall box far taller than its own text and
     left a blank gap before the note underneath it. Column layout has no row
     of siblings to size against, so drop the basis back to content height. */
  .jhv-sense{ flex:1 1 auto; }
}

/* ============ iOS ZOOM: THIS FILE'S OWN CONTROLS (19 August 2026) ============
   app.css sets every form control to 16px at phone widths, because anything
   under Apple's 16px threshold makes Safari magnify the whole page on focus and
   never zoom back. These six are styled here at a higher specificity than a bare
   `input`, so they kept their 13.5px and stayed the last six zoom triggers in
   the app: the job's start and finish dates, its VAT rate, and the three boxes
   for adding an additional work. Measured, not guessed:
   artifacts/qa/audit-sideways-2026-08-19.mjs reported "sub-16px 6" on the job
   panel after the app.css rule landed, and 0 on every other screen.
   ========================================================================= */
@media (max-width:820px){
  .jhd-field input,
  .jhv-field select,
  .jx-add input,
  .ovh-row input,
  .ovh-add input,
  .ovm-row input,
  .fd-row input{ font-size:16px; }
}

/* ==========================================================================
   ROUND 28 (9 September 2026). Jay's 7 September asks.
   Everything here reuses the shapes already in this file: .ovm-row is .ovh-row
   with a month on the left, .fd-row is the same grid with a job on it. A new
   look for a new screen is how an app stops feeling like one app.
   ========================================================================== */

/* ---- overheads, month by month ---- */
.ovm-list{ display:flex; flex-direction:column; gap:1px; background:var(--border); border-radius:8px; overflow:hidden; }
.ovm-row{ display:grid; grid-template-columns:1fr 130px 30px; gap:10px; align-items:center;
          background:var(--surface); padding:8px 12px; }
/* A month with no figure of its own is charged today's list. It is not an
   error and must not look like one, but it must not look settled either: the
   left border is the whole signal, and the word underneath says which it is. */
.ovm-row.assumed{ border-left:3px solid var(--border-strong); }
.ovm-row.stated { border-left:3px solid var(--won); }
.ovm-row.focus  { background:var(--yellow-soft); }
.ovm-row .ci-del{ grid-area:auto; justify-self:end; }
.ovm-when{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.ovm-when strong{ font-size:13.5px; }
.ovm-src{ font-size:11px; color:var(--text-muted); }
.ovm-row.stated .ovm-src{ color:var(--won-text); }
/* The amount is a BORDERED field, unlike the ovh-row it is modelled on. That
   row is a list of things you already typed; this one is mostly boxes waiting
   to be filled in, and a border-on-hover control is invisible on a phone, which
   has no hover. Both vision providers asked "where do I set a month" while
   looking directly at the input, which is the clearest possible evidence that
   an editable field did not read as one. */
.ovm-amt-wrap{
  display:flex; align-items:center; gap:2px; border:1px solid var(--border-strong);
  border-radius:6px; background:var(--surface); padding:0 6px 0 8px; min-width:0;
}
.ovm-amt-wrap:focus-within{ outline:2px solid var(--brand-yellow); outline-offset:1px;
                            border-color:var(--brand-yellow); }
.ovm-cur{ color:var(--text-muted); font-size:13px; font-weight:600; }
.ovm-row input{
  border:0; border-radius:0; padding:6px 0; font:inherit; font-size:13.5px;
  background:transparent; color:var(--text); min-width:0; width:100%; min-height:34px;
  text-align:right; font-variant-numeric:tabular-nums; font-weight:600;
}
.ovm-row input:focus{ outline:none; }

/* ---- the finish-date back-fill ---- */
.fd-list{ display:flex; flex-direction:column; gap:1px; background:var(--border); border-radius:8px; overflow:hidden; }
.fd-row{ display:grid; grid-template-columns:1fr 150px auto; gap:10px; align-items:center;
         background:var(--surface); padding:8px 12px; }
.fd-who{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.fd-who strong{ font-size:13.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.fd-meta{ font-size:11px; color:var(--text-muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.fd-row input{ border:1px solid var(--border-strong); border-radius:6px; padding:6px 8px;
               font:inherit; font-size:13.5px; min-height:34px; min-width:0; }
.fd-row input:focus{ outline:2px solid var(--brand-yellow); outline-offset:1px; border-color:var(--brand-yellow); }

/* ---- business costs, with a way back out ---- */
.bc-list{ display:flex; flex-direction:column; gap:1px; background:var(--border); border-radius:8px; overflow:hidden; }
.bc-row{ display:grid; grid-template-columns:1fr auto 30px; gap:10px; align-items:center;
         background:var(--surface); padding:9px 12px; }
.bc-row .ci-del{ grid-area:auto; justify-self:end; }
.bc-what{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.bc-what strong{ font-size:13.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.bc-meta{ font-size:11px; color:var(--text-muted); }
.bc-amt{ font-variant-numeric:tabular-nums; font-weight:700; font-size:13.5px; white-space:nowrap; }

/* ---- what the banked figure is made of ---- */
.bp-sum{ border:1px solid var(--border); border-radius:8px; overflow:hidden; margin-bottom:12px; }
.bp-line{ display:flex; justify-content:space-between; align-items:baseline; gap:12px;
          padding:9px 12px; font-size:13.5px; border-bottom:1px solid var(--border); }
.bp-line:last-child{ border-bottom:0; }
.bp-line strong{ font-variant-numeric:tabular-nums; }
.bp-line.bp-total{ background:var(--surface-warm); font-weight:700; }
.bp-line.bp-total strong{ font-size:17px; }
.bp-owed{ background:var(--due-bg); border:1px solid var(--due); border-radius:8px;
          padding:10px 12px; font-size:12.5px; line-height:1.55; margin-bottom:14px; }

/* ---- the one tap that says a job is finished ---- */
.jhd-finished{ display:flex; flex-direction:column; gap:4px; }
.jhd-finished > span{ font-size:11px; text-transform:uppercase; letter-spacing:.06em;
                      color:var(--text-muted); font-weight:600; }

@media (max-width:820px){
  .ovm-row{ grid-template-columns:1fr 110px 28px; }
  .fd-row { grid-template-columns:1fr 130px; }
  .fd-row button{ grid-column:1 / -1; }
}
