  /* Owner note (Stage 7 revision 18): "do a website-wide audit of font
     size and set some consistent font size rules and apply them
     consistently throughout" -- audited every font-size in the
     customer-facing areas (public enquiry flow + customer portal; the
     separate Staff/Admin mockup was never part of this owner-review
     process and is left untouched). Almost every size already fell into
     one of these tiers already -- the real bug this surfaced was that
     nothing set an explicit sitewide default, so a handful of un-classed
     elements (e.g. Quote detail's "1./2." item headings) silently fell
     back to the BROWSER's own 16px default instead of the site's own
     13.5px body tier, standing out next to everything around them. Fixed
     at the root by giving body its own explicit default (below) instead
     of patching each stray element individually.
       Hero heading (homepage only): 28px
       Page heading (h1.pageH): 24px
       Section heading (h2.sectionH, card sub-heading): 17px
       Hero tagline (.heroCopy, homepage only): 15px
       Body copy / default (.help, table text, option/tab buttons, kv
         facts, and now the sitewide default -- see body{} below): 13.5px
       Field labels, legends, small links: 13px
       Form field values, main buttons (.primaryCta/.secondaryCta): 14px
       Compact buttons (.btnSm), field errors: 12.5px
       Badges/pills: 11.5px
       Table column headers, route captions, mobile row labels: 11px
     A large "stat" figure (My Yard Card's Account balance/Paid to date,
     an Invoice's own total) is deliberately styled bigger than body copy
     to draw the eye -- that's an intentional exception to the scale
     above, not a drift from it. */
  :root {
    --yc-ink: #1a1a1a;
    --yc-muted: #5b5b52;
    --yc-border: #e4e1d6;
    --yc-green: #2e8b3d;
    --yc-green-btn: #2a7f38;   /* R16 (Q18): filled green buttons with white text (~5.0:1) */
    --yc-green-dark: #1f6b2b;
    --yc-amber-bg: #fff4d6;
    --yc-amber-border: #f0d989;
    --yc-amber-ink: #7a5c00;
    --yc-red-bg: #fbe7e7;
    --yc-red-border: #e9b7b7;
    --yc-red-ink: #7a2020;
    --yc-blue-bg: #e8f0fb;
    --yc-blue-border: #bcd4f0;
    --yc-blue-ink: #1f4c7a;
    --yc-subtle-bg: #faf9f4;
    --font-display: "Arial Narrow", "Franklin Gothic Medium", Impact, sans-serif;
    --font-body: "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, "SF Mono", Consolas, monospace;
  }
  * { box-sizing: border-box; }
  html, body { margin: 0; padding: 0; }
  body {
    font-family: var(--font-body);
    font-size: 13.5px;
    color: var(--yc-ink);
    background: #f4f3ee;
    -webkit-font-smoothing: antialiased;
  }
  a { color: inherit; text-decoration: none; }
  button { font-family: inherit; }

  /* ---------- Owner review chrome ---------- */
  #chrome-sticky { position: sticky; top: 0; z-index: 1000; }
  #owner-banner {
    background: #7a1f1f; color: #fff; padding: 6px 14px; font-size: 12px; line-height: 1.4;
    display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px;
  }
  #owner-banner strong { letter-spacing: 0.02em; }
  #owner-banner span.detail { opacity: 0.9; }

  #mode-switcher {
    background: #2b2b26; color: #f4f3ee; display: flex; align-items: center; gap: 8px;
    padding: 7px 14px; flex-wrap: wrap; font-size: 13px;
  }
  #mode-switcher .msLabel { opacity: 0.7; margin-right: 4px; }
  #mode-switcher button {
    font-size: 13px; padding: 6px 12px; border-radius: 999px;
    border: 1px solid #55554e; background: transparent; color: #f4f3ee; cursor: pointer;
  }
  #mode-switcher button.active { background: var(--yc-green-btn); border-color: var(--yc-green-btn); color: #fff; font-weight: 600; }
  #mode-switcher .homeBtn { margin-left: auto; background: #454540; }
  #mode-switcher .notesBtn { background: #6b4f1d; border-color: #6b4f1d; }

  #viewport-bar {
    background: #1e3a2e; color: #dff0e6; display: flex; align-items: center; gap: 8px;
    padding: 6px 14px; flex-wrap: wrap; font-size: 12px;
  }
  #viewport-bar .msLabel { opacity: 0.85; margin-right: 2px; font-weight: 700; }
  #viewport-bar button {
    font-size: 11.5px; padding: 5px 10px; border-radius: 999px;
    border: 1px solid #4b7a63; background: transparent; color: #dff0e6; cursor: pointer;
  }
  #viewport-bar button.active { background: var(--yc-green-btn); border-color: var(--yc-green-btn); color: #fff; font-weight: 700; }

  #route-bar {
    background: #3a3327; color: #f4e9c9; display: flex; align-items: center; gap: 10px;
    padding: 6px 14px; flex-wrap: wrap; font-size: 11.5px; font-family: var(--font-mono);
    border-bottom: 2px dashed #f2c14e;
  }
  #route-bar .rbLabel { opacity: 0.85; font-weight: 700; font-family: var(--font-body); }
  #route-bar .backBtn {
    font-family: var(--font-body); font-size: 11.5px; padding: 4px 10px; border-radius: 999px;
    border: 1px solid #cdae5a; background: transparent; color: #f4e9c9; cursor: pointer; margin-left: auto;
  }
  #route-bar .backBtn:disabled { opacity: 0.35; cursor: default; }
  #route-bar .portalContextNote { flex-basis: 100%; font-family: var(--font-body); color: #fff3c4; overflow-wrap: anywhere; }   /* R19 (F9) */

  #app-shell { min-height: 60vh; }
  #app-shell.vp-mobile { max-width: 390px; margin: 0 auto; border-left: 1px solid var(--yc-border); border-right: 1px solid var(--yc-border); }

  /* ---------- Public site shell ---------- */
  .pub-header { display: flex; align-items: center; justify-content: center; padding: 5px 20px 5px; background: #fff; border-bottom: 1px solid var(--yc-border); }
  .pub-header .brand { display: inline-flex; line-height: 0; cursor: pointer; }   /* R19 Part B (OQ-5): the vector logo */
  svg.ycLogo { display: block; height: 44px; width: calc(44px * 700 / 190); }
  .pub-main { max-width: 760px; margin: 0 auto; padding: 24px 20px 40px; }
  #app-shell.vp-mobile .pub-main { padding: 18px 14px 30px; }
  .pub-footer { border-top: 1px solid var(--yc-border); background: #fff; padding: 16px 20px 28px; margin-top: 30px; }
  .pub-footer ul { list-style: none; display: flex; gap: 16px; flex-wrap: wrap; justify-content: center; margin: 0; padding: 0; font-size: 13px; }
  .pub-footer a { color: var(--yc-green-dark); cursor: pointer; }
  .pubFooterHelp { margin: 10px 0 0; text-align: center; font-size: 12.5px; color: var(--yc-muted); }   /* R17 (Q10) */
  .navCurrent { color: var(--yc-ink); font-weight: 700; }

  .hero { text-align: center; padding: 8px 0 26px; }
  .heroHeading { font-family: var(--font-display); font-size: 28px; margin: 0 0 8px; }
  /* Owner note (Stage 7 revision 15): "this text is awkwardly on two
     rows" -- an unconstrained centred paragraph can break at an
     arbitrary word; giving it a sensible max-width lets it wrap at a
     more natural, centred point instead. */
  .heroCopy { font-size: 15px; color: var(--yc-muted); margin: 0 auto 16px; max-width: 460px; }
  /* Owner note (Correction Batch 01B §22): min-height:44px + the flex
     centering it needs is the one shared touch-target rule for every
     .primaryCta/.secondaryCta on the site (the public enquiry flow's own
     primary/secondary action, used repeatedly across every public
     screen) -- previously unset, so real padding/font-size alone left
     these well under the 44px minimum this codebase already established
     elsewhere (.btnRow.evenButtons > .btnSm, §14). */
  .primaryCta { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; background: var(--yc-green-btn); color: #fff; font-weight: 700; padding: 11px 20px; border-radius: 4px; cursor: pointer; border: none; font-size: 14px; }
  .primaryCta:hover { background: var(--yc-green-dark); }
  .secondaryCta { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; background: #fff; color: var(--yc-green-dark); font-weight: 600; padding: 10px 18px; border-radius: 4px; cursor: pointer; border: 1px solid var(--yc-green-dark); font-size: 14px; }
  /* Owner note (Stage 7 revision 17): "+Add another contact button needs
     hover treatment" -- same light-green-tint hover as every other
     selection-style control in the site. */
  .secondaryCta:hover { background: #e6f3e8; }

  /* Owner feedback (01D v2 audit, B11): the site-wide button rule requires
     a consistent hover/focus/disabled treatment across every rectangular
     interactive action, not just hover. One shared focus-visible ring and
     one shared disabled treatment, applied here to every button-like class
     rather than duplicated per class, so no control is left out going
     forward. Status pills (.badge) are intentionally excluded -- they are
     never focusable/interactive. */
  .primaryCta:focus-visible, .secondaryCta:focus-visible, .btnSm:focus-visible,
  .optionBtn:focus-visible, .tabBtn:focus-visible, .editLink:focus-visible,
  #route-bar .backBtn:focus-visible, .cardBoxLink:focus-visible,
  a[data-nav]:focus-visible, a[data-staffback]:focus-visible {
    outline: 2px solid var(--yc-green-dark); outline-offset: 2px;
  }
  .primaryCta:disabled, .secondaryCta:disabled, .btnSm:disabled, .tabBtn:disabled, .editLink[aria-disabled="true"] {
    opacity: 0.4; cursor: not-allowed;
  }

  .services2 { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; margin: 18px 0 26px; }
  #app-shell.vp-mobile .services2 { flex-direction: column; }
  .serviceCard2 { flex: 1 1 240px; border: 1px solid var(--yc-border); border-radius: 8px; padding: 18px; background: #fff; display: flex; flex-direction: column; }
  .serviceCard2 h2 { font-family: var(--font-display); font-size: 17px; margin: 0 0 8px; }
  .serviceCard2 p { font-size: 13.5px; color: var(--yc-muted); margin: 0 0 12px; }
  /* Owner note (Stage 7 revision 14): this rule targeted the old
     .secondaryCta class -- renaming the homepage buttons to .primaryCta
     (to make them green) silently broke it, so the two buttons no longer
     lined up on the same row once one card grew taller than the other.
     Targeting both classes keeps this working regardless of which CTA
     style a service card uses. */
  .serviceCard2 .primaryCta, .serviceCard2 .secondaryCta { margin-top: auto; align-self: flex-start; }
  /* Owner note (Stage 7 revision 14): "start a new mowing enquiry" button
     text was splitting over two rows at some widths -- forcing it to stay
     on one line (rather than truncating it) makes the button itself grow
     wider instead, which is what was actually asked for. */
  .serviceCard2 .primaryCta, .serviceCard2 .secondaryCta { white-space: nowrap; }
  /* Owner note (Stage 7 revision 14): "could this be set out in a fun
     website progression instead of a paragraph eg 1, 2, 3?" -- applies
     only to Review & Send's "What happens next" (the Sent screen has its
     own separate, shorter "What happens next" text, left as plain
     paragraphs since it wasn't part of this request). */
  .stepsList { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
  .stepsList li { display: flex; align-items: flex-start; gap: 12px; }
  .stepsList .stepNum { flex: 0 0 auto; width: 28px; height: 28px; border-radius: 50%; background: var(--yc-green-dark); color: #fff; font-weight: 700; font-size: 14px; display: flex; align-items: center; justify-content: center; }
  .stepsList li > div { padding-top: 3px; color: var(--yc-ink); font-size: 16px; line-height: 1.5; }

  h1.pageH { font-family: var(--font-display); font-size: 24px; margin: 0 0 6px; }
  h2.sectionH { font-family: var(--font-display); font-size: 17px; margin: 22px 0 8px; }
  p.help { font-size: 13.5px; color: var(--yc-muted); margin: 0 0 14px; }

  .fieldRow { margin-bottom: 16px; }
  .fieldRow label { display: block; font-size: 13px; font-weight: 700; margin-bottom: 5px; }
  .fieldRow input[type=text], .fieldRow input[type=tel], .fieldRow input[type=email], .fieldRow input[type=number], .fieldRow input[type=time], .fieldRow input[type=date], .fieldRow input[type=password], .fieldRow input[type=search], .fieldRow input[type=url], .fieldRow select, .fieldRow textarea {
    width: 100%; padding: 9px 10px; border: 1px solid #c9c6b8; border-radius: 5px; font-size: 14px; font-family: inherit;
  }
  /* Owner note (Stage 7 revision 18): "should all text fields get hover
     treatment across the website? including drop-down boxes?" -- the
     standard convention for data-entry fields is a subtle border darken
     on hover (distinct from the stronger :focus state a browser already
     draws when a field is active) rather than a background fill, which
     would fight legibility while typing. Applied to every text input,
     select and textarea site-wide via .fieldRow, the shared wrapper
     class already used everywhere a field appears. */
  .fieldRow input[type=text]:hover:not(:disabled), .fieldRow input[type=tel]:hover:not(:disabled), .fieldRow input[type=email]:hover:not(:disabled), .fieldRow input[type=number]:hover:not(:disabled), .fieldRow input[type=time]:hover:not(:disabled), .fieldRow input[type=date]:hover:not(:disabled), .fieldRow input[type=password]:hover:not(:disabled), .fieldRow input[type=search]:hover:not(:disabled), .fieldRow input[type=url]:hover:not(:disabled), .fieldRow select:hover:not(:disabled), .fieldRow textarea:hover:not(:disabled) {
    border-color: var(--yc-green-dark);
  }
  /* Owner note (Stage 7 revision 11): the browser's own dropdown arrow on
     <select> sat hard against the right edge -- replaced with a custom
     arrow drawn further in from the edge, at a consistent spot everywhere
     a select is used. */
  .fieldRow select {
    appearance: none; -webkit-appearance: none; -moz-appearance: none;
    padding-right: 34px;
    background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none' stroke='%235b5b52' stroke-width='1.7'%3E%3Cpath d='M1 1.5l5 5 5-5'/%3E%3C/svg%3E") no-repeat right 14px center;
  }
  /* Owner note (Stage 7 revision 11): a plain type-ahead text field
     (address lookups) isn't a real dropdown -- Chromium was drawing its
     "open list" arrow on any input with a list="" attribute, which read
     as a misleading extra control. Hidden everywhere that pattern is used. */
  input[list]::-webkit-calendar-picker-indicator { display: none !important; }
  .optionRow { display: flex; gap: 10px; flex-wrap: wrap; }
  /* Owner note (Stage 7 revision 17): "these buttons still look bland
     when not hovered or selected -- outline green like the buttons under
     My Yard Card screen" -- the bottom-nav buttons on My Yard Card
     (.btnRow.evenButtons > .btnSm) carry a permanent green border, not
     just on hover; .optionBtn now matches that at-rest treatment instead
     of a plain grey border. */
  .optionBtn {
    min-height: 44px; display: inline-flex; align-items: center; padding: 9px 14px; border-radius: 5px; border: 1px solid var(--yc-green-dark); background: #fff; cursor: pointer; font-size: 13.5px;
  }
  .optionBtn[aria-pressed=true] { background: var(--yc-green-btn); border-color: var(--yc-green-btn); color: #fff; font-weight: 700; }
  /* Owner note (Stage 7 revision 15): "hovering cursor over button should
     show outline of button in a way that feels like a half selection and
     clicking or pushing should turn button green -- apply throughout
     website" -- clicking already turns it solid green (aria-pressed rule
     above); this adds the missing hover step in between: an outline plus
     a light green tint, short of the full selected fill. */
  .optionBtn:hover:not(:disabled):not([aria-pressed=true]) { border-color: var(--yc-green-dark); background: #e6f3e8; }
  .optionBtn:disabled { opacity: 0.4; cursor: not-allowed; background: #f4f3ee; }
  .radioRow { display: flex; flex-direction: column; gap: 8px; }
  .radioRow label { display: flex; gap: 8px; align-items: flex-start; font-size: 13.5px; font-weight: 400; }
  /* Owner note (Stage 7 revision 18): "should all selectable circles and
     tick boxes get hover treatment -- don't just agree with me, what do
     websites usually do here?" -- the real convention is to highlight
     the whole clickable row/label, not the tiny native circle or box
     itself (too small a target to hover-style well, and its native
     appearance varies by browser/OS). Applied generically to every label
     wrapping a checkbox or radio input, not just the ones a specific
     screen happened to be reviewed on. */
  label:has(> input[type="checkbox"]), label:has(> input[type="radio"]) { cursor: pointer; padding: 3px 6px; border-radius: 5px; }
  label:has(> input[type="checkbox"]):hover, label:has(> input[type="radio"]):hover { background: #e6f3e8; }
  .fieldError { color: var(--yc-red-ink); font-size: 12.5px; font-weight: 600; margin: 5px 0 0; }
  .optionRowSpaced { gap: 14px; margin-bottom: 10px; }
  .timingField { margin-top: 18px; }

  .dateInputWrap { position: relative; }
  .dateInputWrap input { cursor: pointer; background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%235b5b52' stroke-width='1.5'%3E%3Crect x='1.5' y='2.5' width='13' height='12' rx='1.5'/%3E%3Cpath d='M1.5 6h13M4.5 1v3M11.5 1v3'/%3E%3C/svg%3E") no-repeat right 10px center; padding-right: 34px; }
  .calPopup {
    position: absolute; z-index: 40; top: calc(100% + 6px); left: 0; width: 280px;
    background: #fff; border: 1px solid var(--yc-border); border-radius: 8px; box-shadow: 0 6px 20px rgba(0,0,0,0.14); padding: 12px;
  }
  .calHead { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; font-size: 13px; font-weight: 700; }
  .calNavBtn { border: 1px solid #c9c6b8; background: #fff; border-radius: 5px; width: 26px; height: 26px; cursor: pointer; font-size: 13px; line-height: 1; }
  .calGrid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
  .calDow { font-size: 10.5px; color: var(--yc-muted); text-align: center; padding: 2px 0; text-transform: uppercase; letter-spacing: 0.02em; }
  .calDay { border: none; background: transparent; border-radius: 5px; font-size: 12.5px; padding: 6px 0; cursor: pointer; }
  .calDay:hover:not(.disabled):not(.pad) { background: var(--yc-subtle-bg); }
  .calDay.pad { visibility: hidden; }
  .calDay.disabled { color: #c9c6b8; cursor: not-allowed; }
  .calDay.today { box-shadow: inset 0 0 0 1px var(--yc-green); }
  .calDay.selected { background: var(--yc-green-btn); color: #fff; font-weight: 700; }

  .navBtns { display: flex; justify-content: space-between; margin-top: 22px; gap: 10px; }

  /* ---------- Staff/Customer app shell ---------- */
  .app-header { background: #1e1e1a; color: #f4f3ee; padding: 10px 16px; display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
  .app-header .brand { display: inline-flex; align-items: center; line-height: 0; background: #fff; border-radius: 6px; padding: 3px 7px; cursor: pointer; }   /* R19 Part B (OQ-5): the vector logo on a white tab */
  .app-header .brand svg.ycLogo { height: 28px; width: calc(28px * 700 / 190); }
  .app-header .roleTag { font-size: 11px; background: #383830; padding: 3px 9px; border-radius: 999px; letter-spacing: 0.04em; }
  .app-header .signout { margin-left: auto; font-size: 12px; opacity: 0.75; }
  .breadcrumb { padding: 10px 16px; font-size: 12.5px; color: var(--yc-muted); background: var(--yc-subtle-bg); border-bottom: 1px solid var(--yc-border); display: flex; gap: 6px; flex-wrap: wrap; }
  .breadcrumb a { color: var(--yc-green-dark); cursor: pointer; }
  .breadcrumb .sep { opacity: 0.5; }
  .app-main { max-width: 980px; margin: 0 auto; padding: 22px 20px 46px; }
  #app-shell.vp-mobile .app-main { padding: 16px 14px 34px; max-width: 100%; }

  .linkList { display: flex; flex-direction: column; gap: 2px; }
  .linkList a.navLink { padding: 10px 4px; color: var(--yc-green-dark); font-weight: 600; cursor: pointer; border-bottom: 1px solid var(--yc-border); font-size: 14.5px; }
  .attnCount { color: var(--yc-amber-ink); font-weight: 700; }

  table.ycTable { width: 100%; border-collapse: collapse; font-size: 13.5px; margin: 8px 0 4px; background: #fff; }
  table.ycTable th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--yc-muted); border-bottom: 1px solid var(--yc-border); padding: 8px 10px; }
  /* Owner note (Stage 7 revision 16): "Change service button sits low --
     can we align the horizontal centre of the row?" / "same issue in Job
     history -- paid/overdue badge not centred with the text in its row"
     -- top-alignment made any pill/badge/button cell (whose own padding
     makes it visually taller than plain text) look like it was hanging
     low against the plain-text cells beside it. Middle-alignment gives
     every cell in a row -- text, badge or button -- a shared vertical
     centre. */
  table.ycTable td { padding: 10px; border-bottom: 1px solid var(--yc-border); vertical-align: middle; }
  table.ycTable tr:hover td { background: var(--yc-subtle-bg); }
  #app-shell.vp-mobile table.ycTable, #app-shell.vp-mobile table.ycTable thead, #app-shell.vp-mobile table.ycTable tbody, #app-shell.vp-mobile table.ycTable tr { display: block; }
  #app-shell.vp-mobile table.ycTable thead { display: none; }
  #app-shell.vp-mobile table.ycTable tr { border: 1px solid var(--yc-border); border-radius: 6px; margin-bottom: 10px; padding: 4px 0; background: #fff; }
  #app-shell.vp-mobile table.ycTable td { display: flex; justify-content: space-between; align-items: center; gap: 10px; border-bottom: none; padding: 6px 10px; min-height: 20px; }
  #app-shell.vp-mobile table.ycTable td::before { content: attr(data-label); font-weight: 700; color: var(--yc-muted); font-size: 11px; text-transform: uppercase; }
  #app-shell.vp-mobile table.ycTable td:not(:is([data-label="Property"], [data-label="Address"], [data-label="Mowing address"], [data-label="Detail"])) > :last-child { margin-left: auto; text-align: right; }   /* R14 (notes 75): same rule in the phone view */

  /* Owner note (Stage 7 revision 19): "clicking/pushing anywhere in the
     row becomes a selection" -- the whole <tr> is a nav target now (see
     the Job history markup), so it needs its own pointer cursor; the
     existing table.ycTable tr:hover rule already highlights the whole
     row on hover, which is what the owner said they liked. */
  .clickableRow { cursor: pointer; }
  .srDecline { margin-top: 20px; padding-top: 14px; border-top: 1px solid var(--yc-border); max-width: 520px; }
  .srDecline .subH { margin: 0 0 8px; }
  .srWorkflow .srWork { border-top: 1px solid var(--yc-border); padding-top: 14px; margin-top: 4px; }
  .srWorkflow .subH { margin: 0 0 8px; }
  .srAction { min-width: 200px; justify-content: center; text-align: center; display: inline-flex; }
  .srTargetFields { margin: 20px 0 12px; }
  .labelHint { font-weight: 400; color: var(--yc-muted); font-size: 13px; margin-left: 6px; }
  details.srPermRoute { margin-top: 16px; } details.srPermRoute summary { cursor: pointer; font-weight: 600; }
  @media (max-width: 600px) { .srAction { width: 100%; min-width: 0; } }
  #app-shell.vp-mobile .srAction { width: 100%; min-width: 0; }
  [data-sr-keep-booked] { margin: 10px 0 16px; }
  .rowAction { width: 112px; box-sizing: border-box; display: inline-flex; justify-content: center; white-space: nowrap; margin-top: 6px; position: relative; overflow: hidden; }
  @media (max-width: 1000px) { .rowAction { width: auto; min-height: 0; padding: 3px 6px; white-space: normal; font-size: 12px; } }
  @media (max-width: 600px) { .rowAction { width: 100%; padding: 8px 12px; font-size: 14px; } }
  #app-shell.vp-mobile .rowAction { width: 100%; padding: 8px 12px; font-size: 14px; }
  table.ycTable tr.jobRow:hover td { background: #f3f8f3; }
  /* Owner note (Staff Operations & Engagement Correction round, §E --
     site-wide interaction affordance rule): the reported back-arrow
     cursor defect -- a[data-nav] elements throughout this mockup
     (backLink()'s "&larr; Back", and every other plain in-content nav
     link) carry no real href attribute, since navigation is handled by
     the delegated data-nav click listener, not native link following.
     Without an href, a browser's own default <a> styling does NOT apply
     a pointer cursor -- it falls back to a generic/text cursor, which is
     exactly the "malformed/custom symbol cursor" the owner reported over
     the back arrow glyph. This single rule covers every such element
     site-wide in one place (never needs per-screen fixing), without
     touching any element that is genuinely not clickable: an element
     only ever carries data-nav because clicking it already navigates.
     Does not change .clickableRow/.cardBoxLink/etc. above, which already
     had their own explicit cursor:pointer rules. */
  a[data-nav] { cursor: pointer; }
  /* Owner note (§E): Staff Home/Jobs worklist/attention table rows are
     NOT marked .clickableRow -- only their own Service-name link
     actually navigates, so the existing table.ycTable tr:hover rule
     (which highlights the whole row) is scoped down here to rows that
     really are fully clickable, leaving an ordinary data table row's
     hover feedback to its one genuinely clickable cell only -- never
     implying the whole row is clickable when it isn't. */
  table.ycTable tr:not(.clickableRow):hover td { background: inherit; }
  /* Owner note (Stage 7 revision 11): My Yard Card's job history is a
     reverse-chronological ledger the owner explicitly wants scrollable
     rather than growing the page indefinitely. */
  .jobHistoryScroll { max-height: 320px; overflow-y: auto; border: 1px solid var(--yc-border); border-radius: 8px; }
  .jobHistoryScroll table.ycTable { margin: 0; }
  .jobHistoryScroll table.ycTable thead th { position: sticky; top: 0; background: #fff; }
  /* Owner note (Stage 7 revision 14): "job history jobs are distinct on
     desktop but run together on mobile" -- on mobile each visit already
     becomes a bordered card, but a white card on the scroll container's
     own white background made the borders (the only separation) hard to
     notice. Tinting the container and adding a shadow to each card makes
     the separation obvious without changing the desktop table at all. */
  #app-shell.vp-mobile .jobHistoryScroll { background: var(--yc-subtle-bg); padding: 8px; }
  #app-shell.vp-mobile .jobHistoryScroll table.ycTable tr { box-shadow: 0 1px 3px rgba(0,0,0,0.1); margin-bottom: 10px; }
  #app-shell.vp-mobile .jobHistoryScroll table.ycTable tr:last-child { margin-bottom: 0; }

  /* Owner note (Stage 7 revision 12): "address selection buttons need to
     be more obvious -- maybe highlight green like days of week" -- tabs
     now use the same selected/unselected visual language as .optionBtn
     (a solid green fill for the active choice) instead of a subtle
     underline, consistently everywhere .tabRow is used. */
  /* Owner note (Stage 7 revision 13): "address buttons are different
     widths on mobile and maybe desktop" -- each tab previously sized to
     its own text, so a short label sat noticeably narrower than a long
     address next to it. Equal flex-basis makes every tab in a row the
     same width, matching the evenButtons treatment used elsewhere. */
  .tabRow { display: flex; gap: 8px; flex-wrap: wrap; margin: 10px 0 14px; }
  /* Owner note (Stage 7 revision 17): same permanent green-outline
     treatment as .optionBtn above, for the same "selection button"
     consistency rule. */
  /* Owner review notes_32: "clicking between addresses changes spacing of
     account balance section underneath" -- root cause was that only the
     SELECTED tab was bold, so its text was wider than the same address
     unselected and could wrap onto an extra line purely from being
     selected, changing the tabRow's (and everything below it) height as
     you switched properties.
     Owner review notes_33: "I'm ok with layout of 10 address buttons but
     font is bold and quite confronting -- what would be better visually?"
     / "style of property buttons still needs to feel consistent with
     whole website" -- notes_32's own fix (bolding every tab all the
     time, selected or not) broke from the sitewide selection-button
     convention .optionBtn and .btnRow.evenButtons both use: outline +
     normal weight at rest, solid fill + bold only once selected. That's
     restored below -- but the reflow risk that made notes_32 bold
     everything in the first place is solved differently this time: each
     label is rendered TWICE, stacked in the same grid cell -- a real,
     visible one (bold only when selected, same as every other selection
     control) and an invisible always-bold "ghost" copy purely so the
     button's box is always already sized for its own bold variant. The
     visible label can then switch weight on selection without ever
     changing the button's size, since that size was never based on the
     visible label's own weight to begin with. */
  .tabBtn { flex: 1 1 160px; display: grid; text-align: center; padding: 8px 14px; border: 1px solid var(--yc-green-dark); border-radius: 5px; background: #fff; cursor: pointer; font-size: 13.5px; color: var(--yc-ink); }
  .tabBtn > .tabBtnGhost, .tabBtn > .tabBtnLabel { grid-area: 1 / 1; }
  .tabBtn > .tabBtnGhost { font-weight: 700; visibility: hidden; }
  .tabBtn[aria-selected=true] { background: var(--yc-green-btn); border-color: var(--yc-green-btn); color: #fff; }
  .tabBtn[aria-selected=true] > .tabBtnLabel { font-weight: 700; }
  /* Owner note (Stage 7 revision 16): "same hover treatment for address
     selection buttons" / "hover treatment needs to be across the website
     over selection buttons" -- the same light-green-tint hover .optionBtn
     already has, so every selection-style control in the site shares one
     rule: light green on hover, solid green once selected. */
  .tabBtn:hover:not([aria-selected=true]) { border-color: var(--yc-green-dark); background: #e6f3e8; }

  .badge { display: inline-block; padding: 3px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 700; }
  #screenRoot select { max-width: 100%; }   /* Round 14: a select never runs past its box (320px census) */
  /* Owner note (Stage 7 revision 15): "upcoming and pending statuses are
     not aligned with their heading above" -- a badge's own left
     padding+border (10px total) was pushing its readable text 10px to
     the right of the plain column-header text above it. Cancel that
     inset when a badge is the first thing in a table cell so the text
     itself lines up with its header, not just the pill's outer edge. */
  table.ycTable td > .badge:first-child { margin-left: 0; }   /* Round 14: pill edge = column heading edge */
  .badge.amber { background: var(--yc-amber-bg); border: 1px solid var(--yc-amber-border); color: var(--yc-amber-ink); }
  .badge.green { background: #e6f3e8; border: 1px solid #b9dcc0; color: var(--yc-green-dark); }
  .badge.red { background: var(--yc-red-bg); border: 1px solid var(--yc-red-border); color: var(--yc-red-ink); }
  .badge.blue { background: var(--yc-blue-bg); border: 1px solid var(--yc-blue-border); color: var(--yc-blue-ink); }
  .badge.grey { background: #ececec; border: 1px solid #d6d6d6; color: #555; }
  /* Owner note (Stage 7 revision 10): a margin-left nudge (revision 8)
     still read as "cramped and inconsistent" once text length varied --
     every field label/legend/heading now consistently pushes its
     trailing "Optional" badge all the way to the far right on its own
     line, rather than however-far-inline the preceding text happened to
     run. Safe on labels/legends with no badge too -- a lone text node in
     a flex container still renders identically. */
  .fieldRow > label:not(:has(input)), fieldset > legend, h2.sectionH {
    display: flex; justify-content: space-between; align-items: center; gap: 8px;
  }

  .cardBox { border: 1px solid var(--yc-border); border-radius: 8px; padding: 16px; background: #fff; margin-bottom: 14px; }
  /* Owner note (Post-01D v2 Independent Audit + Owner Feedback, B4): the
     one canonical spacing treatment for a repeating "entry in a feed/
     history/list" cardBox -- a correction-history item, an activity-log
     item, a Customer-facing update item, an alternate-contact card -- so
     every equivalent content block uses the SAME margin wherever it
     appears, Customer or Staff side, rather than a different hand-picked
     value per screen (8px/10px/12px/14px were all found in use for the
     structurally same kind of block). 10px matches the existing
     .optionRowSpaced rhythm already used elsewhere in this file -- the
     closest thing to an established "clean inset" value -- rather than
     inventing a new one. Applied via a class, never an inline style, so a
     screen can never silently reintroduce its own one-off margin.
     Padding is deliberately left alone (never overridden) -- cardBox's own
     16px is the one canonical padding for every entry alike. */
  .cardEntry { margin-bottom: 10px; }
  .cardBoxLink { cursor: pointer; transition: background-color 0.1s, border-color 0.1s; }
  .cardBoxLink:hover { background: var(--yc-subtle-bg); border-color: var(--yc-green-dark); }
  /* Owner note (§1, Customer/Public Owner Feedback round): each person
     under "People with access" previously ran together as a plain
     border-bottom-separated list -- easy to misread who's who. A
     distinct tinted sub-card per person (the existing --yc-subtle-bg
     token, not a new colour) makes each one scannable at a glance. */
  /* Owner note (Customer/Public Owner Review Batch 2, Change E): every
     person sub-card now uses one consistent very-light Yard Card green
     tint (the same #e6f3e8/#b9dcc0 pairing .badge.green and this app's
     own success banners already use, not a new colour) instead of the
     previous neutral grey --yc-subtle-bg -- and deliberately the SAME
     tint for every person, regardless of role (Customer vs household
     member/associate): colour must never imply an authority/permission
     hierarchy here, only the existing text (name, "(you)", role/access
     description) distinguishes people. */
  .personCard { background: #e6f3e8; border: 1px solid #b9dcc0; border-radius: 6px; padding: 12px 14px; margin-bottom: 10px; }
  .personCard:last-child { margin-bottom: 0; }
  /* Owner note (§11, v16 correction round): the amber "Action needed" card
     on My Yard Card previously set its amber background/border as an
     INLINE style, which (inline beats a class selector) silently cancelled
     .cardBoxLink:hover above -- every other cardBoxLink in the app gets a
     visible hover highlight; this one, despite being genuinely clickable,
     gave no feedback at all on hover. A dedicated class instead of inline
     styles restores real hover feedback while keeping the same amber
     colours the rest of "needs attention" styling already uses. */
  .lifecycleForms form { margin: 8px 0 12px; }
  input[type="tel"], input[data-phonefield] { letter-spacing: normal; font-variant-numeric: normal; }
  .postcodeRow input { max-width: 8ch; letter-spacing: normal; }
  .kv { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: 6px 14px; font-size: 13.5px; }   /* Round 9 §5/§14: one shared label column, wide enough for "Customer would like a response by" */
  @media (max-width: 600px) { .kv { grid-template-columns: minmax(0, 1fr); } .kv dt:not(:first-child) { margin-top: 10px; } }
  .enquiryStatusLine { margin: 8px 0 12px; }   /* Stage 7 §8: fits "Public holiday visits okay?" on one line */
  #app-shell.vp-mobile .kv { grid-template-columns: 1fr; }
  .kv dt { color: var(--yc-muted); font-weight: 600; }
  .kv dd { margin: 0; }
  /* Owner note (Stage 7 revision 12): stacked to one column on mobile,
     dt/dd pairs ran together with no visual break between one field and
     the next -- e.g. a name's value sitting right on top of the next
     field's label. Each new field (after the first) now gets breathing
     room above its label on mobile only -- the desktop two-column grid
     already reads correctly row by row and doesn't need this. */
  #app-shell.vp-mobile .kv dt:not(:first-child) { margin-top: 10px; }
  /* Owner note (No-Mow-Day Scheduling Safeguards round, since superseded by
     MASTER v3 §4): "Public holiday visits okay?" is longer than the
     default .kv label column (170px) can hold on one line, so that round
     gave ONLY the Timing dl/dt pairs holding these three permission
     questions a wider 240px column (.kvWidePermissions) to avoid mid-word
     wrapping. MASTER v3 §4 (3 October 2026) explicitly requires the
     opposite trade-off: "Across related cards on the same screen, the
     value column aligns identically. Do not allow one card's values to
     start at a different indent." On pub-review and staffBiz-enquiry-
     detail, the Timing/permissions card sits directly beside other cards
     using the plain 170px .kv, so the 240px variant was itself the
     violation -- a longer label (e.g. "Regular mowing frequency
     requested", 35 characters) already wraps acceptably at 170px
     elsewhere sitewide, so retiring this one-off width in favour of the
     same wrap behaviour is the consistent fix, not a regression. The
     kvWidePermissions class itself is removed from all 3 call sites below
     rather than left as a dead selector; see
     canonicalCardFieldAlignmentCheck() for the regression proving no
     rendered .kv anywhere diverges from the single canonical column
     width. */

  /* Owner review notes_34 (superseded by Correction Batch 01B §14): this
     rule used to also carry the Invoices button, sharing the stats row
     with "Account balance"/"Paid to date" -- the note below explained why
     that button got an unconditional row of its own on mobile, as a fix
     for content-dependent wrapping. The independent audit found that
     whole small-button-in-a-stats-row design wasn't prominent or tappable
     enough for the single most important action on this screen (where the
     Customer manages money), so the button has moved out into its own
     .navActionCard below -- this row is now stats only. */
  /* Round 11 N14: label / amount / note share rows across every item (top-aligned grid) */
  .acctBalanceRow { display: grid; grid-auto-flow: column; grid-auto-columns: max-content; grid-template-rows: auto auto auto; column-gap: 40px; row-gap: 2px; align-items: start; }
  .acctItem { display: grid; grid-row: span 3; grid-template-rows: subgrid; }
  .acctLabel { color: var(--yc-muted); font-size: 13.5px; }
  .acctAmount { font-size: 18px; font-weight: 700; }
  .acctNote { color: var(--yc-muted); font-size: 13px; }
  @media (max-width: 600px) { .acctBalanceRow { grid-auto-flow: row; grid-template-columns: 1fr auto; grid-template-rows: none; row-gap: 12px; }
    .acctItem { grid-column: 1 / -1; grid-row: auto; grid-template-columns: subgrid; grid-template-rows: none; row-gap: 0; align-items: baseline; }
    .acctItem .acctAmount { grid-column: 2; text-align: right; } .acctItem .acctNote { grid-column: 2; text-align: right; } }
  #app-shell.vp-mobile #screenRoot .btnRow:not(.decisionRow) > * { flex: 0 0 auto; }
  #app-shell.vp-mobile .acctBalanceRow { grid-auto-flow: row; grid-template-columns: 1fr auto; grid-template-rows: none; row-gap: 12px; }
  #app-shell.vp-mobile .acctItem { grid-column: 1 / -1; grid-row: auto; grid-template-columns: subgrid; grid-template-rows: none; row-gap: 0; align-items: baseline; }
  #app-shell.vp-mobile .acctItem .acctAmount, #app-shell.vp-mobile .acctItem .acctNote { grid-column: 2; text-align: right; }

  /* Owner note (Correction Batch 01B §14): the prominent "Invoices &
     payments" nav-action card -- built on the SAME shared .cardBox
     .cardBoxLink component already used elsewhere on this screen (the
     "Action needed" card, every row on the Invoices & payments list
     itself), not a one-off mobile CSS patch. .cardBox is already an
     effectively full-width block at mobile widths and a proportionate
     bordered card at desktop widths, so full-width/card-like on mobile
     and "visually obvious but not oversized" on desktop both come for
     free from the shared component; only the internal layout (label +
     arrow) and a minimum touch target are added here. */
  /* Owner feedback (01D v2 audit, B15): "Restore a clear outlined/action
     treatment for Invoices & payments" -- .cardBox's own default border
     (var(--yc-border), the same neutral grey every plain content card
     uses) reads as passive information, not a primary action, on the one
     nav card this screen most wants a Customer to notice. A green border
     (the same "this is a navigable action" colour already used by the
     canonical secondary button/.secondaryCta) distinguishes it the way
     the arrow alone was meant to but couldn't on its own.
     Owner feedback (MASTER v3 correction, Section 3): "Do not scatter
     decorative arrows on business-action buttons. Specifically remove
     the unwanted right arrow from Invoices and payments without changing
     that control's semantic button styling." The arrow span is removed;
     the card's own green border above already carries the "this is
     actionable" signal on its own, confirmed by this exact comment
     history -- no replacement decoration was added. */
  .navActionCard { display: flex; align-items: center; gap: 10px; min-height: 52px; font-size: 15px; border-color: var(--yc-green-dark); }

  .noteBox { background: var(--yc-amber-bg); border: 1px dashed var(--yc-amber-border); color: var(--yc-amber-ink); padding: 10px 12px; border-radius: 6px; font-size: 13px; margin: 10px 0; }
  .ownerDecisionBox { background: var(--yc-blue-bg); border: 1px solid var(--yc-blue-border); color: var(--yc-blue-ink); padding: 12px 14px; border-radius: 6px; font-size: 13px; margin: 14px 0; }
  .ownerDecisionBox b { display: block; margin-bottom: 4px; }

  .routeCaption { font-family: var(--font-mono); font-size: 11px; color: #9a9a90; margin: 2px 0 16px; }

  .btnRow { display: flex; gap: 8px; flex-wrap: wrap; margin: 10px 0; }
  /* Owner note (Stage 7 revision 12): the My Yard Card bottom action
     buttons looked lopsided -- one much longer label dominating the row
     while the others shrank to their own text. Given equal flex-basis so
     they read as one balanced row instead. */
  /* Owner note (Correction Batch 01B §14): "Check all My Yard Card
     navigation actions for consistent hierarchy and touch targets" --
     min-height/display/align/justify added so these three (Service
     requests/Service issues/My details) get a genuine minimum touch
     target rather than shrinking to .btnSm's bare text size, while
     staying deliberately smaller/secondary next to the new
     .navActionCard above (consistent hierarchy: one clear primary
     action, not four equal ones). */
  .btnRow.evenButtons > .btnSm { flex: 1 1 170px; text-align: center; font-weight: 600; min-height: 44px; display: flex; align-items: center; justify-content: center; }
  /* Owner note (Stage 7 revision 16): "three buttons at bottom look far
     better now, don't change layout -- but need same hover treatment as
     other selection buttons -- light green hovering, green when
     selected" -- same light-green hover as .optionBtn/.tabBtn; ":active"
     stands in for "when selected" here since these are one-way nav links
     with no persistent selected state, giving the same solid-green
     press feedback at the moment of the click. */
  .btnRow.evenButtons > .btnSm:hover { border-color: var(--yc-green-dark); background: #e6f3e8; }
  .btnRow.evenButtons > .btnSm:active { background: var(--yc-green-btn); border-color: var(--yc-green-btn); color: #fff; }
  /* Owner review notes_31 / MASTER v3 §2: this used to be a SEPARATE
     green-outline modifier with its own var(--yc-ink) text colour,
     contradicting the canonical SECONDARY treatment's own green text --
     .greenOutline is now a harmless legacy alias of the (now identical)
     unmodified .btnSm default; kept defined rather than hunting down
     every call site, since it genuinely renders the same either way. */
  .btnSm.greenOutline { font-weight: 600; color: var(--yc-green-dark); border-color: var(--yc-green-dark); }
  .btnSm.greenOutline:hover { border-color: var(--yc-green-dark); background: #e6f3e8; }
  .btnSm.greenOutline:active { background: var(--yc-green-btn); border-color: var(--yc-green-btn); color: #fff; }
  /* Owner feedback (01D v2 audit, B17): "Accept this item, Decline this
     Quote, View Quote, Review Quote and all equivalent actions must
     inherit canonical hover, keyboard focus, disabled and alignment
     treatment." Direct inspection found these real Customer-facing Quote-
     decision actions were plain .btnSm with no min-height -- 30px tall on
     mobile, well under this site's own established 44px touch-target
     minimum (§14/§22). .btnSmTouch is the one shared modifier for
     this exact family, deliberately separate from the base .btnSm used for
     dense Staff admin controls (e.g. the People & Authority Revoke/Edit
     links), which are intentionally compact and never meant to grow to a
     44px touch target.
     Also applied to each action's paired Cancel button, for genuine
     alignment where one matters: "Confirm decline" sits alone inside its
     own <form> (a flex item of .btnRow), so ITS OWN height is what the row
     stretches to match -- an unfixed Confirm decline would genuinely pull
     Cancel's stretched height down with it. (The reverse pairing --
     Confirm acceptance/Cancel -- already aligns via primaryCta's own
     pre-existing min-height and .btnRow's default flex stretch alone, with
     or without this class on Cancel; verified by sabotage, so that specific
     claim is not repeated here as a fixed defect.)
     Owner note: "View Quote" also uses .btnSmTouch, since it is
     the same kind of high-stakes, infrequent Quote action named in the
     brief (equivalent to Accept/Decline/Review), not a dense inline
     control.
     Owner feedback (01D v2 audit, B21): "Schedule Job, Back and every
     other clickable Job action must use the same canonical button/link
     hover, keyboard-focus, touch and disabled-state treatment established
     in B11/B17. This is not a Job-detail-only patch." -- renamed from the
     original .quoteDecisionAction to this more general name and reused
     (never duplicated) for the equivalent Job-action family below, exactly
     as B21 asks: the SAME canonical class, not a second copy of the same
     CSS under a Job-specific name. */
  .btnSm.btnSmTouch, .editLink.btnSmTouch { min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }
  /* Owner feedback (MASTER v3 correction, Section 2): "Implement ONE
     semantic action-control system and migrate every interactive action
     to it... All interactive action controls are rectangular... PRIMARY:
     solid Yard Card green, white text... SECONDARY: light/white
     background, Yard Card green border and green text... NAVIGATION/
     NEUTRAL: one consistent neutral treatment for Back/Cancel/leave
     actions... DESTRUCTIVE/CONSEQUENTIAL: a clearly differentiated
     warning treatment only for genuinely consequential actions." Direct
     inspection found the compact .btnSm family's own "secondary" variants
     (the unmodified default, .greenOutline, .btnRow.evenButtons > .btnSm)
     all rendered with var(--yc-ink) (near-black) text despite a green
     border -- contradicting .secondaryCta's own already-correct "green
     border AND green text" convention one screen over. The unmodified
     .btnSm default is now the ONE canonical SECONDARY treatment (matching
     .secondaryCta's colours exactly); a new .btnSm.neutral is the ONE
     canonical NAVIGATION/NEUTRAL treatment (Back/Cancel), kept visually
     distinct from secondary so the semantic roles stay separable at a
     glance; .btnSm.primary/.btnSm.danger are unchanged in colour (already
     correct) but share the same base. .editLink was also found to be
     border-radius:999px -- an interactive action rendered as a pill,
     which the locked owner decision explicitly reserves for non-
     interactive status badges only -- corrected to a rectangular radius
     matching .btnSm. */
  .btnSm { padding: 7px 12px; border-radius: 5px; border: 1.5px solid var(--yc-green-dark); background: #fff; color: var(--yc-green-dark); font-weight: 600; cursor: pointer; font-size: 12.5px; }
  .btnSm:hover:not(.primary):not(.danger):not(.neutral):not(:disabled) { background: #e6f3e8; }
  .btnSm.primary { background: var(--yc-green-btn); border-color: var(--yc-green-btn); color: #fff; font-weight: 700; }
  /* Owner note (Stage 7 revision 17): "Submit request/Report a service
     issue/Send invitation button needs hover treatment" -- same darken-
     on-hover .primaryCta already has, applied to every .btnSm.primary
     (Save, Submit request, Report a Service issue, Add person,
     etc.) for one consistent solid-green-CTA hover rule site-wide. */
  .btnSm.primary:hover { background: var(--yc-green-dark); border-color: var(--yc-green-dark); }
  /* NAVIGATION/NEUTRAL (MASTER v3 §2): Back/Cancel/leave actions -- a
     consistent neutral treatment, deliberately NOT the green secondary
     look, so a reader never mistakes "leaving/cancelling" for a
     supporting content action. Still rectangular, still 44px-capable via
     .btnSmTouch, still has its own hover/focus (the shared :focus-visible
     rule below already covers every .btnSm regardless of modifier). */
  .btnSm.neutral { background: #fff; border-color: #c9c6b8; color: var(--yc-ink); font-weight: 600; }
  .btnSm.neutral:hover { background: #f4f3ee; border-color: #b0ad9e; }
  /* R19 Part B final: the Terms page reads like the approved document -- normal body text, spaced paragraphs */
  .termsBody .termsPara { margin: 0 0 10px; font-size: 15px; line-height: 1.55; max-width: 72ch; }
  .termsBody .termsSection .sectionH { margin-top: 22px; }
  .btnSm.danger { background: #fff; border-color: var(--yc-red-border); color: var(--yc-red-ink); font-weight: 600; }
  .btnSm.danger:hover { background: var(--yc-red-bg); }

  /* Owner note (Stage 7 revision 8): Review & Send's Edit links "existed
     but the owner didn't notice them" -- make each one an unmistakable
     small button, not plain inline text. */
  .editLink {
    display: inline-flex; align-items: center; gap: 4px; font-size: 12.5px; font-weight: 600;
    padding: 5px 12px; border-radius: 5px; border: 1.5px solid var(--yc-green-dark);
    color: var(--yc-green-dark); background: #fff; cursor: pointer; white-space: nowrap;
  }
  .editLink:hover { background: #e6f3e8; }
  .reviewSection { margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--yc-border); }
  .reviewSection:first-of-type { margin-top: 18px; padding-top: 0; border-top: none; }
  .reviewSectionHead {
    display: flex; justify-content: space-between; align-items: flex-start; gap: 10px;   /* Round 14: badge level with the heading's first line */
    padding-bottom: 10px; margin-bottom: 14px; border-bottom: 1px solid var(--yc-border);
  }
  /* a badge sits level with the heading's first line when both fit; a long badge drops to its own line (never off-screen) */
  #screenRoot .reviewSectionHead { flex-wrap: wrap; row-gap: 6px; }
  .reviewSectionHead > :first-child { line-height: max(1.3em, 22px); flex: 1 1 12em; min-width: 0; }
  #screenRoot .reviewSectionHead > .badge { flex: 0 1 auto; max-width: 100%; margin-top: 0; }
  #screenRoot .reviewSectionHead > .srStatusCell { flex: 0 1 auto; max-width: 100%; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
  .reviewSectionHead h2.sectionH { margin: 0; }


  /* ---------- Review Notes (restored verbatim from the earlier
     owner-website-preview-v2.html mechanism the owner used successfully --
     a persistent floating toggle + bottom-right panel with per-screen
     tagged notes, an overall-notes tab, and Copy/Download/Clear actions.
     There is no different "email" mechanism in any earlier version to
     restore instead of this one: v1, v2 and this file all use the same
     copy-to-clipboard / download-a-.txt-file pattern -- the owner then
     pastes or attaches that into a new message in his own mail client.
     What changed here is restoring v2's more reliable, always-visible
     panel in place of this file's own short-lived slide-out drawer. ---------- */
  #review-toggle {
    position: fixed; bottom: 18px; right: 18px; z-index: 2100; font: inherit; font-size: 13px;
    padding: 10px 16px; border-radius: 999px; border: none; background: var(--yc-green-btn); color: #fff;
    cursor: pointer; box-shadow: 0 3px 10px rgba(0,0,0,0.25);
  }
  @media (max-width: 480px) {
    #review-toggle { bottom: 12px; right: 12px; padding: 9px 12px; font-size: 0; }
    #review-toggle::before { content: "\1F4DD"; font-size: 18px; }
  }
  #review-panel {
    position: fixed; bottom: 0; right: 0; z-index: 2101; width: 360px; max-width: 94vw; max-height: 82vh;
    background: #fff; border: 1px solid var(--yc-border); border-radius: 10px 10px 0 0;
    box-shadow: 0 -4px 16px rgba(0,0,0,0.2); display: none; flex-direction: column;
  }
  #review-panel.open { display: flex; }
  /* ---- Round 9 §2/§3: shared form spacing and step hierarchy */
  :root { --yc-question-gap: 20px; --yc-label-gap: 6px; }
  #screenRoot .fieldRow { margin-bottom: var(--yc-question-gap); }
  #screenRoot .fieldRow + .fieldRow, #screenRoot .fieldRow + fieldset, #screenRoot fieldset + .fieldRow { margin-top: var(--yc-question-gap); }
  #screenRoot .fieldRow > label { margin-bottom: var(--yc-label-gap); }
  #screenRoot .journeySteps { margin: 10px 0 34px; }
  #screenRoot .journeySteps + :is(h1, .pageH) { margin-top: 0; }
  /* ---- Round 9 §7 */
  .heroCopy { text-wrap: balance; }
  .keepTogether { white-space: nowrap; }
  /* ---- Round 9 §37: invalid controls are marked by more than colour */
  #screenRoot [aria-invalid="true"] { border-color: var(--yc-red-ink, #9b2c2c) !important; box-shadow: 0 0 0 2px rgba(155, 44, 44, 0.25); }
  #screenRoot .optionRow.invalidGroup { outline: 2px solid var(--yc-red-ink, #9b2c2c); outline-offset: 4px; border-radius: 6px; }
  #screenRoot .fieldError::before { content: "⚠ "; }
  .errorSummaryLinks { margin: 6px 0 0; padding-left: 18px; }
  /* ---- Round 9 §40: addresses get real width on desktop Staff lists */
  @media (min-width: 1001px) { table.ycTable :is(td, th)[data-label="Property"], table.ycTable :is(td, th)[data-label="Address"], table.ycTable td[data-label="Mowing address"] { min-width: 230px; } }
  /* ---- Round 13: short button rows stay side by side on phones (class set by layoutButtonRows) */
  @media (max-width: 600px) {
    #screenRoot .btnRow.btnRowInline:not(.decisionRow) { flex-direction: row; flex-wrap: nowrap; align-items: stretch; }
    #screenRoot .btnRow.btnRowInline:not(.decisionRow) > * { flex: 1 1 auto; min-width: 0; width: auto; }
  }
  #app-shell.vp-mobile #screenRoot .btnRow.btnRowInline:not(.decisionRow) { flex-direction: row; flex-wrap: nowrap; align-items: stretch; }
  #app-shell.vp-mobile #screenRoot .btnRow.btnRowInline:not(.decisionRow) > * { flex: 1 1 auto; min-width: 0; width: auto; }
  #screenRoot .btnRow.btnRowInline > form { display: flex; margin: 0; }
  #screenRoot .btnRow.btnRowInline > form > :is(.btnSm, .primaryCta, button) { width: 100%; }
  /* ---- Round 9 §4/§41: narrow viewports (the real width, not only the harness switch) */
  @media (max-width: 600px) {
    #screenRoot .btnRow:not(.decisionRow) { flex-direction: column; align-items: stretch; gap: 10px; }
    #screenRoot .btnRow:not(.decisionRow) > * { flex: 0 0 auto; }
    #screenRoot .btnRow:not(.decisionRow) > :is(.btnSm, .primaryCta, .secondaryCta, a.btnSm), #screenRoot .btnRow:not(.decisionRow) > form, #screenRoot .btnRow:not(.decisionRow) > form > :is(.btnSm, .primaryCta) { width: 100%; box-sizing: border-box; }
    #screenRoot .btnRow:not(.decisionRow) > :is(.btnSm, .primaryCta, .secondaryCta), #screenRoot .btnRow:not(.decisionRow) > form > :is(.btnSm, .primaryCta), #screenRoot .navBtns > :is(.primaryCta, .secondaryCta, .btnSm), #screenRoot form > .primaryCta, #screenRoot .navActionCard {
      display: flex; align-items: center; justify-content: center; text-align: center; min-height: 44px; white-space: normal; }
    #screenRoot .navBtns { display: flex; flex-direction: column; gap: 10px; }
    #screenRoot .navBtns > * { width: 100%; box-sizing: border-box; }
    #screenRoot form > .primaryCta, #screenRoot .issueChoice > .btnSm { width: 100%; box-sizing: border-box; }
    table.ycTable, table.ycTable thead, table.ycTable tbody, table.ycTable tr { display: block; }
    table.ycTable thead { display: none; }
    #screenRoot .badge { white-space: normal; max-width: 100%; box-sizing: border-box; }   /* §41: a long status badge wraps at 320px */
    #screenRoot form[data-mockform="source-filter"] { flex-wrap: wrap; }
    table.ycTable tr { border: 1px solid var(--yc-border); border-radius: 6px; margin-bottom: 10px; padding: 4px 0; background: #fff; }
    table.ycTable td { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 4px 10px; border-bottom: none; padding: 6px 10px; min-height: 20px; overflow-wrap: anywhere; }
    table.ycTable td::before { content: attr(data-label); font-weight: 700; color: var(--yc-muted); font-size: 11px; text-transform: uppercase; }
    table.ycTable td:is([data-label="Property"], [data-label="Address"], [data-label="Mowing address"], [data-label="Detail"]) { flex-direction: column; align-items: flex-start; }
  }
  #app-shell.vp-mobile table.ycTable td:is([data-label="Property"], [data-label="Address"], [data-label="Mowing address"], [data-label="Detail"]) { flex-direction: column; align-items: flex-start; }

  /* Round 9 §9-§11: Quote document hierarchy */
  .infoCard { margin: 14px 0 0; padding: 12px 14px; border: 1px solid var(--yc-border); border-left: 4px solid #9fb8a6; border-radius: 8px; background: #f8faf7; }
  .infoCardH { margin: 0 0 4px; font-size: 14px; font-weight: 700; color: var(--yc-ink, #1f2a22); }
  .ycqHead { border-top: 4px solid var(--yc-green); }
  .ycqOffer .ycqPrice { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; }
  .primaryCta.acceptCta { display: inline-flex; align-items: center; justify-content: center; min-height: 48px; min-width: 260px; font-size: 16px; font-weight: 700; box-sizing: border-box; }
  [data-quote-decision] .declineCard .decisionBtn { font-weight: 400; }
  @media (max-width: 600px) { .primaryCta.acceptCta { display: flex; width: 100%; min-width: 0; } }
  .issueChoices { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; margin-top: 10px; }
  .issueChoice .help { margin: 6px 0 0; }
  .firstMowSub { margin: 12px 0 4px; padding-top: 10px; border-top: 1px dashed var(--yc-border); }
  /* Round 9 §8: review-harness simulation controls are hidden from the operational UI */
  body:not(.reviewHarnessOn) [data-review-harness] { display: none !important; }
  body.reviewHarnessOn [data-review-harness] { outline: 2px dashed #7a4fbf; outline-offset: 2px; }
  .rp-head { display: flex; justify-content: space-between; align-items: flex-start; padding: 12px 14px; border-bottom: 1px solid var(--yc-border); background: #f6f4ed; border-radius: 10px 10px 0 0; }
  .rp-head strong { font-size: 13px; display: block; }
  .rp-head small { font-size: 11px; color: var(--yc-muted); }
  .rp-close { background: none; border: none; font-size: 15px; cursor: pointer; color: var(--yc-muted); }
  .rp-tabs { display: flex; border-bottom: 1px solid var(--yc-border); }
  .rp-tab { flex: 1; font: inherit; font-size: 12px; font-weight: 700; padding: 8px; border: none; background: #fff; color: var(--yc-muted); cursor: pointer; border-bottom: 2px solid transparent; }
  .rp-tab.active { color: var(--yc-green-dark); border-bottom-color: var(--yc-green); }
  .rp-body { padding: 12px 14px; overflow-y: auto; flex: 1; display: flex; flex-direction: column; gap: 10px; }
  .rp-current { font-size: 12px; color: var(--yc-muted); }
  .rp-saved { font-size: 11px; color: var(--yc-green-dark); font-weight: 700; min-height: 14px; }
  .rp-saved.rp-save-error { color: var(--yc-red-ink); }
  .rp-tags { display: flex; flex-wrap: wrap; gap: 6px; }
  .rp-tag { font: inherit; font-size: 11.5px; padding: 5px 9px; border-radius: 999px; border: 1px solid var(--yc-border); background: #fff; cursor: pointer; }
  .rp-tag.good { border-color: #bfe0c4; color: var(--yc-green-dark); }
  .rp-tag.confusing { border-color: #f0d989; color: #7a5c00; }
  .rp-tag.bug { border-color: #e6c9c6; color: #a3241f; }
  .rp-tag.missing { border-color: #c6d9f0; color: #1f4f8f; }
  .rp-tag.wording { border-color: #d7c9f0; color: #4a2e7a; }
  .rp-tag.visual { border-color: #ddd5c0; color: #5b5b52; }
  #rp-note, #rp-overall { font: inherit; font-size: 13px; width: 100%; min-height: 60px; padding: 8px; border: 1px solid var(--yc-border); border-radius: 4px; resize: vertical; box-sizing: border-box; }
  #rp-overall { min-height: 120px; }
  .rp-add { font: inherit; font-size: 12px; padding: 6px 10px; border-radius: 4px; border: 1px solid var(--yc-border); background: #fff; cursor: pointer; align-self: flex-start; }
  .rp-list { display: flex; flex-direction: column; gap: 8px; font-size: 12px; }
  .rp-item { border: 1px solid var(--yc-border); border-radius: 6px; padding: 8px; position: relative; }
  .rp-item .tag { font-weight: 700; font-size: 11px; }
  .rp-item .screen { color: var(--yc-muted); font-size: 11px; }
  .rp-item .rp-del { position: absolute; top: 6px; right: 6px; background: none; border: none; color: var(--yc-muted); cursor: pointer; font-size: 12px; }
  .rp-foot { display: flex; gap: 8px; padding: 10px 14px; border-top: 1px solid var(--yc-border); flex-wrap: wrap; }
  .rp-foot button { flex: 1; font: inherit; font-size: 11.5px; padding: 8px 6px; border-radius: 4px; border: 1px solid var(--yc-border); background: #fff; cursor: pointer; }
  .rp-foot button.danger { color: #a3241f; border-color: #e6c9c6; }

  #console-check { position: fixed; bottom: 0; left: 0; background: #111; color: #6fcf7f; font-family: var(--font-mono); font-size: 11px; padding: 4px 8px; z-index: 3000; opacity: 0.85; }
  /* PRELAUNCH CONVERGENCE P1: canonical seasonal-frequency value + snapshot blocks */
  /* Round 11 N02: ONE grid for both seasons -> the frequency words share one column edge */
  .freqPeriods { display: grid; grid-template-columns: max-content 1fr; column-gap: 18px; row-gap: 8px; align-items: baseline; }
  .freqPeriod { display: contents; }
  .freqRange { color: var(--yc-muted); }
  .svcListLine, .svcListFreq { display: block; margin-top: 6px; }
  .fbList { display: grid; gap: 10px; }
  a.textLink { color: var(--yc-green-dark); text-decoration: underline; text-underline-offset: 2px; }
  .fbItemHead { margin: 0; display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; }
  .fbReply { margin-top: 10px; padding: 8px 10px; border-left: 3px solid var(--yc-green); background: #f3f8f3; border-radius: 4px; }
  .fbSteps { margin: 0 0 12px; padding-left: 20px; } .fbSteps li.done { text-decoration: line-through; }
  .svcStatusCard { margin-top: 18px; }
  .svcStatusCard .fieldRow { max-width: 260px; }
  .svcStatusCard .dtPair .fieldRow { max-width: 220px; }
  .svcStatusCard .svcState { margin: 0 0 12px; line-height: 1.6; }
  .svcStatusCard .svcState > .badge { margin-right: 8px; }
  .svcStatusCard .svcAction { border-top: 1px solid var(--yc-border); padding-top: 12px; margin-top: 4px; }
  .svcStatusCard .subH { margin: 0 0 8px; }
  .pendingChange { display: block; margin-top: 6px; font-size: 13px; color: var(--yc-amber-ink); }
  .srOutcomeLine { display: block; margin-top: 4px; font-size: 13px; font-weight: 400; color: var(--yc-muted); }
  .pageH .srOutcomeLine, .headWithBadge .srOutcomeLine { display: inline; margin: 0 0 0 6px; }
  .srStatusCell { text-align: right; }
  .svcState .pendingChange { display: inline; margin: 0; font-size: inherit; }
  .jobRefLine { display: block; font-weight: 600; margin-bottom: 2px; }
  .svcStatusCard button.btnSm { min-width: 200px; text-align: center; justify-content: center; }
  @media (max-width: 600px) { .svcStatusCard button.btnSm { width: 100%; min-width: 0; } }
  #app-shell.vp-mobile .svcStatusCard button.btnSm { width: 100%; min-width: 0; }
  .svcStatusCard [data-svc-request-links] .btnRow { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); grid-auto-rows: 1fr; gap: 8px; }   /* R17: every button the same height */
  .svcStatusCard [data-svc-request-links] .btnRow > .btnSm { margin: 0; text-align: center; justify-content: center; }
  .svcStatusCard [data-svc-request-links] .btnSm[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }   /* R19 (note 21) */
  .svcStatusCard [data-svc-request-links] .btnRow.svcReqGrid { grid-auto-rows: auto; }
  .svcStatusCard [data-svc-request-links] .svcReqGrid > .btnSm { min-height: 44px; }
  .svcStatusCard [data-svc-request-links] .svcReqHead, .svcStatusCard [data-svc-request-links] .svcReqNote { grid-column: 1 / -1; margin: 6px 0 0; }
  @media (max-width: 600px) { .svcStatusCard [data-svc-request-links] .btnRow { grid-template-columns: 1fr; } }
  #app-shell.vp-mobile .svcStatusCard [data-svc-request-links] .btnRow { grid-template-columns: 1fr; }
  .notRecorded { color: var(--yc-muted); font-style: italic; }
  .serviceBlock { margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid var(--yc-border); }
  .serviceBlock + dl { margin-top: 4px; }
  .currencyInput { display: inline-flex; align-items: stretch; max-width: 220px; width: 100%; }
  .currencyInput .currencySymbol { display: inline-flex; align-items: center; padding: 0 10px; border: 1px solid var(--yc-border); border-right: 0; border-radius: 8px 0 0 8px; background: #f4f6f3; color: var(--yc-muted); font-weight: 700; }
  .currencyInput input { border-radius: 0 8px 8px 0; min-width: 0; flex: 1; }
  .savedNotice { color: var(--yc-green-dark); font-weight: 700; margin: 6px 0 0; }
  .noRemedyForm { margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--yc-border); }
  .noteEntry { border-left: 3px solid var(--yc-green-dark); background: #f4f8f4; padding: 8px 10px; margin: 8px 0; border-radius: 4px; }
  .noteEntry.customer { border-left-color: var(--yc-blue-border); background: var(--yc-blue-bg); }
  .noteMeta { font-size: 12px; color: var(--yc-muted); margin-bottom: 4px; display: flex; flex-wrap: wrap; gap: 4px 6px; align-items: center; }
  .staffNoteForm { margin-top: 10px; }
  .releaseUpdate { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--yc-border); }
  .subH { font-size: 15px; margin: 0 0 8px; }
  .previewBox { border: 1px dashed var(--yc-blue-border); background: var(--yc-blue-bg); padding: 10px 12px; border-radius: 8px; margin: 6px 0 10px; }
  .checkLine { display: flex; gap: 8px; align-items: flex-start; margin: 8px 0; font-size: 14px; }
  .outcomeTable tr.current td { background: #f4f8f4; }
  .paymentOutcome { border-radius: 10px; padding: 12px 14px; margin: 12px 0; border: 2px solid; animation: poIn .25s ease-out; }
  .paymentOutcome p { margin: 4px 0 0; }
  .paymentOutcome .poTitle { margin: 0; font-weight: 800; font-size: 16px; }
  .paymentOutcome.success { background: #e9f6ec; border-color: var(--yc-green-dark); color: #164d22; }
  .paymentOutcome.failed { background: var(--yc-red-bg); border-color: var(--yc-red-border); color: var(--yc-red-ink); }
  .paymentOutcome.cancelled { background: #f4f6f3; border-color: var(--yc-border); }
  @keyframes poIn { from { transform: scale(.98); opacity: .4; } to { transform: none; opacity: 1; } }
  @media (prefers-reduced-motion: reduce) { .paymentOutcome { animation: none; } }
  .notBuiltList { margin: 6px 0 0; padding-left: 18px; }
  .notBuiltList li { margin: 4px 0; }

  /* ==================================================================
     PRELAUNCH CONVERGENCE P2 -- ONE canonical control system (brief §9;
     owner notes-57: button widths, "hover treatment shade of green is
     inconsistent ... should be ... all light green", "drop-down boxes
     ... also need hover treatment", green vs black button text).
     Declared LAST so it is the single source of truth over the many
     earlier per-round rules above.
       Geometry : every action control 44px min height, 6px radius,
                  14px text, 1.5px border, centred content.
       PRIMARY  : solid green / white text  (.primaryCta, .btnSm.primary)
       SECONDARY: white / green border + green text (.secondaryCta, .btnSm,
                  .editLink, nav tiles)
       NAVIGATION (Back / Cancel): white / neutral border, ink text
       DESTRUCTIVE: white / red border + red text; red-tint hover
                  (deliberate warning treatment, §9)
       HOVER    : ONE light-green treatment for every non-destructive
                  control, selects included.
       FOCUS    : one 3px green ring on every interactive element.
       WIDTH    : alternatives in one decision row share one width
                  (the widest), never ragged.
     Status pills (.badge) are never buttons and get none of this.
     ================================================================== */
  :root { --yc-hover-bg: #e6f3e8; }
  .primaryCta, .secondaryCta, .btnSm, .editLink, .optionBtn, .tabBtn, .navActionCard {
    min-height: 44px; border-radius: 6px; font-size: 14px; line-height: 1.25; border-width: 1.5px; border-style: solid;
    box-sizing: border-box; cursor: pointer; text-decoration: none;
  }
  .primaryCta, .secondaryCta, .btnSm, .editLink { display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 10px 16px; text-align: center; vertical-align: middle; }
  .primaryCta, .btnSm.primary { background: var(--yc-green-btn); border-color: var(--yc-green-btn); color: #fff; font-weight: 700; }
  .secondaryCta, .btnSm, .btnSm.greenOutline, .editLink { background: #fff; border-color: var(--yc-green-dark); color: var(--yc-green-dark); font-weight: 600; }
  .btnSm.neutral { background: #fff; border-color: #c9c6b8; color: var(--yc-ink); font-weight: 600; }
  .btnSm.danger { background: #fff; border-color: var(--yc-red-border); color: var(--yc-red-ink); font-weight: 600; }
  .navActionCard { color: var(--yc-green-dark); font-weight: 700; border-color: var(--yc-green-dark); }
  .navActionCard::after { content: "\203A"; margin-left: auto; font-size: 22px; line-height: 1; }
  :is(.primaryCta, .secondaryCta, .btnSm, .editLink, .optionBtn, .tabBtn, .navActionCard, .cardBoxLink, .btnRow.evenButtons > .btnSm):hover:not(:disabled):not([aria-disabled="true"]):not(.danger):not([aria-pressed="true"]):not([aria-selected="true"]) {
    background: var(--yc-hover-bg); border-color: var(--yc-green-dark); color: var(--yc-green-dark);
  }
  .btnSm.danger:hover:not(:disabled) { background: var(--yc-red-bg); border-color: var(--yc-red-ink); color: var(--yc-red-ink); }
  select { min-height: 44px; padding: 9px 34px 9px 10px; border: 1.5px solid #c9c6b8; border-radius: 6px; font: inherit; font-size: 14px; color: var(--yc-ink); cursor: pointer;
    appearance: none; -webkit-appearance: none;
    background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none' stroke='%235b5b52' stroke-width='1.7'%3E%3Cpath d='M1 1.5l5 5 5-5'/%3E%3C/svg%3E") no-repeat right 14px center; }
  select:hover:not(:disabled) { background-color: var(--yc-hover-bg); border-color: var(--yc-green-dark); }
  :is(a, button, select, input, textarea, [tabindex], [data-nav]):focus-visible { outline: 3px solid var(--yc-green-dark); outline-offset: 2px; }
  .btnRow { display: flex; flex-wrap: wrap; gap: 10px; align-items: stretch; }
  .btnRow.decisionRow { display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr; max-width: 100%; }
  .btnRow.decisionRow > *, .btnRow.decisionRow > form > button { width: 100%; }
  .btnRow.decisionRow > form { display: flex; margin: 0; }
  @media (max-width: 480px) {
    .btnRow.decisionRow { display: grid; grid-auto-flow: row; width: 100%; }
    .services2 .primaryCta { width: 100%; }
  }
  /* homepage: the two service choices are one decision -- same-width CTAs */
  .services2 .serviceCard2 { display: flex; flex-direction: column; }
  .services2 .serviceCard2 > p { flex: 1 1 auto; }
  .services2 .primaryCta { align-self: stretch; }
  /* headings carrying a status pill: the pill is centred on the heading row */
  .pageH:has(> .badge), .sectionH:has(> .badge), .withBadge { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px; }
  .focusTarget { outline: 3px solid var(--yc-amber-border); outline-offset: 2px; }
  /* P2 quote layout (owner notes-57: frequency/clippings "crammed", accept vs decline "different sizes and don't line up") */

  dl.kv + .help, .freqPeriods + .help { margin-top: 10px !important; }
  .itemDecision { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--yc-border); }
  .reviewSectionHead + .itemDecision { margin-top: 0; padding-top: 0; border-top: none; }   /* §11: one rule under the item heading, not two */
  .decisionBtn { min-width: 220px; }
  @media (max-width: 480px) { .decisionBtn { width: 100%; } }
  .acceptedNote { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; line-height: 1.5; margin-top: 14px; }
  .acceptedNote .acceptedNoteNext { flex: 1 1 260px; }
  .amountSummary { margin-top: 4px; }
  .invoiceStatusLine { margin: -4px 0 8px; }
  .stripeSimRow > .btnRow { display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr; max-width: 100%; }
  .stripeSimRow > .btnRow > form { display: flex; margin: 0; }
  .stripeSimRow > .btnRow > form > button { width: 100%; }
  @media (max-width: 600px) { .stripeSimRow > .btnRow { display: grid; grid-auto-flow: row; width: 100%; } }
  .paymentHistory td[data-label="Date"], .paymentHistory td[data-label="Amount"] { white-space: nowrap; }
  .paymentHistory td[data-label="Method"] { overflow-wrap: anywhere; }
  /* P2: between phone and full desktop (tablet, or desktop at 200% zoom) a wide
     table scrolls sideways inside its own box -- never the whole page, never
     dropping a column; a fading edge shows there is more to the right. */
  @media (min-width: 601px) and (max-width: 900px) {
    #screenRoot .badge { white-space: normal; max-width: 100%; box-sizing: border-box; }
    #app-shell:not(.vp-mobile) table.ycTable, #app-shell:not(.vp-mobile) table.ycTable thead, #app-shell:not(.vp-mobile) table.ycTable tbody, #app-shell:not(.vp-mobile) table.ycTable tr { display: block; }
    #app-shell:not(.vp-mobile) table.ycTable thead { display: none; }
    #app-shell:not(.vp-mobile) table.ycTable tr { border: 1px solid var(--yc-border); border-radius: 6px; margin-bottom: 10px; padding: 4px 0; background: #fff; }
    #app-shell:not(.vp-mobile) table.ycTable td { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 4px 16px; border-bottom: none; padding: 6px 12px; min-height: 20px; overflow-wrap: anywhere; }
    #app-shell:not(.vp-mobile) table.ycTable td::before { content: attr(data-label); font-weight: 700; color: var(--yc-muted); font-size: 11px; text-transform: uppercase; }
    #app-shell:not(.vp-mobile) table.ycTable td > .badge:first-child { margin-left: 0; }
    #app-shell:not(.vp-mobile) table.ycTable td:not(:is([data-label="Property"], [data-label="Address"], [data-label="Mowing address"], [data-label="Detail"])) > :last-child { margin-left: auto; text-align: right; }   /* R14 (notes 75): a card value (badge or text) keeps to the right, even when it wraps */
    #app-shell:not(.vp-mobile) table.ycTable td:is([data-label="Property"], [data-label="Address"], [data-label="Mowing address"], [data-label="Detail"]) { flex-direction: column; align-items: flex-start; }
  }
  @media (min-width: 901px) and (max-width: 1000px) {
    #app-shell:not(.vp-mobile) table.ycTable { display: block; max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch;
      background: linear-gradient(to right, #fff 30%, rgba(255,255,255,0)) left center / 24px 100% no-repeat local, linear-gradient(to left, #fff 30%, rgba(255,255,255,0)) right center / 24px 100% no-repeat local, radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,.18), rgba(0,0,0,0)) right center / 10px 100% no-repeat scroll; }
  }
  .journeySteps { margin: 0 0 16px; }
  .journeyStepsLabel { margin: 0 0 var(--yc-text-control-gap); font-size: 13px; font-weight: 700; color: var(--yc-muted); }   /* R19 Part B (note 4): more space under "Step N of 5" */
  .journeySteps ol { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(5, 1fr); gap: 4px; }
  .journeySteps li { border-top: 5px solid #d9d6ca; padding-top: 5px; font-size: 12px; line-height: 1.3; color: var(--yc-muted); }
  .journeySteps li.done { border-top-color: var(--yc-green); }
  .journeySteps li.done a { color: var(--yc-green-dark); text-decoration: underline; cursor: pointer; }
  .journeySteps li.current { border-top-color: var(--yc-green-dark); color: var(--yc-ink); font-weight: 700; }
  #app-shell.vp-mobile .journeySteps li span, #app-shell.vp-mobile .journeySteps li a { font-size: 11px; }
  /* P2 (owner notes-58 "enquiry etc cards are too close to left margin"; notes-59
     "service request card text is awkwardly aligned"): ROOT CAUSE was the rule
     "table.ycTable td > .badge:first-child { margin-left: -10px }", which pulled
     every leading pill out of its cell onto the table edge, plus pills wrapping
     onto two lines. Pills now sit inside the cell padding and never wrap; a
     reference never breaks across lines. (N57-07: a clear gap before frequency.) */
  /* (keeps revision 15's intent -- pill TEXT aligned with its header -- without
     pushing a first-column pill out of the table: there the header moves in) */
  table.ycTable td:first-child > .badge:first-child { margin-left: 0; }

  .badge { white-space: nowrap; }
  table.ycTable td[data-label="Reference"], table.ycTable td[data-label="Invoice"], table.ycTable td[data-label="Quote"] { white-space: nowrap; }
  dl.kv dt:has(+ dd[data-freq-row]), dl.kv dd[data-freq-row] { margin-top: 10px; }
  .roleChangeForm { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: flex-end; margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--yc-border); }
  .roleChangeForm .fieldRow { margin: 0; min-width: 180px; flex: 1 1 180px; }
  .matrixIndex ol { columns: 2 240px; margin: 0 0 14px; padding-left: 20px; font-size: 13px; }
  .matrixIndex a { color: var(--yc-green-dark); }
  .matrixCard { scroll-margin-top: 12px; }
  .matrixCardTitle { margin: 0 0 8px; font-size: 15px; }
  dl.kv.matrixKv dd { overflow-wrap: anywhere; }
  /* Stage 7 §13: Needs-attention summary badges */
  .attentionSummary { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px; }
  .attentionChip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 999px; border: 1px solid var(--yc-amber-border); background: var(--yc-amber-bg); color: var(--yc-amber-ink); font-weight: 700; font-size: 13px; cursor: pointer; text-decoration: none; }
  .attentionChip .attentionCount { display: inline-block; min-width: 20px; text-align: center; background: var(--yc-amber-ink); color: #fff; border-radius: 999px; padding: 0 6px; }
  .filterNote { margin: -4px 0 12px; font-size: 13px; color: var(--yc-muted); }
  /* Stage 7 §6: two sibling actions with identical geometry */
  .ctaPair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 22px; max-width: 560px; }
  .ctaPair > .secondaryCta, .ctaPair > .primaryCta { width: 100%; box-sizing: border-box; text-align: center; }
  .ctaPair:has(> :only-child) { grid-template-columns: minmax(0, 1fr); max-width: 275px; }
  @media (max-width: 560px) { .ctaPair { grid-template-columns: minmax(0, 1fr); max-width: none; } .ctaPair:has(> :only-child) { max-width: none; } }
  /* Stage 7 §8/§10/§11 */
  .headWithBadge { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
  .headWithBadge .badge { font-size: 12px; vertical-align: middle; }
  .personFacts { margin-top: 6px; }
  .relGroupHead { margin: 10px 0 4px; font-size: 12.5px; font-weight: 700; color: var(--yc-muted); }
  .plainList { margin: 0; padding-left: 18px; }
  .plainList li + li { margin-top: 4px; }
  /* Round 14 (notes 71): ONE history style -- unbulleted lines, like contact edit history */
  ul.historyList { list-style: none; margin: 0; padding: 0; }
  ul.historyList > li + li { margin-top: 6px; }
  .removedPeople { margin: 10px 0; padding-top: 10px; border-top: 1px solid var(--yc-border); }
  /* Stage 7 §8: shared spacing tokens. */
  :root { --yc-text-control-gap: 12px; --yc-step-gap: 22px; }
  /* explanatory text directly above a control, card, list or table: one consistent gap,
     whatever inline margin an individual help paragraph carries */
  #screenRoot p.help + :is(.optionRow, .cardBox, table, .btnRow, dl.kv, form, select, input, textarea, .navBtns, .ctaPair, .tabRow, .linkList, ul) { margin-top: var(--yc-text-control-gap) !important; }
  #screenRoot .fieldRow > p.help + :is(select, input, textarea, .optionRow) { margin-top: var(--yc-text-control-gap) !important; }
  /* Review & Send: one shared label column wide enough for the longest label
     ("Public holiday visits okay?") on a single line at desktop width */
  /* Property address step */
  .addressStep .fieldRow { margin-top: var(--yc-step-gap); }
  .addressStep .fieldRow > input { padding: 12px; font-size: 15px; }
  .addressStep .fieldRow > p.help { margin-top: 10px !important; line-height: 1.5; }
  .addressStep .navBtns { margin-top: 28px; }
  /* My details: person facts as their own labelled rows */
  .personCard .personFacts { margin-top: 8px; }
  /* Round 8 §B: the canonical Quote document (mirrors the Estimator's Quote Details) */
  .ycqDoc .ycqOffer { margin-top: 8px; }
  .ycqItemName { display: block; font-size: 15px; margin: 0 0 6px; }
  /* Round 10 §4: one neutral service-name treatment (never green-as-status) */
  .serviceName { color: var(--yc-ink); font-weight: 600; }
  /* Round 10 §5: shared rhythm -- groups, facts, action blocks */
  dl.kv .kvGroupStart { margin-top: 10px; padding-top: 12px; border-top: 1px solid var(--yc-border); }
  .factLines { display: grid; gap: 6px; margin: 16px 0 0; }
  .factLines > p, .factLines > .help { margin: 0 !important; }
  .noteBox + .factLines { margin-top: 18px; }
  .outcomeActions { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; margin-top: 12px; }
  .outcomeAction { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; padding: 12px; border: 1px solid var(--yc-border); border-radius: 8px; background: #fff; margin: 0; }
  .outcomeAction .fieldRow { width: 100%; margin: 0; }
  .outcomeActions > form:not(.outcomeAction) { align-self: end; }
  #app-shell.vp-mobile .outcomeAction .btnSm { width: 100%; justify-content: center; text-align: center; }
  @media (max-width: 600px) { .outcomeAction .btnSm { width: 100%; justify-content: center; text-align: center; } }
  fieldset.plainFieldset, .permPanel fieldset, [data-si-resolve] fieldset { border: 0; padding: 0; margin: 0 0 12px; min-width: 0; }
  .permPanel legend, [data-si-resolve] legend { font-weight: 700; font-size: 13px; padding: 0; margin: 0 0 6px; }
  fieldset.plainFieldset > legend { font-weight: 700; font-size: 13px; padding: 0; margin: 0 0 5px; }
  .radioCol { display: grid; gap: 6px; }
  /* Round 10 census (320px): inline source/target pickers wrap instead of pushing the page sideways */
  .inlineForm { display: inline-flex; flex-wrap: wrap; gap: 6px; align-items: center; max-width: 100%; }
  .inlineForm select { max-width: 100%; min-width: 0; }
  .tlActions { display: grid; gap: 6px; margin-top: 6px; }
  /* Round 10 §20E: shared icon language (inline SVG, aria-hidden, always beside text) */
  .ycIcon { width: 1.15em; height: 1.15em; flex: none; vertical-align: -0.2em; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
  .iconLabel { display: inline-flex; align-items: center; gap: 0.4em; }
  .pageIcon { width: 26px; height: 26px; margin-right: 8px; color: var(--yc-green-dark); vertical-align: -4px; }
  .visuallyHidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  /* Round 10 §20D: Needs Attention -- one record per card */
  .attnList { list-style: none; margin: 0 0 8px; padding: 0; display: grid; gap: 10px; }
  .attnItem { display: block; }
  .actionNeededList { margin: 0 0 18px; }
  .attnCard { display: grid; grid-template-columns: 28px minmax(0, 1fr); column-gap: 14px; padding: 18px 20px 20px; background: #fff; color: var(--yc-ink); text-decoration: none;
    border: 1px solid var(--yc-border); border-left: 5px solid var(--yc-amber-border); border-radius: 10px; cursor: pointer; }
  .attnCard:hover { background: var(--yc-amber-bg); border-color: var(--yc-amber-border); }
  .attnCard:focus-visible { outline: 3px solid var(--yc-green-dark); outline-offset: 3px; }
  .attnCardIcon { color: var(--yc-amber-ink); padding-top: 1px; }
  .attnCardIcon .ycIcon { width: 26px; height: 26px; stroke-width: 2; }
  .attnCardBody { display: flex; flex-direction: column; min-width: 0; }
  .attnCardHead { font-size: 17px; font-weight: 700; line-height: 1.3; margin: 0 0 10px; }
  .attnCardSubject { font-size: 16px; font-weight: 600; line-height: 1.35; }
  .attnCardAddress { font-size: 14px; color: var(--yc-ink); margin-top: 3px; line-height: 1.4; }
  .attnCardRef { font-size: 13px; color: var(--yc-muted); margin-top: 6px; }
  .attnCardReason { font-size: 14px; margin-top: 6px; line-height: 1.4; }
  .attnCardCta { align-self: flex-start; margin-top: 16px; min-width: 200px; box-sizing: border-box; padding: 10px 18px; border-radius: 8px; background: var(--yc-green-dark); color: #fff;
    font-weight: 700; font-size: 14px; letter-spacing: 0.06em; text-transform: uppercase; text-align: center; }
  .attnCard:hover .attnCardCta { filter: brightness(1.08); }
  /* Round 13: Staff cards only -- the category icon sits centred in its column and centred against the text block */
  .attnCard.attnCardStaff { align-items: center; }
  .attnCardStaff .attnCardIcon { display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; padding-top: 0; color: var(--yc-green-dark); }
  .attnCardStaff .attnCardCta { justify-content: center; text-decoration: none; }
  .attnCard .attnLink { color: var(--yc-green-dark); font-weight: 700; text-decoration: underline; cursor: pointer; overflow-wrap: anywhere; }
  @media (max-width: 600px) { .attnCard { padding: 16px; column-gap: 12px; } .attnCardCta { align-self: stretch; min-width: 0; } }
  #app-shell.vp-mobile .attnCard { padding: 16px; column-gap: 12px; }
  #app-shell.vp-mobile .attnCardCta { align-self: stretch; min-width: 0; }
  /* Round 10 §19A: low-emphasis portal footer row */
  .portalFooterMinor { margin: 10px 0 0; font-size: 12px; color: var(--yc-muted); text-align: center; }
  .portalFooterMinor a { color: var(--yc-muted); text-decoration: underline; cursor: pointer; }
  a.serviceName { color: var(--yc-green-dark); text-decoration: underline; text-underline-offset: 2px; text-decoration-color: currentColor; cursor: pointer; }
  a.serviceName:hover { color: var(--yc-green); }
  a.serviceName:hover, a.serviceName:focus-visible { text-decoration-color: var(--yc-ink); }
  .ycqPrice { margin: 14px 0 0; font-weight: 700; font-size: 15px; }
  #screenRoot .ycqSec + .ycqSec, .ycqSec + .ycqSec { margin-top: 16px; }   /* Round 14 (notes 72): every section gap = the No preference -> Weekend gap */
  .ycqSec > .ycqSecH, .ycqSec > .ycqSecH.permBlockH, .ycqSec .permBlock > .ycqSecH.permBlockH { margin-bottom: 10px; }   /* Round 14: space under each Quote section heading (Agreed frequency, ...) */   /* Round 13: clear space ABOVE every price */
  /* Round 13: Weekend and Public Holiday Mowing Permission -- three plain lines, values on one indent */
  .permBlock { margin: 16px 0 0; }
  .permBlock .permBlockH { margin: 0 0 8px; }
  /* Round 14: the permission lines ARE a .kv list -- same label column, label style and value indent as the card above */
  dl[data-permission-lines] { margin: 0; }
  .permLine { display: contents; }
  .warnBox { background: var(--yc-amber-bg); border: 1px solid var(--yc-amber-border); color: var(--yc-amber-ink); }
  .schedulePanel { margin: 14px 0 0; padding: 14px 0 0; border-top: 1px solid var(--yc-border); }
  .cardBox > .permBlock:first-child, .cardBox > section.permBlock:first-child { margin-top: 0; }
  a.permEditLink { display: inline-block; margin-top: 6px; color: var(--yc-green-dark); }
  dd[data-perm-rows] > dl[data-perm-nested] { grid-template-columns: max-content minmax(0, 1fr); gap: 4px 18px; }
  .permBlock p.permNotBooking { margin: 10px 0 0; }
  /* Round 13: Current frequency / Change to -- per season, values on one indent */
  .freqChange { display: grid; gap: 22px; margin: 4px 0 18px; }   /* Round 14: seasons clearly apart */
  .freqChangeHead { margin: 0 0 8px; }
  .freqChangeLines .radioRow { display: flex; flex-wrap: wrap; gap: 6px 22px; margin: 0; }
  .freqChangeLines { display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: 24px; row-gap: 10px; align-items: start; }
  .fcValue .radioRow { margin-top: 0; }
  .fcLabel { color: var(--yc-muted); }
  .fcValue { font-weight: 600; min-width: 0; }
  .fcValue .radioRow { font-weight: 400; }
  .visuallyHidden { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  #screenRoot [hidden] { display: none !important; }   /* Round 13: a conditional field that is off is never shown, whatever its layout class */
  .ycqPrices { margin: 14px 0 0; }
  .ycqPrices dt + dd.ycqPrice, .ycqPrices dt { margin-top: 0; }
  .priceSpaced { margin-top: 14px !important; }
  .runSheet { margin: 14px 0 6px; }
  /* R19 Part B (N78-21): the Customer messages table */
  .commTableGap { height: 18px; }
  table.ycTable.commTable td { vertical-align: top; }
  .commTable .commEmailCell { text-align: right; white-space: nowrap; width: 1%; }
  .btnSm.btnXs { padding: 4px 10px; min-height: 0; font-size: 13px; line-height: 1.3; }
  #app-shell.vp-mobile .commTable .commEmailCell { text-align: left; width: auto; }
  @media (max-width: 600px) { .commTable .commEmailCell { text-align: left; width: auto; } }
  .billEditRow { margin: 6px 0 0; }
  /* R19 Part B (N78-28, notes 20/22/34/37/47): Job and service screen spacing */
  .jobActions { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 10px; margin: 14px 0; max-width: 600px; }
  .jobActions .btnSm { width: 100%; min-width: 0; box-sizing: border-box; justify-content: center; text-align: center; margin: 0; }
  .viewServiceRow { margin: 12px 0 16px; }
  .dayFacts { border: 1px solid var(--yc-border); border-radius: 8px; padding: 12px 16px; margin: 16px 0; }
  .dayFacts .subH { margin: 0 0 8px; }
  .dayFacts .kv dt, .dayFacts .kv dd { margin-bottom: 6px; }
  .dayFactsEdit { margin: 12px 0 0; }
  #screenRoot .cardBox[data-notes-about], #screenRoot p.help + .cardBox[data-notes-about] { margin-top: 24px !important; }   /* note 47: the shared card gap, also after a help line */
  [data-perm-readonly] { margin: 14px 0 10px !important; }
  .svcActRow { margin: 10px 0 14px; }
  .svcAction { margin: 0 0 18px; }
  .srAmber { background: var(--yc-amber-bg); border: 1px solid var(--yc-amber-border); color: var(--yc-amber-ink); }
  .srChoseWrote { border: 2px solid var(--yc-border); border-radius: 10px; padding: 10px 12px; margin: 10px 0 14px; background: #fbfcfa; }
  .srChoseWrote.srMismatch { border-color: var(--yc-amber-border); }
  .srChoseWrote .noteBox { margin: 10px 0 0; }
  .pendingWhen { font-weight: 400; }
  .runStart { margin: 10px 0 0; }
  .runStartLine { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; overflow-wrap: anywhere; }
  .runStartForm { margin: 8px 0 0; max-width: 520px; }
  .runRoute { margin: 12px 0 0; }
  .runRouteHelp { margin: 6px 0 0; }
  .runPick { margin: 8px 0 0; }
  .cardFocus { border-color: var(--yc-green-dark); box-shadow: 0 0 0 2px rgba(31,106,58,.18); }
  .runLink { color: var(--yc-green-dark); font-weight: 700; text-decoration: underline; cursor: pointer; }
  .runPick[hidden] { display: none; }
  .runPickForm { margin: 8px 0 0; max-width: 260px; }
  .runList { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 10px; }
  .runItem { border: 1px solid var(--yc-border); border-radius: 8px; padding: 10px 12px; }
  .runItem.runDone { background: var(--yc-green-bg, #eef7ef); }
  .runItem .crewNotes { margin: 8px 0 0; }
  .runComplete { min-width: 160px; justify-content: center; text-align: center; }
  #app-shell.vp-mobile .runComplete { width: 100%; }
  @media (max-width: 600px) { .runComplete { width: 100%; } }
  .nextMowDone { margin: 8px 0 0; }
  @media print {
    body.printRunSheet * { visibility: hidden !important; }
    body.printRunSheet [data-run-sheet], body.printRunSheet [data-run-sheet] * { visibility: visible !important; }
    body.printRunSheet [data-run-sheet] { position: absolute; left: 0; top: 0; width: 100%; border: 0; }
    body.printRunSheet [data-run-days], body.printRunSheet .runComplete { display: none !important; }
    body.printRunSheet [data-run-route], body.printRunSheet [data-run-route-help], body.printRunSheet [data-run-start-change], body.printRunSheet [data-run-start-form], body.printRunSheet [data-run-start-reset] { display: none !important; }   /* R19 Part B (D13): the start line and the order print; links don't */
    #chrome-sticky, #review-panel, #viewport-bar, #route-bar, .btnRow, form, [data-stripe-sim], .ownerDecisionNote { display: none !important; }
    .ycqPrice, .ycqPrices { margin-top: 18px !important; }
    .ycqOffer { break-inside: avoid; }
  }
  .ycqPrices dd.ycqPrice { margin: 0; }
  .ycqQual { font-weight: 400; color: var(--yc-muted, #5b6560); font-size: 13px; }
  .ycqDetail { display: grid; gap: 10px; margin: 12px 0 0; }
  .ycqSec { border-left: 3px solid var(--yc-line, #dde2dc); padding: 2px 0 2px 10px; }
  .ycqSecH { font-size: 14px; font-weight: 700; margin: 0 0 4px; }
  .ycqVal { margin: 0; font-size: 13.5px; }
  .ycqNote { margin: 4px 0 0 !important; }
  .ycqKv { margin: 0; }
  /* R19 Part B final (owner 13 Oct): How often -- two lines, frequencies in their own column, phone included */
  .ycqDetail dl.ycqKv[data-q="frequency"], #app-shell.vp-mobile .ycqDetail dl.ycqKv[data-q="frequency"] { grid-template-columns: max-content minmax(0, 1fr); column-gap: 18px; row-gap: 4px; }
  .ycqDetail dl.ycqKv[data-q="frequency"] dt, #app-shell.vp-mobile .ycqDetail dl.ycqKv[data-q="frequency"] dt { margin-top: 0; }
  /* Round 8 §C: contact with identity; the destructive action on its own line, set apart */
  .personContact { margin: 6px 0 4px; }
  .personDanger { margin: 10px 0 0; padding-top: 8px; border-top: 1px dashed var(--yc-border); }
  .periodTable { margin: 6px 0 0; }
  /* Round 8 §G */
  fieldset.requestLink { border: 1px solid var(--yc-border); border-radius: 8px; padding: 8px 12px; margin: 0 0 10px; }
  fieldset.requestLink legend { font-size: 13.5px; padding: 0 4px; }
  ul.plainList { margin: 0; padding-left: 18px; }
  /* R19 Part B: "GST summary" on Quotes -- the Lawn Estimator 0.16.29 .cc-gst table (owner-approved), website names */
  .ycGstH { margin: 12px 0 0; }
  .ycGst { margin: 10px 0 2px; border-collapse: collapse; font-size: 13.5px; font-variant-numeric: tabular-nums; width: auto; max-width: 100%; }
  .ycGst th, .ycGst td { padding: 4px 14px 4px 0; text-align: left; vertical-align: baseline; border: 0; width: auto; color: inherit; overflow-wrap: normal; background: none; }
  .ycGst tbody th, .ycGst tbody td { border-bottom: 1px solid #dcdcd4; }
  .ycGst tbody th { white-space: nowrap; }
  .ycGst tbody th, .ycGst thead td { width: 170px; min-width: 170px; padding-right: 12px; box-sizing: content-box; }
  .ycGst thead td { border: 0; }
  .ycGst thead th { color: #5b5b52; font-weight: 600; text-align: left; padding-right: 24px; }
  .ycGst tbody th { color: #5b5b52; font-weight: 600; }
  .ycGst td { text-align: left; white-space: normal; padding-right: 24px; }
  .ycGstWrap .ycGstAmt { white-space: nowrap; }
  .ycGst tr.ycGstTotal th, .ycGst tr.ycGstTotal td { border-top: 1px solid #1a1a1a; border-bottom: 0; font-weight: 700; color: #1a1a1a; }
  .ycGstStack { display: none; }
  .ycGstBlock { margin: 0; }
  .ycGstBh { margin: 0 0 2px; font-size: 13.5px; font-weight: 700; color: #1f1f1a; }
  .ycGstDl { display: grid; grid-template-columns: max-content minmax(0, 1fr); margin: 0; font-size: 13.5px; font-variant-numeric: tabular-nums; }
  .ycGstDl dt, .ycGstDl dd { margin: 0; padding: 4px 0; border-bottom: 1px solid #dcdcd4; }
  .ycGstDl dt { color: #5b5b52; font-weight: 600; white-space: nowrap; padding-right: 12px; }
  .ycGstDl dd { min-width: 0; }
  .ycGstDl .ycGstTotal { border-top: 1px solid #1a1a1a; border-bottom: 0; font-weight: 700; color: #1a1a1a; }
  @media (max-width: 480px) { .ycGstWrap .ycGst { display: none; } .ycGstStack { display: grid; gap: 12px; margin: 10px 0 2px; } }
  .plainList + .cardBox { margin-top: 14px; }   /* R19 Part B (note 59): the card after a list gets the card gap */
  .responseLines { margin: 0 0 14px; }   /* R19 Part B (N78-3/6): the hear-by line on its own line, a space above */
  .responseLines p { margin: 0; }
  .responseLines p + p, .responseLines .hearByLine { margin-top: 12px; }
  .nextSteps p { font-size: 15px; line-height: 1.5; color: var(--yc-ink); margin: 0 0 12px; }   /* R19 Part B (N78-5): body size */
  .nextSteps .responseLines { margin: 0 0 12px; }
  /* Round 8 §H: the failure sits beside the action that failed */
  .localError { color: var(--yc-red, #b42318); font-weight: 600; font-size: 13.5px; margin: 6px 0 0; outline: none; }
  /* Round 11 N10: date + time are one aligned pair -- same height, same top, labels on one line */
  .dtPair { display: flex; flex-wrap: wrap; gap: 0 12px; align-items: stretch; }
  .dtPair .fieldRow { flex: 0 1 190px; margin-bottom: 6px; display: flex; flex-direction: column; justify-content: flex-end; }
  .dtPair .fieldRow input { height: 38px; box-sizing: border-box; }
  .dtPair .fieldRow label { min-height: 16px; }
  #screenRoot .dtPair > .fieldRow + .fieldRow { margin-top: 0; }   /* the question-gap rule is for separate questions, not the two halves of one */
  .dtPair { row-gap: 10px; }
  .dtLegend { margin: 0 0 4px; font-size: 13.5px; }
  /* Round 8 addendum §2/§3: reusable navigation tiles + wide primary action */
  .navTileGrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; margin: 0 0 16px; }
  .navTile { display: flex; flex-direction: column; justify-content: space-between; align-items: center; gap: 6px; min-height: 92px; min-width: 0; padding: 12px 14px;
    background: #fff; border: 1px solid var(--yc-border); border-radius: 10px; text-decoration: none; color: inherit; cursor: pointer; text-align: center; }
  .navTile:hover, .navTile:focus-visible { border-color: var(--yc-green-dark); box-shadow: 0 0 0 2px rgba(31,106,58,.15); }
  .navTileLabel { width: 100%; font-size: 13.5px; color: var(--yc-muted); line-height: 1.3; overflow-wrap: anywhere; text-align: center; }
  .navTileCount { width: 100%; font-size: 22px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; text-align: center; }
  #app-shell.vp-mobile .navTileGrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .navTileGrid[data-nav-tiles], #app-shell.vp-mobile .navTileGrid[data-nav-tiles] { grid-template-columns: repeat(3, minmax(0, 1fr)); }   /* R17 (Q23): 9 tiles, even 3 x 3 */
  @media (max-width: 600px) { .navTileGrid[data-nav-tiles] { gap: 8px; } .navTileGrid[data-nav-tiles] .navTile { padding: 10px 6px; } }
  #app-shell.vp-mobile .navTileGrid[data-nav-tiles] { gap: 8px; } #app-shell.vp-mobile .navTileGrid[data-nav-tiles] .navTile { padding: 10px 6px; }
  .actionWideRow { margin: 0 0 14px; }
  .btnSm.actionWide { display: inline-flex; align-items: center; justify-content: center; width: min(100%, 520px); min-width: 0; box-sizing: border-box; font-size: 15px; }
  #app-shell.vp-mobile .btnSm.actionWide { display: flex; width: 100%; min-width: 0; box-sizing: border-box; }
  /* Round 8 addendum §9: the engagement timeline -- date | event on desktop, stacked on mobile */
  .timeline { list-style: none; margin: 0; padding: 0; }
  .tlItem { display: grid; grid-template-columns: 13.5rem minmax(0, 1fr); gap: 4px 16px; padding: 12px 0; border-top: 1px solid var(--yc-border); }   /* R19 Part B (N78-11/19): fits "Wednesday 30 September 2026" */
  .tlWhen { display: flex; flex-direction: column; font-variant-numeric: tabular-nums; }
  .tlDate { font-weight: 700; white-space: normal; overflow-wrap: anywhere; font-size: 13.5px; }   /* R19 Part B (N78-11/19): may wrap, never runs into the title */
  .tlWhen { min-width: 0; }
  .tlTime { color: var(--yc-muted); font-size: 13px; white-space: nowrap; }
  .tlBody { min-width: 0; }
  .tlHead { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
  .tlTitle { font-weight: 700; font-size: 14.5px; }
  .tlRef { color: var(--yc-green-dark); font-weight: 700; white-space: nowrap; font-size: 13px; }
  .tlSummary { margin: 4px 0 0; font-size: 13.5px; white-space: pre-wrap; overflow-wrap: anywhere; }
  .tlMeta { margin: 6px 0 0; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; font-size: 12.5px; color: var(--yc-muted); }
  .tlChip { border: 1px solid var(--yc-border); border-radius: 999px; padding: 1px 8px; background: #f6f7f5; color: var(--yc-ink, #1d2421); }
  .tlChipStatus { background: #fff; }
  .tlCorrected { margin: 6px 0 0; font-size: 12.5px; color: var(--yc-amber-ink, #7a4b00); overflow-wrap: anywhere; }
  .tlPrior { margin: 4px 0 0; overflow-wrap: anywhere; }
  .tlCorrect { margin-top: 6px; font-size: 13px; }
  #app-shell.vp-mobile .tlItem { grid-template-columns: minmax(0, 1fr); }
  #app-shell.vp-mobile .tlWhen { flex-direction: row; gap: 8px; }
  .commBody { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 14px; }
  .commPreview { border: 2px solid var(--yc-green-dark); }
  .previewLabel { margin: 0 0 6px; font-weight: 700; font-size: 13px; color: var(--yc-green-dark); }
  /* Round 8 §J */
  .actionDone { margin: -4px 0 12px; }
  details.evidenceDetails { margin: 8px 0 0; font-size: 13.5px; }
  /* Round 8 §M: a reference and its status badge never run together under/in a heading */
  h1.pageH .badge, h2.sectionH .badge { margin-left: 8px; vertical-align: middle; }
  h1.pageH { overflow-wrap: anywhere; }
  /* ---- Round 11 N05/N06: ONE page-heading block. The icon hangs in its own column; the heading
     text, its badge when it wraps, and every line under it start at the same margin. */
  .pageHead { --pi: 0px; margin: 0 0 14px; }
  .pageHead.hasIcon { --pi: 38px; }
  .pageHead > h1.pageH { position: relative; padding-left: var(--pi); margin: 0; }
  .pageHead > h1.pageH > svg.pageIcon { position: absolute; left: 0; top: 3px; top: calc(0.5lh - 0.575em); margin: 0; }   /* R14 A03: centred on line 1 */
  .pageHead > h1.pageH.headWithBadge { row-gap: 8px; }
  .pageHead > h1.pageH .badge { margin-left: 0; }
  .pageSub { color: var(--yc-muted); margin: 10px 0 0; }
  .pageHead > .pageSub { padding-left: var(--pi); }
  .pageHead > .pageSub + .pageSub { margin-top: 4px; }
  .sectionSub { margin: 2px 0 12px !important; }
  @media (max-width: 600px) { .pageHead.hasIcon { --pi: 34px; } .pageHead > h1.pageH > svg.pageIcon { width: 24px; height: 24px; top: 2px; } }
  #app-shell.vp-mobile .pageHead.hasIcon { --pi: 34px; }
  #app-shell.vp-mobile .pageHead > h1.pageH > svg.pageIcon { width: 24px; height: 24px; top: 2px; }

  /* R17 (A3): ONE tick-line style -- box and sentence side by side, box centred on the first line, wrapped
     text stays on the text margin, normal weight like the radio rows, room above and below */
  .fieldRow > label.tickLine, label.tickLine { display: flex; gap: 10px; align-items: flex-start; font-size: 13.5px; font-weight: 400; color: var(--yc-text, inherit); margin: 4px 0; line-height: 1.45; }
  label.tickLine > input[type=checkbox] { flex: 0 0 auto; width: 18px; height: 18px; margin: calc((1.45em - 18px) / 2) 0 0; }
  .radioRow label > input[type=radio], label.checkLine > input[type=radio] { flex: 0 0 auto; margin-top: calc((1.45em - 13px) / 2); }
  /* R17 (A4): in a read-only enquiry the permission heading is a sub-heading like the service name
     ("Regular Lawn Mowing"): same size and weight, with clear space above it */
  .permBlock.permSub { margin-top: 22px; }
  .permBlock.permSub > .permBlockH { font-size: 14px; font-weight: 700; margin: 0 0 8px; }
  .fieldRow.timingField > h2.sectionH.formGroupH, .formGroupH { margin: 26px 0 6px !important; }
  /* R17 (A7): short pages keep their footer at the bottom of the window, not halfway up */
  #screenRoot:has(> footer.pub-footer) { display: flex; flex-direction: column; min-height: calc(100vh - var(--yc-chrome-h, 0px)); }
  #screenRoot:has(> footer.pub-footer) > main { flex: 1 0 auto; width: 100%; box-sizing: border-box; }
  #screenRoot:has(> footer.pub-footer) > header, #screenRoot:has(> footer.pub-footer) > footer, #screenRoot:has(> footer.pub-footer) > .app-header { flex: 0 0 auto; }
  /* R17 (A11): on a phone each permission line gets the same gap as every other label/value row */
  @media (max-width: 600px) { dl[data-permission-lines] > .permLine:not(:first-child) > dt { margin-top: 10px; } }
  #app-shell.vp-mobile dl[data-permission-lines] > .permLine:not(:first-child) > dt { margin-top: 10px; }
  .owingLine { display: block; }   /* R17 (A15) */
  .owingLine.pipInNote { display: none; margin-bottom: 4px; }   /* R19 Part B (N78-26): phone copy of the badge */
  @media (max-width: 600px) { .acctLabel .pipInLabel { display: none; } .owingLine.pipInNote { display: block; } }
  #app-shell.vp-mobile .acctLabel .pipInLabel { display: none; } #app-shell.vp-mobile .owingLine.pipInNote { display: block; }

  /* R17: the mower lawn -- the uncut grass and the mower move together as ONE composited piece at one steady pace */
  .mowLawn { position: relative; height: 96px; border-radius: 8px; overflow: hidden; background: repeating-linear-gradient(90deg, #7cc35a 0 30px, #8fd06b 30px 60px); }
  .mowLawn.mowSmall { height: 78px; margin: 0 0 16px; }
  .mowLawn .mowLetters { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-weight: 900; font-size: 46px; letter-spacing: .14em; color: #b5e59a; }
  .mowLawn .mowLetters b { color: #d6f3c4; font-weight: 900; }
  .mowLawn .mowUncut { position: absolute; top: 0; bottom: 0; left: 0; width: 100%; background: #4f9a3a; transform: translate3d(0,0,0); will-change: transform; }
  .mowLawn .mowUncut::before { content: ""; position: absolute; inset: 0; background: repeating-linear-gradient(90deg, rgba(255,255,255,0.07) 0 3px, transparent 3px 7px); }
  .mowLawn.mowGo .mowUncut { animation: ycMowDrive var(--mow-dur, 5s) linear .4s forwards; backface-visibility: hidden; }
  .mowLawn.mowDone .mowUncut, .mowLawn:not(.mowGo):not(.mowDone) .mowUncut { display: none; }
  .mowLawn svg.mowSvg { position: absolute; bottom: 0; right: 100%; margin-right: -64px; width: 128px; height: auto; }   /* R17: preview 12 */
  .mowLawn.mowSmall svg.mowSvg { width: 104px; margin-right: -52px; }
  /* R19 Part B (mower jank): nothing inside the moving mower animates (no wheel / bob / clipping animations -- they
     could only repaint every frame), and the drive's keyframes read no variable: one composited transform per frame. */
  .mowClip { opacity: 0; }
  @keyframes ycMowDrive { to { transform: translate3d(calc(100% + 60px),0,0); } }
  .homeLawn { max-width: 720px; margin: 4px auto 22px; }
  /* owner's logo tick (preview 9): white card, no border or shadow, aspect ratio kept */
  .logoTickCard { background: #fff; border-radius: 12px; padding: 20px 16px; margin: 0 0 18px; }
  .logoTickCard svg { display: block; width: min(100%, 420px); height: auto; margin: 0 auto; }
  .logoTickCard.logoTickSmall svg { width: min(100%, 260px); }
  .logoTickCard .ltPen { stroke-dasharray: 126 400; stroke-dashoffset: 0; }   /* the tick's real length (126), not pathLength=1 */
  .logoTickCard.mowGo svg { animation: ycLtFade .3s ease-out both; }
  .logoTickCard.mowGo .ltPen { animation: ycLtDraw 1.4s cubic-bezier(.45,.05,.35,1) .5s both; }
  .logoTickCard.mowGo .ltEnds { animation: ycLtFade .25s ease-out 1.85s both; }
  @keyframes ycLtDraw { from { stroke-dashoffset: 127; } to { stroke-dashoffset: 0; } }
  @keyframes ycLtFade { from { opacity: 0; } to { opacity: 1; } }
  .logoTickCard:not(.mowGo) .ltDrawing { display: none; }   /* R19 Part B (OQ-5): still / finished / switched off = the plain tick */
  .logoTickCard.mowGo .ltDrawing { animation: ycLtSwapOut 2.1s step-end both; }
  .logoTickCard.mowGo .ltFinal { animation: ycLtSwapIn 2.1s step-end both; }
  @keyframes ycLtSwapOut { from { opacity: 1; } to { opacity: 0; } }
  @keyframes ycLtSwapIn { from { opacity: 0; } to { opacity: 1; } }
  @media (prefers-reduced-motion: reduce) { .logoTickCard svg, .logoTickCard .ltPen, .logoTickCard .ltEnds { animation: none !important; } .logoTickCard .ltDrawing { display: none !important; } .logoTickCard .ltFinal { animation: none !important; opacity: 1 !important; } }
  @media (max-width: 600px) { .mowLawn .mowLetters { font-size: 30px; } }
  #app-shell.vp-mobile .mowLawn .mowLetters { font-size: 30px; }
  @media (prefers-reduced-motion: reduce) { .mowLawn .mowUncut { display: none !important; } }
  .freqSeasons { display: inline-flex; flex-direction: column; gap: 3px; align-items: flex-start; }   /* R17 (Q19/A21): one edge, one line per season */
  .freqSeasons .freqWhen { color: var(--yc-muted); font-weight: 400; }
  .freqSeasons .freqNext { color: var(--yc-muted); }
  /* R19 Part B (D17b): the season that applies now is bold -- only when a second season is shown to contrast with */
  .freqSeasons:has(> .freqNext) > .freqNow { font-weight: 700; }
  /* R19 Part B (N78-4 + note 30): Quote accepted -- destination buttons equal width like .ctaPair; text above its button */
  [data-accepted-destinations] { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; max-width: 560px; justify-content: start; }
  [data-accepted-destinations]:has(> :only-child) { grid-template-columns: minmax(0, 1fr); max-width: 275px; }
  [data-accepted-destinations] > * { width: 100%; box-sizing: border-box; text-align: center; justify-content: center; margin: 0 !important; }
  .acceptedNote { display: block; }
  .acceptedNote > a.btnSm { display: table; margin-top: 10px; }
  /* R19 Part B (notes 29/30): My details next steps -- label on its own row, the button below; buttons one size */
  dl.nextSteps { display: grid; grid-template-columns: minmax(0, max-content); gap: 6px 0; margin: 14px 0 0; font-size: 13.5px; }
  dl.nextSteps dt { color: var(--yc-muted); font-weight: 600; }
  dl.nextSteps dt:not(:first-child) { margin-top: 10px; }
  dl.nextSteps dd { margin: 0; }
  dl.nextSteps dd > .btnSm { display: block; width: 100%; box-sizing: border-box; text-align: center; }
  @media (max-width: 600px) { dl.nextSteps { grid-template-columns: minmax(0, 1fr); } }
  #app-shell.vp-mobile dl.nextSteps { grid-template-columns: minmax(0, 1fr); }
  /* R19 Part B (N78-27): phone tables -- the label on the left, everything else together on the right, one line */
  #app-shell.vp-mobile table.ycTable td:not(:is([data-label="Property"], [data-label="Address"], [data-label="Mowing address"], [data-label="Detail"]))::before { margin-right: auto; }
  #app-shell.vp-mobile table.ycTable td > .badge + .pendingChange { margin-top: 0; }
  @media (max-width: 600px) { table.ycTable td:not(:is([data-label="Property"], [data-label="Address"], [data-label="Mowing address"], [data-label="Detail"]))::before { margin-right: auto; } table.ycTable td > .badge + .pendingChange { margin-top: 0; } }
  /* R19 Part B (note 5 / R17-A4): one spacing rule for every form group heading block (How often, Weekend and Public Holiday) */
  .formGroupBlock { border: none; border-top: 1px solid var(--yc-border); padding: 20px 0 0; margin: 18px 0 0; }
  .formGroupBlock > h2.sectionH.formGroupH { margin-top: 0 !important; }
  .propIdentity { margin-bottom: 16px; }
  details.provenanceDetails { margin-top: 8px; font-size: 13.5px; }

/* ---------- Phase 1 website (added to the Round 20 stylesheet) ---------- */
/* A real phone is not the prototype's 390px preview frame: full width, no side borders. */
#app-shell.vp-mobile { max-width: none; border-left: 0; border-right: 0; }
/* The hidden "honeypot" box: robots fill it in, people never see it. */
.p1hp { position: absolute !important; left: -10000px !important; top: auto !important; width: 1px !important; height: 1px !important; overflow: hidden !important; }
/* Cloudflare's spam check: usually invisible; when it does need a click it sits above Send. */
#p1-turnstile:not(:empty) { margin: 12px 0 0; display: flex; justify-content: flex-end; }
/* Owner decision 18 Oct 2026: the header logo was too small to read "MAKING THE GRADE".
   Laptop and tablet 1.6 times (70px tall), phone 1.3 times (57px tall). The proportions stay the same. */
svg.ycLogo { height: 70px; width: calc(70px * 700 / 190); }
@media (max-width: 600px) { svg.ycLogo { height: 57px; width: calc(57px * 700 / 190); } }
