/* RPP Central component styles. Values come from tokens.css; see docs/DESIGN.md.
   The few app-level constants tokens.css does not define are named once here. */

:root {
  --rail-w: 76px;
  --rail-link: 44px;
  --rail-drawer-w: 264px;    /* the rail opened as a drawer on a phone */
  --topbar-h: 66px;
  --control-h-xs: 32px;      /* segmented items, icon buttons over photos */
  --control-h-chip: 34px;    /* filter chips and topbar controls */
  --pill-h: 26px;            /* status pills */
  --tag-h: 22px;             /* tags, the callout icon, the product badge */
  --inset-control: 14px;     /* side inset of fields and filter chips */
  --inset-pill: 10px;        /* dot-side inset of status pills; tags */
  --gap-control: 10px;       /* icon to label in buttons */
  --segment-gap: 2px;
  --toggle-w: 40px;
  --toggle-h: 22px;
  --toggle-thumb: 18px;
  --avatar-20: 20px; --avatar-24: 24px; --avatar-28: 28px; --avatar-32: 32px; --avatar-36: 36px; --avatar-40: 40px; --avatar-56: 56px; --avatar-64: 64px;
  --hairline: 1px;
  --dot: 6px;                /* event-row dots, booking progress and day dots */
  --pill-dot: 8px;
  --swatch: 10px;
  --divider-w: 28px;
  --cal-hour-h: 52px;        /* an hour of the calendar's time grid */
  --cal-col-min: 88px;       /* a time grid column; below it the grid scrolls inside its card */
  --cal-event-min: 78px;     /* a time grid entry, room for "12:45pm", or a whole hour and a Google entry's EXT ("12pm EXT"): narrower side by side, entries overlap instead (Week), or the column widens (Day); with a column's insets no more than --cal-col-min */
  --event-title-min: 4ch;    /* the least of an entry's title worth showing beside its time, "7 L…"; with less room it drops out */
  --cal-day-min-h: 112px;    /* a day of the calendar's month view */
  --mini-day-h: 28px;        /* a day of the calendar's mini month */
  --filters-w: 300px;        /* the calendar's Filters panel */
  --event-bar-w: 3px;
  --tip-notch: 7px;
  --thumb-inset: 2px;
  --agenda-time-w: 56px;
  --file-tile-min: 200px;
  --empty-desc-max: 360px;
  --title-max: 880px;
  --logo-h: 36px;            /* public header lock-up; minimum on screen is 24px */
  --public-w: 1040px;        /* a public page's content, the delivery board's 1440 less its 200px sides */
  --lede-max: 520px;         /* the delivery header's sentence, on two lines as on its board */
  --side-panel-w: 320px;
  --settings-nav-w: 240px;
  --section-head-min: 320px; /* a settings card head's title and line, before the card's actions wrap under it */
  --integration-tile-min: 280px;  /* an Integrations tile: three side by side beside the settings nav from 1280px */
  --book-bar-h: 64px;
  --book-actions-h: 84px;
  --wizard-bar-h-fallback: 136px;  /* Upload to Editor's pinned bar with its buttons wrapped, until wizard_bar_controller.js sets --wizard-bar-h */
  --book-pill-w: 28px;
  --book-w-narrow: 560px;
  --book-w-mid: 768px;
  --book-w-wide: 1024px;
  --slot-min-w: 96px;
  --hatch: 6px;
  --places-max-h: 280px;
  --unit-input-w: 120px;
  --bill-select-min: 240px;  /* the Billing tab's product select, before Custom line wraps under it */
  --profile-text-min: 240px; /* the customer page header's name and contact line, before its actions wrap under it */
  --customer-lifetime-w: 184px;  /* the Customers list's fixed columns: Lifetime value fits its header and a seven-figure average */
  --customer-last-shoot-w: 232px; /* Last shoot fits a lapsed date and how long ago on one line */
  --member-role-w: 140px;        /* Settings › Team's fixed Role and Status columns, as drawn */
  --member-status-w: 110px;
  --time-select-w: 150px;
  --product-preview-w: 240px;
  --breakdown-title-w: 140px;    /* a Job reports breakdown's title column, "Orders by status" */
  --report-panel-h: 260px;       /* Revenue overview's dashed panel in the income chart's place, without Xero */
  --overlay-ink: color-mix(in srgb, var(--rpp-grey-900) 94%, transparent);
  --overlay-line: color-mix(in srgb, var(--color-surface) 22%, transparent);
  --overlay-chip: color-mix(in srgb, var(--color-surface) 50%, transparent);
  --overlay-scrim: color-mix(in srgb, var(--rpp-grey-900) 45%, transparent);  /* behind a dialog */
  --overlay-tile: color-mix(in srgb, var(--color-surface) 8%, transparent);  /* the lightbox's icon tiles */
  --overlay-cover: color-mix(in srgb, var(--rpp-grey-900) 85%, transparent);  /* the delivery header's cover: white and grey-300 type stay legible over a bright sky */
  --book-dialog-w: 640px;
  --confirm-dialog-w: 480px;
  --area-map-h: 420px;
  --area-overview-h: 520px;  /* Settings › Service area's map of every area, beside the area cards */
  --area-cards-w: 300px;     /* an area card there: one column of them beside that map, as many as fit without it */
  --place-map-h: 240px;      /* Create job's map of the picked address */
  --run-map-h: 820px;        /* the dashboard's Run mode map, at its shortest, beside the run card */
  --run-map-h-narrow: 520px; /* the same map over the card, below 1200px */
  --run-card-w: 460px;       /* Run mode's card, The run · today */
  --run-line-w: 4px;         /* a run's line on that map, read by run_map_controller.js */
  --area-pick: 28px;         /* a colour choice in the area picker */
  --area-ring: 2px;          /* gap and ring around the chosen colour */
  --check-list-max-h: 220px;
  --tab-underline: 2px;      /* the active underline tab */
  --status-property-min: 176px;  /* Order status grid columns: a cell fits the widest status select, "Not started" */
  --status-date-w: 72px;
  --status-notes-w: 48px;
  --status-cell-min: 124px;
  --task-tile-min: 160px;        /* a Project status tile fits its name and the widest status, "Not started", with its chevron */
  --cover-w: 200px;              /* the job page's cover beside the title */
  --notifications-w: 400px;      /* the bell's panel */
  --badge-h: 18px;               /* the bell's unread count */
  --search-w: 480px;             /* the topbar's search field */
  --search-panel-w: 560px;       /* its matches, under it */
  --chart-line: 2px;             /* a chart's lines and their legend keys */
  --field-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2382878C' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");  /* a select's and a time field's trailing chevron, grey-600 */
  --field-calendar: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2382878C' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2v4'/%3E%3Cpath d='M16 2v4'/%3E%3Crect width='18' height='18' x='3' y='4' rx='2'/%3E%3Cpath d='M3 10h18'/%3E%3C/svg%3E");  /* a date field's leading calendar, grey-600 */
  --field-clock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2382878C' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpolyline points='12 6 12 12 16 14'/%3E%3C/svg%3E");  /* a time field's leading clock, grey-600 */
}

*, *::before, *::after { box-sizing: border-box; }
html { font-family: var(--font-sans); font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-light); color: var(--color-text-body); background: var(--color-bg); -webkit-font-smoothing: antialiased; }
body { margin: 0; min-height: 100vh; }
a { color: var(--color-primary); text-decoration: none; }
a:hover { color: var(--color-primary-hover); }
a:not([class]) { font-weight: var(--weight-medium); }  /* a text link is orange Medium; classed links style themselves */
h1, h2, h3, p, ul, ol { margin: 0; }
h1, h2, h3 { color: var(--color-text); }  /* headings are ink; body copy is grey-800 */
button, input, select, textarea { font: inherit; color: inherit; }
strong, b { font-weight: var(--weight-medium); }  /* the brand has no bold; never let the browser fake one */
img { max-width: 100%; height: auto; }  /* an image never pushes past its column; sized images set their own height */
.icon { flex: none; vertical-align: middle; }
[hidden] { display: none !important; }

/* ---------- Shell ---------- */
.shell { display: flex; min-height: 100vh; }
.rail { width: var(--rail-w); flex: none; display: flex; flex-direction: column; align-items: center; padding: var(--space-4) 0; background: var(--color-surface); border-right: var(--hairline) solid var(--color-border); position: sticky; top: 0; height: 100vh; z-index: 6; }  /* over Upload to Editor's pinned bar, so a rail tip shows */
.rail-logo img { height: var(--space-8); width: auto; display: block; }
.rail-nav { display: flex; flex-direction: column; align-items: center; gap: var(--space-1); margin-top: var(--space-4); flex: 1; }
.rail-divider { width: var(--divider-w); height: var(--hairline); background: var(--color-border); margin: var(--space-2) 0; }
.rail-foot { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); }
.rail-foot form { margin: 0; }
.rail-link { position: relative; display: flex; align-items: center; justify-content: center; width: var(--rail-link); height: var(--rail-link); border-radius: var(--radius-tile); color: var(--color-text-muted); background: transparent; border: 0; cursor: pointer; transition: background var(--duration-fast) var(--easing-standard), color var(--duration-fast) var(--easing-standard); }
.rail-link:hover { background: var(--color-surface-alt); color: var(--color-text); }
.rail-link.active { background: var(--color-primary); color: var(--color-text-inverse); box-shadow: var(--shadow-glow); }
.rail-link.active:hover { background: var(--color-primary-hover); color: var(--color-text-inverse); }
.rail-link:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.rail-tip { position: absolute; left: calc(100% + 14px); top: 50%; transform: translateY(-50%) translateX(-4px); background: var(--rpp-grey-900); color: var(--color-text-inverse); font-size: var(--text-xs); font-weight: var(--weight-medium); line-height: var(--lh-xs); padding: var(--space-1) var(--space-2); border-radius: var(--radius-card); box-shadow: var(--shadow-md); white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity var(--duration-fast) var(--easing-standard), transform var(--duration-fast) var(--easing-standard); z-index: 20; }
.rail-tip::before { content: ""; position: absolute; left: calc(-1 * var(--tip-notch) / 2); top: 50%; width: var(--tip-notch); height: var(--tip-notch); margin-top: calc(-1 * var(--tip-notch) / 2); background: inherit; transform: rotate(45deg); }
.rail-link:hover .rail-tip, .rail-link:focus-visible .rail-tip { opacity: 1; transform: translateY(-50%) translateX(0); }
.shell-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.rail-toggle, .rail-scrim { display: none; }
/* Phone: the rail hides behind a menu button in the topbar and slides in as a drawer with its labels shown. */
@media (max-width: 600px) {
  .rail { position: fixed; z-index: 40; left: 0; top: 0; bottom: 0; height: auto; width: var(--rail-drawer-w); align-items: stretch; padding: var(--space-4) var(--space-3); overflow-y: auto; box-shadow: var(--shadow-lg); transform: translateX(-100%); visibility: hidden; transition: transform var(--duration-normal) var(--easing-standard), visibility 0s linear var(--duration-normal); }
  .rail-open .rail { transform: none; visibility: visible; transition: transform var(--duration-normal) var(--easing-standard); }
  .rail-scrim { position: fixed; z-index: 39; inset: 0; background: var(--overlay-scrim); }
  .rail-open .rail-scrim { display: block; }
  .rail-logo { padding: 0 var(--space-3); }
  .rail-nav, .rail-foot { align-items: stretch; }
  .rail-divider { width: auto; margin: var(--space-2) var(--space-3); }
  .rail-link { width: auto; justify-content: flex-start; gap: var(--space-3); padding: 0 var(--space-3); }
  .rail-tip { position: static; opacity: 1; transform: none; background: none; color: inherit; box-shadow: none; padding: 0; font-size: var(--text-sm); line-height: var(--lh-sm); }
  .rail-tip::before { display: none; }
  .rail-link:hover .rail-tip, .rail-link:focus-visible .rail-tip { transform: none; }
  .rail-foot > .avatar { margin-left: var(--space-3); }
  .rail-foot form .rail-link { width: 100%; }
  .rail-toggle { display: inline-flex; align-items: center; justify-content: center; width: var(--control-h-chip); height: var(--control-h-chip); margin-left: calc(-1 * var(--space-2)); padding: 0; border: 0; border-radius: var(--radius-tile); background: transparent; color: var(--color-text); cursor: pointer; flex: none; }
  .rail-toggle:hover { background: var(--color-surface-alt); }
  .rail-toggle:focus-visible { outline: none; box-shadow: var(--focus-ring); }
}
.topbar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); height: var(--topbar-h); padding: 0 var(--space-8); border-bottom: var(--hairline) solid var(--color-border); background: var(--color-surface); }
.eyebrow { font-size: var(--text-xs); line-height: var(--lh-eyebrow); font-weight: var(--weight-medium); letter-spacing: var(--tracking-eyebrow); text-transform: uppercase; color: var(--color-primary); }
.topbar-eyebrow { font-size: var(--text-xs); line-height: var(--lh-eyebrow); font-weight: var(--weight-medium); letter-spacing: var(--tracking-eyebrow); text-transform: uppercase; color: var(--color-text-subtle); white-space: nowrap; flex: none; }  /* the workspace chip gives way first; only a narrow phone shortens it */
.topbar-chip { display: flex; align-items: center; gap: var(--space-2); max-width: 100%; height: var(--control-h-chip); padding: 0 var(--space-2) 0 var(--space-1); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-pill); background: var(--color-surface); box-shadow: var(--shadow-sm); font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--color-text); min-width: 0; list-style: none; cursor: pointer; transition: border-color var(--duration-fast) var(--easing-standard); }  /* the account menu's summary; the avatar sits inside the pill's curve */
.topbar-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }  /* a long workspace name shortens rather than wrapping on a phone */
/* The + menu, the bell and the account menu (the workspace chip), each a details.menu; only the chip shrinks, and a long name never
   grows it past the side panel's width. */
.topbar-actions { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.topbar-actions > .menu { flex: none; }
.topbar-actions > .account-menu { flex: 0 1 auto; min-width: 0; max-width: var(--side-panel-w); }
.topbar-icon { position: relative; display: inline-flex; align-items: center; justify-content: center; width: var(--control-h-chip); height: var(--control-h-chip); border-radius: var(--radius-pill); color: var(--color-text); list-style: none; cursor: pointer; transition: background var(--duration-fast) var(--easing-standard); }
.topbar-icon::-webkit-details-marker, .topbar-chip::-webkit-details-marker { display: none; }
.topbar-icon:hover, .menu[open] > .topbar-icon { background: var(--color-surface-alt); }
.topbar-icon:focus-visible, .topbar-chip:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.topbar-badge { position: absolute; top: calc(-1 * var(--space-1)); right: calc(-1 * var(--space-1)); min-width: var(--badge-h); height: var(--badge-h); padding: 0 var(--space-1); border-radius: var(--radius-pill); background: var(--color-primary); color: var(--color-text-inverse); font-size: var(--text-xs); line-height: var(--badge-h); font-weight: var(--weight-medium); font-variant-numeric: tabular-nums; text-align: center; }
.topbar-chip:hover, .menu[open] > .topbar-chip { border-color: var(--color-border-strong); }
.topbar-chip > .icon { color: var(--color-text-subtle); }
.dot { display: inline-block; width: var(--dot); height: var(--dot); border-radius: var(--radius-circle); background: var(--color-primary); flex: none; }
.page { padding: var(--space-8); width: 100%; }  /* fills the space beside the rail at every screen width */
/* Below 960px the topbar's menus open under the whole group, at its right edge, so the bell's wide panel never runs off the left or
   over the rail, and fit a phone. */
@media (max-width: 960px) {
  .topbar-actions { position: relative; }
  .topbar-actions > .menu { position: static; }
  .topbar-actions .menu-panel { max-width: calc(100vw - 2 * var(--space-4)); }
}
@media (max-width: 600px) {
  .topbar, .page { padding-left: var(--space-4); padding-right: var(--space-4); }
  .page { padding-top: var(--space-6); }
  .topbar { justify-content: flex-start; gap: var(--space-2); }
  .topbar-actions { flex: none; margin-left: auto; }  /* the menus keep their size; on a narrow phone the eyebrow shortens instead */
  .topbar-eyebrow { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  .topbar-chip-name { display: none; }  /* the chip is the avatar and chevron; the account menu names the organisation */
}

/* ---------- Topbar search (⌘K) ---------- */
/* The field (.search's shell) sits after the section eyebrow, the ⌘K hint inside it until something is typed; the matches drop
   down under it on a panel: the chips, then the chosen chip's first matches, highlighted as the keys or the mouse move. */
.topbar-search { position: relative; flex: 0 1 var(--search-w); min-width: 0; margin-right: auto; }
.search-open { display: none; padding: 0; border: 0; background: none; }  /* the phone's button; .topbar-icon draws it */
.search-box { position: relative; background: var(--color-surface); }
.search-field .input { padding-right: var(--space-16); }  /* room for the hint, "Ctrl K" the widest */
.search-field .input:not(:placeholder-shown) { padding-right: var(--inset-control); }  /* typing: the clear button sits at the field's edge */
.search-field .input::-webkit-search-cancel-button { cursor: pointer; }
.search-kbd { position: absolute; right: var(--space-3); top: 50%; transform: translateY(-50%); padding: 0 var(--space-1); border: var(--hairline) solid var(--color-border-strong); border-radius: var(--radius-field); font-family: inherit; font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text-subtle); pointer-events: none; }
.search-field .input:not(:placeholder-shown) ~ .search-kbd { display: none; }  /* the field's clear button takes its place */
.search-panel { position: absolute; top: calc(100% + var(--space-1)); left: 0; z-index: 30; width: max(100%, var(--search-panel-w)); max-height: calc(100vh - var(--topbar-h) - var(--space-6)); overflow-y: auto; padding: var(--space-3); background: var(--color-surface); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); box-shadow: var(--shadow-pop); }
.search-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); padding-bottom: var(--space-3); }
.search-list { display: flex; flex-direction: column; }
.search-row { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-3); border-radius: var(--radius-field); color: var(--color-text); cursor: pointer; }
.search-row:hover { color: var(--color-text); }
.search-row.is-active { background: var(--color-surface-alt); }  /* hovering moves the highlight, so only one shows */
.search-row-text { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.search-row-title, .search-row-meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.search-row-title { font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text); }
.search-row-meta { font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); }
.search-show-all, .search-show-all:hover { gap: var(--space-2); font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-primary); }
.search-show-all.is-active { color: var(--color-primary-hover); }
.search-none { padding: var(--space-2) var(--space-3); font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--color-text-muted); }
.search-panel .empty { padding: var(--space-6) var(--space-4); }
/* Below 960px the panel opens under the whole topbar, clear of the rail and the menus; beside the search the workspace chip is
   the avatar and chevron, as on a phone, and the menus keep their size while the field gives way. */
@media (max-width: 960px) {
  .topbar { position: relative; }
  .topbar-search, .search-box { position: static; }
  .search-panel { top: calc(100% - var(--space-2)); left: var(--space-8); right: var(--space-8); width: auto; }
  .topbar-search ~ .topbar-actions { flex: none; }
  .topbar-search ~ .topbar-actions .topbar-chip-name { display: none; }
}
/* On a phone the field folds into a button beside the menus, 4px apart so it fits beside the longest eyebrow; opened, it lies
   over the topbar with the panel under it. A match's text wraps instead of its icon taking the room. */
@media (max-width: 600px) {
  .topbar:has(.topbar-search), .topbar-search ~ .topbar-actions { gap: var(--space-1); }
  .topbar-search { flex: none; margin-left: auto; margin-right: 0; }
  .topbar-search ~ .topbar-actions { margin-left: 0; }
  .search-open { display: inline-flex; }
  .search-box { display: none; }
  .topbar-search.is-expanded .search-box { display: block; position: absolute; z-index: 31; top: calc((var(--topbar-h) - var(--control-h-md)) / 2); left: var(--space-4); right: var(--space-4); }
  .search-kbd { display: none; }
  .search-field .input { padding-right: var(--inset-control); }
  .search-panel { top: calc(100% + var(--space-1)); left: 0; right: 0; }
  .search-row .search-row-icon { display: none; }
  .search-row-title, .search-row-meta { white-space: normal; overflow-wrap: anywhere; }
}

/* ---------- Page header ---------- */
/* The ancestors are text links (orange, orange-900 on hover), the current page grey-800. */
.breadcrumb { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text-subtle); margin-bottom: var(--space-2); }
.breadcrumb .icon { color: var(--color-text-disabled); }
.breadcrumb-current { color: var(--color-text-muted); font-weight: var(--weight-medium); }
.record-header > .breadcrumb { margin-bottom: var(--space-5); }  /* the customer page's header card stands clear of it */
.page-header { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--space-4) var(--space-6); margin-bottom: var(--space-6); }  /* actions drop under the title when they do not fit beside it */
.page-header > div:first-child { min-width: 0; }
.page-header .eyebrow { margin-bottom: var(--space-2); }
.page-title-row { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.page-title { font-weight: var(--weight-light); font-size: var(--text-h3); line-height: var(--lh-h3); letter-spacing: var(--tracking-heading); }
.page-title-greeting { font-size: var(--text-h2); line-height: var(--lh-h2); }
.page-subtitle { color: var(--color-text-body); font-size: var(--text-base); line-height: var(--lh-base); margin-top: var(--space-2); display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.page-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
.page-header .page-actions, .record-card-bar > .page-actions, .record-actions { gap: var(--space-3); }  /* a header's actions and a card head's (Editing's count beside Add option); a row's (Team) keep 8px */
.page-actions form { margin: 0; }

/* ---------- Layout helpers ---------- */
.stack { display: flex; flex-direction: column; gap: var(--space-4); }
.stack-sm { display: flex; flex-direction: column; gap: var(--space-2); }
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-6); }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); }
.detail { display: grid; grid-template-columns: minmax(0, 1fr) var(--side-panel-w); gap: var(--space-6); align-items: start; }
@media (max-width: 960px) { .grid-2, .grid-3, .detail { grid-template-columns: minmax(0, 1fr); } }
.muted { color: var(--color-text-muted); }
.hint { color: var(--color-text-subtle); font-size: var(--text-xs); line-height: var(--lh-xs); }
.num { font-variant-numeric: tabular-nums; }
.prewrap { white-space: pre-wrap; }

/* ---------- Card ---------- */
.card { background: var(--color-surface); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); box-shadow: var(--shadow-card); padding: var(--space-6); }
.card-flush { padding: 0; overflow: hidden; }
.card:has(.table):not(:has(.menu)) { overflow-x: auto; }  /* a table wider than a phone scrolls inside its card; a row menu would be clipped, so its card keeps the page scroll */
.card-flush > .empty { padding: var(--space-10) var(--space-6); }
.card-title { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); font-size: var(--text-lg); line-height: var(--lh-lg); font-weight: var(--weight-light); letter-spacing: var(--tracking-heading); margin-bottom: var(--space-4); }
.card-title .icon { color: var(--color-text-subtle); }
.card-title .pill { margin-left: var(--space-1); }
.card-title .count { font-size: var(--text-sm); font-weight: var(--weight-light); }  /* a count reads at its label's size, Light, as on the filter chips */
.count { font-size: var(--text-xs); font-weight: var(--weight-medium); letter-spacing: 0; color: var(--color-text-subtle); font-variant-numeric: tabular-nums; }

/* ---------- Buttons ---------- */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--gap-control); height: var(--control-h-md); padding: 0 var(--space-5); border-radius: var(--radius-button); border: var(--hairline) solid transparent; font-size: var(--text-sm); font-weight: var(--weight-medium); line-height: 1; cursor: pointer; white-space: nowrap; transition: background var(--duration-fast) var(--easing-standard), border-color var(--duration-fast) var(--easing-standard), color var(--duration-fast) var(--easing-standard), transform var(--duration-normal) var(--easing-standard); text-decoration: none; }
.btn:active:not(:disabled) { transform: scale(var(--press-scale)); }
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.btn:disabled { cursor: default; }
.btn-sm { height: var(--control-h-sm); padding: 0 var(--space-3); font-size: var(--text-sm); }
.btn-lg { height: var(--control-h-lg); padding: 0 var(--space-8); font-size: var(--text-base); }
.btn-block { width: 100%; }
.btn-icon { width: var(--control-h-md); height: var(--control-h-md); padding: 0; }
.btn-icon.btn-sm { width: var(--control-h-sm); height: var(--control-h-sm); }
.btn-primary { background: var(--color-primary); color: var(--color-text-inverse); box-shadow: var(--shadow-glow); }
.btn-primary:hover { background: var(--color-primary-hover); color: var(--color-text-inverse); }
.btn-primary:disabled { background: var(--rpp-orange-100); color: var(--color-text-inverse); box-shadow: none; }
.btn-secondary { background: var(--color-surface); color: var(--color-text); border-color: var(--rpp-orange-100); box-shadow: var(--shadow-sm); }
.btn-secondary:hover { background: var(--color-surface-alt); color: var(--color-text); border-color: var(--rpp-orange-200); }
.btn-secondary:disabled { color: var(--color-text-disabled); border-color: var(--color-border); box-shadow: none; }
.btn-secondary .icon:last-child:not(:only-child) { color: var(--color-primary); }  /* the trailing arrow is orange, as in the brand's secondary button */
/* A secondary button that is the current choice of a pair (the calendar's Shared and By photographer): pressed, without the glow. The
   chosen choice chip (below, under Forms) and the delivery page's shown Includes: chip (under Public pages) are pressed the same way. */
.btn-secondary[aria-current="true"], .btn-secondary[aria-current="true"]:hover, .choice-chip:has(:checked), .choice-chip:has(:checked):hover, .delivery-chip[aria-pressed="true"] { background: var(--color-primary-tint); border-color: var(--color-primary); color: var(--rpp-orange-900); }
.btn-ghost { background: transparent; color: var(--color-text-muted); }
.btn-ghost:hover { background: var(--color-surface-alt); color: var(--color-text); }
.btn-danger { background: var(--color-surface); color: var(--color-primary-hover); border-color: var(--rpp-orange-200); }
.btn-danger:hover { background: var(--rpp-orange-100); color: var(--color-primary-hover); }
/* On the one dark field, the delivery page's header. */
.btn-inverse { background: transparent; color: var(--color-text-inverse); border-color: var(--overlay-line); }
.btn-inverse:hover, .menu[open] > .btn-inverse { background: var(--overlay-line); color: var(--color-text-inverse); }
.btn-inverse:disabled { background: transparent; color: var(--overlay-chip); }
summary.btn { list-style: none; }
summary.btn::-webkit-details-marker { display: none; }

/* ---------- Forms ---------- */
.field { display: flex; flex-direction: column; gap: var(--space-2); }
.label { font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text); }
.input, .select { height: var(--control-h-md); padding: 0 var(--inset-control); border: var(--hairline) solid var(--color-border-strong); border-radius: var(--radius-field); background: var(--color-surface); box-shadow: var(--shadow-sm); width: 100%; font-size: var(--text-sm); color: var(--color-text); transition: border-color var(--duration-fast) var(--easing-standard), box-shadow var(--duration-fast) var(--easing-standard); }
.input:focus, .select:focus { outline: none; border-color: var(--color-primary); box-shadow: var(--focus-ring); }
.input::placeholder { color: var(--color-text-subtle); }
.input-sm { height: var(--control-h-sm); font-size: var(--text-sm); }
/* A value shown in a field's shell but not typed there, as the Schedule
   appointment dialog's worked-out duration; a focusable one keeps its ring. */
.input-static, .appointment-form .input[readonly] { background: var(--color-surface-alt); color: var(--color-text-muted); border-color: var(--color-border); box-shadow: none; }
.input-static { display: flex; align-items: center; }
.appointment-form .input[readonly]:focus { border-color: var(--color-primary); box-shadow: var(--focus-ring); }
.textarea { height: auto; padding: var(--space-2) var(--inset-control); resize: vertical; line-height: var(--lh-sm); }
.textarea-fit { field-sizing: content; }  /* grows with what is typed; a browser without field-sizing keeps the rows it is drawn with */
.select { appearance: none; padding-right: var(--space-10); background-image: var(--field-chevron); background-repeat: no-repeat; background-position: right var(--inset-control) center; }
/* A labelled select (the Customers list's Sort, Job reports' filters): one field shell, its Light grey-800 label then the bare select
   in Medium, as wide as its choice, ringed while the select has focus; the select runs to the shell's end, so a press there opens it. */
.select-field { display: inline-flex; align-items: center; gap: var(--space-2); max-width: 100%; height: var(--control-h-md); padding-left: var(--inset-control); border: var(--hairline) solid var(--color-border-strong); border-radius: var(--radius-field); background: var(--color-surface); box-shadow: var(--shadow-sm); font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--color-text-muted); transition: border-color var(--duration-fast) var(--easing-standard), box-shadow var(--duration-fast) var(--easing-standard); }
.select-field:focus-within { border-color: var(--color-primary); box-shadow: var(--focus-ring); }
.select-field .select, .select-field .select:focus { height: 100%; min-width: 0; padding-left: 0; border: 0; box-shadow: none; background-color: transparent; font-weight: var(--weight-medium); field-sizing: content; }
/* A date or time field: a leading grey-600 calendar or clock, as search's icon, and on a time the select's chevron. The browser's
   own picker button is stretched, unseen, over the whole field, so a press anywhere opens its picker (Chrome and Edge; Safari has no
   button, Firefox keeps its own). */
.input:is([type="date"], [type="time"]) { position: relative; padding-left: var(--space-10); background-repeat: no-repeat; background-position: left var(--inset-control) center; }
.input[type="date"] { background-image: var(--field-calendar); }
.input[type="time"] { padding-right: var(--space-10); background-image: var(--field-clock), var(--field-chevron); background-position: left var(--inset-control) center, right var(--inset-control) center; }
.input:is([type="date"], [type="time"])::-webkit-calendar-picker-indicator { position: absolute; inset: 0; width: auto; height: auto; margin: 0; padding: 0; opacity: 0; cursor: pointer; }
/* A date or time field with the app's own picker (shared/_date_field, shared/_time_field): the field shell as a button with the same
   leading icon, and on a time the chevron, a placeholder in grey-600, and the shell ringed while its popover is open. */
.picker-field { display: flex; align-items: center; padding-left: var(--space-10); background-repeat: no-repeat; background-position: left var(--inset-control) center; text-align: left; cursor: pointer; }
.picker-field-date { background-image: var(--field-calendar); }
.picker-field-time { padding-right: var(--space-10); background-image: var(--field-clock), var(--field-chevron); background-position: left var(--inset-control) center, right var(--inset-control) center; }
.picker-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker-empty { color: var(--color-text-subtle); }
.picker:has(.picker-panel:popover-open) .picker-field { border-color: var(--color-primary); box-shadow: var(--focus-ring); }
/* Its popover, drawn as a menu panel and placed under the field by picker_field.js. */
.picker-panel { inset: auto; margin: 0; padding: var(--space-3); background: var(--color-surface); color: var(--color-text); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); box-shadow: var(--shadow-pop); }
/* The calendar: the month between its arrows, then Monday-first weeks of 40px days; today in orange, the day chosen filled orange,
   the days of the months either side in grey-600. */
.calendar-pick-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); margin-bottom: var(--space-2); }
.calendar-pick-title { font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); }
.calendar-pick-grid { border-collapse: collapse; }
.calendar-pick-grid th { height: var(--control-h-sm); font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-regular); color: var(--color-text-muted); }
.calendar-pick-grid td { padding: 0; }
.calendar-pick-day { display: grid; place-items: center; width: var(--control-h-md); height: var(--control-h-md); padding: 0; border: 0; border-radius: var(--radius-field); background: none; font-size: var(--text-sm); color: var(--color-text); cursor: pointer; }
.calendar-pick-day:hover { background: var(--color-surface-alt); }
.calendar-pick-day:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.calendar-pick-day.is-outside { color: var(--color-text-subtle); }
.calendar-pick-day[aria-current="date"] { color: var(--color-primary); font-weight: var(--weight-medium); }
.calendar-pick-day[aria-pressed="true"] { background: var(--color-primary); color: var(--color-text-inverse); }
.calendar-pick-day:disabled { background: none; color: var(--color-text-disabled); cursor: default; }
/* The time list: seven 40px times at once, as menu items, the time chosen on orange-100; an arrow over its top or bottom edge while
   there is more that way. */
.time-pick { padding: var(--space-1); }
.time-pick-list { position: relative; max-height: calc(7 * var(--control-h-md)); margin: 0; padding: 0; overflow-y: auto; scrollbar-width: none; list-style: none; }
.time-pick-option { display: flex; align-items: center; min-height: var(--control-h-md); padding: 0 var(--space-3); border-radius: var(--radius-field); font-size: var(--text-sm); cursor: pointer; }
.time-pick-option:hover, .time-pick-option:focus { outline: none; background: var(--color-surface-alt); }
.time-pick-option[aria-selected="true"] { background: var(--rpp-orange-100); font-weight: var(--weight-medium); }
.time-pick-scroll { position: absolute; left: var(--space-1); right: var(--space-1); z-index: 1; display: flex; justify-content: center; padding: var(--space-1) 0; background: var(--color-surface); color: var(--color-text-muted); }
.time-pick-scroll:first-child { top: var(--space-1); }
.time-pick-scroll:last-child { bottom: var(--space-1); }
.check { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); line-height: var(--lh-sm); }
.check input { width: var(--space-4); height: var(--space-4); accent-color: var(--color-primary); }
.form-actions { display: flex; justify-content: flex-end; gap: var(--space-2); padding-top: var(--space-2); }
.form-actions.start { justify-content: flex-start; }
.form-actions.column { flex-direction: column; }
.form-actions > .hint { align-self: center; }  /* a line beside the buttons (Booking's active area count), on their middle */
/* A phone: the dialling-code select, then the number. */
.phone-field { display: grid; grid-template-columns: var(--unit-input-w) minmax(0, 1fr); gap: var(--space-2); }
.inline-form { display: flex; gap: var(--space-2); align-items: center; margin-top: var(--space-4); }
.inline-form.tight { margin-top: 0; }
.inline-form .input, .inline-form .select { flex: 1; min-width: 0; }
.inline-form .select { flex: 0 1 180px; }
@media (max-width: 600px) { .inline-form { flex-wrap: wrap; } .inline-form .input { flex-basis: 100%; } }
.segmented { display: inline-flex; align-self: flex-start; border: var(--hairline) solid var(--color-border); border-radius: var(--radius-pill); padding: var(--space-1); gap: var(--segment-gap); background: var(--color-surface); box-shadow: var(--shadow-sm); }
.segment { position: relative; }
.segment input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
/* A segment is a radio (a choice in a form) or a link (a view switch, the current one marked aria-current). */
.segment span, a.segment { display: inline-flex; align-items: center; gap: var(--space-2); height: var(--control-h-xs); padding: 0 var(--space-4); border-radius: var(--radius-pill); font-size: var(--text-sm); line-height: 1; font-weight: var(--weight-medium); color: var(--color-text); transition: background var(--duration-fast) var(--easing-standard), color var(--duration-fast) var(--easing-standard); }
a.segment:hover { background: var(--color-surface-alt); color: var(--color-text); }
.segment input:checked + span, a.segment[aria-current], a.segment[aria-current]:hover { background: var(--color-primary); color: var(--color-text-inverse); box-shadow: var(--shadow-glow); }
.segment input:focus-visible + span, a.segment:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* Choice chips: one of a few in a form (Create event's Photographer and Type), each a label round a visually hidden radio, drawn as a
   34px pill with no shadow; the chosen one is pressed like the current secondary button. */
.choice-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.choice-chip { position: relative; display: inline-flex; align-items: center; min-width: 0; max-width: 100%; height: var(--control-h-chip); padding: 0 var(--inset-control); border: var(--hairline) solid var(--color-border-strong); border-radius: var(--radius-pill); background: var(--color-surface); font-size: var(--text-sm); line-height: 1; font-weight: var(--weight-medium); color: var(--color-text-body); white-space: nowrap; cursor: pointer; transition: background var(--duration-fast) var(--easing-standard), border-color var(--duration-fast) var(--easing-standard), color var(--duration-fast) var(--easing-standard); }
.choice-chip:hover { background: var(--color-surface-alt); color: var(--color-text); }
.choice-chip-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }  /* a long name shortens inside its row on a phone; the chip's title has it whole */
.choice-chip:has(:focus-visible) { box-shadow: var(--focus-ring); }
/* Quick add's words (Create and Edit customer's notes): buttons in the chip's look, a plus before the words, never pressed. */
.quick-add-chip { gap: var(--space-1); }
.quick-add-chip:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* ---------- Status pill and tag ---------- */
.pill { display: inline-flex; align-items: center; gap: var(--space-2); height: var(--pill-h); padding: 0 var(--space-3) 0 var(--inset-pill); border-radius: var(--radius-pill); font-size: var(--text-xs); font-weight: var(--weight-medium); line-height: 1; letter-spacing: 0; color: var(--rpp-grey-900); white-space: nowrap; --pill-dot-color: var(--rpp-grey-400); }
.pill::before { content: ""; width: var(--pill-dot); height: var(--pill-dot); border-radius: var(--radius-circle); background: var(--pill-dot-color); flex: none; }
.pill-neutral { background: var(--status-neutral-bg); --pill-dot-color: var(--rpp-grey-400); }
.pill-info { background: var(--status-blue-bg); --pill-dot-color: var(--status-blue); }
.pill-warning { background: var(--status-yellow-bg); --pill-dot-color: var(--status-yellow); }
.pill-success { background: var(--status-green-bg); --pill-dot-color: var(--status-green); }
.pill-danger { background: var(--rpp-orange-100); --pill-dot-color: var(--rpp-orange-500); }
.pill-off { background: var(--status-off-bg); color: var(--status-off-fg); --pill-dot-color: var(--rpp-grey-400); }
.tag { display: inline-flex; align-items: center; height: var(--tag-h); padding: 0 var(--inset-pill); border-radius: var(--radius-pill); background: var(--rpp-grey-200); color: var(--color-text-muted); font-size: var(--text-xs); font-weight: var(--weight-medium); line-height: 1; letter-spacing: 0; white-space: nowrap; }
.tag-outline { height: var(--pill-h); background: var(--color-surface); border: var(--hairline) solid var(--color-border-strong); }  /* a wizard's step count, which counts rather than sorts */

/* ---------- Table ---------- */
.table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); line-height: var(--lh-sm); }
.table th { text-align: left; font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text-muted); padding: var(--space-2) var(--space-3); background: var(--color-surface-alt); border-bottom: var(--hairline) solid var(--color-border); }
.table td { color: var(--color-text); padding: var(--space-3); border-bottom: var(--hairline) solid var(--color-surface-alt); vertical-align: middle; }
.table td.muted { color: var(--color-text-muted); }
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover td { background: var(--color-surface-alt); }
.table tfoot td { border-top: var(--hairline) solid var(--color-border-strong); border-bottom: 0; padding-top: var(--space-3); }
.table tfoot tr + tr td { border-top: 0; padding-top: 0; }
.card-flush .table th:first-child, .card-flush .table td:first-child { padding-left: var(--space-6); }
.card-flush .table th:last-child, .card-flush .table td:last-child { padding-right: var(--space-6); }
.card:not(.card-flush) > .table { margin: 0 calc(-1 * var(--space-6)); width: calc(100% + 2 * var(--space-6)); }
.card:not(.card-flush) > .table th:first-child, .card:not(.card-flush) > .table td:first-child { padding-left: var(--space-6); }
.card:not(.card-flush) > .table th:last-child, .card:not(.card-flush) > .table td:last-child { padding-right: var(--space-6); }
.card:not(.card-flush) > .table:last-child { margin-bottom: calc(-1 * var(--space-6)); }
.row-action { text-align: right; white-space: nowrap; }
.row-action form { display: inline-block; margin: 0; }
.link-strong { color: var(--color-text); font-weight: var(--weight-medium); }
.link-strong:hover { color: var(--color-primary); }
.text-strong { font-weight: var(--weight-medium); color: var(--color-text); }
.with-avatar { display: inline-flex; align-items: center; gap: var(--space-2); }
.cell-stack { display: flex; flex-direction: column; line-height: var(--lh-sm); }
.cell-sub { font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); }
.qty-col { width: 90px; text-align: right; }
.qty { width: 72px; text-align: right; }
.tag-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.tag.finals-changed { background: var(--tint-orange-bg); color: var(--tint-orange-fg); }
/* An add-on row, indented under the order line it was sent on. */
.line-addon { display: flex; flex-direction: column; padding-left: var(--space-4); color: var(--color-text-body); }
.num-col { width: 110px; text-align: right; }
.total-label { text-align: right; font-weight: var(--weight-medium); }
.total-value { font-weight: var(--weight-medium); font-size: var(--text-base); color: var(--color-text); }

/* ---------- Avatar ---------- */
.avatar { display: inline-flex; align-items: center; justify-content: center; border-radius: var(--radius-circle); color: var(--color-text-inverse); font-weight: var(--weight-medium); letter-spacing: 0.3px; flex: none; width: var(--avatar-size); height: var(--avatar-size); font-size: calc(var(--avatar-size) * 0.4); }
.avatar-20 { --avatar-size: var(--avatar-20); }
.avatar-24 { --avatar-size: var(--avatar-24); }
.avatar-28 { --avatar-size: var(--avatar-28); }
.avatar-32 { --avatar-size: var(--avatar-32); }
.avatar-36 { --avatar-size: var(--avatar-36); }
.avatar-40 { --avatar-size: var(--avatar-40); }
.avatar-56 { --avatar-size: var(--avatar-56); }
.avatar-64 { --avatar-size: var(--avatar-64); }
.avatar-tint-1, .swatch-1 { background: var(--rpp-orange-500); }
.avatar-tint-2, .swatch-2 { background: var(--tint-blue-fg); }
.avatar-tint-3, .swatch-3 { background: var(--status-green-fg); }
.avatar-tint-4, .swatch-4 { background: var(--tint-purple-fg); }
.avatar-tint-5, .swatch-5 { background: var(--tint-amber-fg); }
.avatar-tint-6, .swatch-6 { background: var(--rpp-grey-800); }
.avatar-brand { background: var(--rpp-orange-100); color: var(--rpp-orange-900); }  /* on customer-facing pages, where identity colours never go */
/* A customer's photo as their avatar (customer_avatar): the thumbnail fills the circle, on the grey-100 surface while it loads. */
.avatar-photo { background: var(--color-surface-alt); overflow: hidden; }
.avatar-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.swatch-0 { background: var(--rpp-grey-400); }

/* ---------- Callout ---------- */
.callout { display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-3) var(--space-4); border-radius: var(--radius-card); border: var(--hairline) solid color-mix(in srgb, currentColor 35%, transparent); font-size: var(--text-sm); line-height: var(--lh-sm); margin-bottom: var(--space-4); }
.callout-icon { display: inline-flex; align-items: center; justify-content: center; width: var(--tag-h); height: var(--tag-h); border-radius: var(--radius-circle); background: var(--overlay-chip); flex: none; margin-top: -1px; }
.callout-body { flex: 1; min-width: 0; overflow-wrap: anywhere; }  /* a long email in a notice breaks rather than widening the page */
.callout-title { font-weight: var(--weight-medium); }
.callout-info { background: var(--status-blue-bg); color: var(--status-blue-fg); }
.callout-warning { background: var(--status-yellow-bg); color: var(--status-yellow-fg); }
.callout-success { background: var(--status-green-bg); color: var(--status-green-fg); }
.callout-danger { background: var(--rpp-orange-100); color: var(--rpp-orange-900); }
.callout .prewrap { margin-top: var(--space-1); }
.callout a { color: inherit; text-decoration: underline; }

/* ---------- Empty state ---------- */
.empty { display: flex; flex-direction: column; align-items: center; text-align: center; padding: var(--space-8) var(--space-4); gap: var(--space-1); }
.empty-glyph { width: var(--space-16); height: var(--space-16); border-radius: var(--radius-circle); background: var(--color-primary-wash); color: var(--color-primary-hover); display: flex; align-items: center; justify-content: center; margin-bottom: var(--space-3); }
.empty-title { font-size: var(--text-base); font-weight: var(--weight-medium); color: var(--color-text); }
.empty-desc { color: var(--color-text-muted); font-size: var(--text-sm); line-height: var(--lh-sm); max-width: var(--empty-desc-max); }
.empty-action { margin-top: var(--space-3); }
/* Inside a form (Create and Edit customer's team rows): compact, in a dashed box, the glyph in ink on a quiet grey tile the rail's
   size, so the dialog's orange stays on its step and its Continue. */
.empty-boxed { border: var(--hairline) dashed var(--color-border-strong); border-radius: var(--radius-card); padding: var(--space-8); }
.empty-boxed .empty-glyph { width: var(--rail-link); height: var(--rail-link); border-radius: var(--radius-tile); background: var(--color-surface-alt); color: var(--color-text); margin-bottom: var(--space-2); }
.empty-boxed .empty-title { font-size: var(--text-sm); line-height: var(--lh-sm); }
.empty-boxed .empty-desc { font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); }

/* ---------- Lists, menus, steps ---------- */
.list { list-style: none; padding: 0; display: flex; flex-direction: column; font-size: var(--text-sm); line-height: var(--lh-sm); }
.list li { color: var(--color-text); display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-2) 0; border-bottom: var(--hairline) solid var(--color-surface-alt); }
.list li:last-child { border-bottom: 0; }
.list form { margin: 0; }
.steps { padding-left: var(--space-5); color: var(--color-text-muted); font-size: var(--text-sm); line-height: var(--lh-sm); display: flex; flex-direction: column; gap: var(--space-2); }
.menu { position: relative; display: inline-block; }
.menu-panel { position: absolute; right: 0; top: calc(100% + var(--space-1)); z-index: 30; width: 240px; padding: var(--space-3); background: var(--color-surface); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); box-shadow: var(--shadow-pop); display: flex; flex-direction: column; gap: var(--space-3); text-align: left; }
.menu-panel form { margin: 0; }
.menu-panel .btn { width: 100%; }
.disclosure > summary { list-style: none; display: inline-flex; }
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure[open] > summary { margin-bottom: var(--space-3); }
code { font-size: var(--text-xs); font-variant-numeric: tabular-nums; background: var(--color-surface-alt); padding: 1px var(--space-1); border-radius: var(--radius-xs); }

/* ---------- Auth ---------- */
.auth-body { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: var(--space-6); }
.auth { width: 100%; max-width: 420px; display: flex; flex-direction: column; align-items: center; gap: var(--space-6); }
.auth-logo { height: var(--control-h-lg); width: auto; }
.auth-card { width: 100%; padding: var(--space-8); }
.auth-title { font-weight: var(--weight-light); font-size: var(--text-lg); line-height: var(--lh-lg); letter-spacing: var(--tracking-heading); }
.auth-sub { color: var(--color-text-muted); margin: var(--space-2) 0 var(--space-6); }

/* ---------- Filter bar, tabs, stats ---------- */
.filter-bar { display: flex; align-items: center; gap: var(--space-4); margin-bottom: var(--space-4); flex-wrap: wrap; }
.search { position: relative; flex: 0 1 320px; }
.search .icon { position: absolute; left: var(--inset-control); top: 50%; transform: translateY(-50%); color: var(--color-text-subtle); pointer-events: none; }  /* a press on it reaches the field */
.search .input { padding-left: var(--space-10); }
.tabs-pill { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.tab-pill, .filter-toggle { display: inline-flex; align-items: center; gap: var(--space-2); height: var(--control-h-chip); padding: 0 var(--inset-control); border-radius: var(--radius-pill); border: var(--hairline) solid var(--color-border-strong); background: var(--color-surface); box-shadow: var(--shadow-sm); font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--color-text-muted); transition: background var(--duration-fast) var(--easing-standard), color var(--duration-fast) var(--easing-standard), border-color var(--duration-fast) var(--easing-standard); }
.tab-pill:hover, .filter-toggle:hover { background: var(--color-surface-alt); color: var(--color-text); }
.tab-pill.active, .filter-toggle:has(:checked) { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-text-inverse); box-shadow: var(--shadow-glow); }
.tab-pill.active:hover, .filter-toggle:has(:checked):hover { background: var(--color-primary-hover); border-color: var(--color-primary-hover); color: var(--color-text-inverse); }
.tab-pill .num { font-weight: var(--weight-light); color: var(--color-text-subtle); }
.tab-pill.active .num { color: var(--rpp-orange-100); }
.tab-pill .dot { width: var(--space-2); height: var(--space-2); }
/* A filter chip that is a checkbox (the Jobs list's Starred), drawn as a status chip: on, it fills like the active one, its star
   filled too. */
.filter-toggle { position: relative; cursor: pointer; }
.filter-toggle:has(:focus-visible) { box-shadow: var(--focus-ring); }
.filter-toggle:has(:checked) .icon { fill: currentColor; }

/* ---------- Jobs list ---------- */
/* The search full width, then the status chips, then the filters; one card per job under them. */
.job-list-controls { display: grid; gap: var(--space-6); margin-bottom: var(--space-6); }
.job-list-filters { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.job-list-filters-label { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text-muted); }
.job-list-filters .select { width: auto; max-width: 100%; }
.job-cards { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.job-card { display: grid; grid-template-columns: var(--space-16) minmax(0, 1fr) auto; grid-template-areas: "thumb body side"; align-items: center; gap: var(--space-4); padding: var(--space-3); }  /* no overflow clip, so the row menu opens over the cards below */
.job-card-thumb { grid-area: thumb; display: flex; align-items: center; justify-content: center; width: var(--space-16); height: var(--space-16); border-radius: var(--radius-image); background: var(--color-surface-alt); color: var(--color-text-subtle); overflow: hidden; }
.job-card-thumb img, .record-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.job-card-body { grid-area: body; min-width: 0; }
.job-card-title { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.job-card-address { font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); color: var(--color-text); overflow-wrap: anywhere; }
.job-card-meta { margin-top: var(--space-1); font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); }
.job-card-side { grid-area: side; display: flex; align-items: center; gap: var(--space-4); }
.job-card-people { display: flex; align-items: center; gap: var(--space-1); }
.avatar-empty { background: var(--color-surface-alt); color: var(--color-text-subtle); }  /* nobody on the shoot yet */
.job-card-total { min-width: var(--space-20); text-align: right; font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--color-text); }
.job-card-menu-space { width: var(--control-h-md); flex: none; }  /* holds the menu's place on a row with nothing to offer */
/* The user's star beside a job's name and after its page's status pill: grey until starred, then filled orange. Compact (sm) on a
   card; md in the job page's header, the height of the buttons beside it. */
.star-frame { display: inline-flex; }
.star-button { color: var(--color-text-subtle); }
.record-actions .star-button { width: var(--control-h-md); height: var(--control-h-md); }
.star-button[aria-pressed="true"], .star-button[aria-pressed="true"]:hover { color: var(--color-primary); }
.star-button[aria-pressed="true"] .icon { fill: currentColor; }
@media (max-width: 600px) {
  .job-card { grid-template-columns: var(--space-16) minmax(0, 1fr); grid-template-areas: "thumb body" "side side"; gap: var(--space-3) var(--space-4); }
  .job-card-side { flex-wrap: wrap; gap: var(--space-3); }
  .job-card-total { min-width: 0; margin-left: auto; }
  .job-card-menu-space { display: none; }
}

/* ---------- Customers list ---------- */
/* The search grows beside Sort (a labelled select, .select-field); below the search's own width, Sort wraps under it. */
.customer-list-search .search { flex-grow: 1; }
/* The rows have menus, so the flush card doesn't clip (the last rows' menus open below it); the header band and the last row then
   take the card's corners themselves. */
.customer-list { overflow: visible; }
.customer-table { border-collapse: separate; border-spacing: 0; }
.customer-table th:first-child { border-top-left-radius: calc(var(--radius-card) - var(--hairline)); }
.customer-table th:last-child { border-top-right-radius: calc(var(--radius-card) - var(--hairline)); }
.customer-table tbody tr:last-child td:first-child { border-bottom-left-radius: calc(var(--radius-card) - var(--hairline)); }
.customer-table tbody tr:last-child td:last-child { border-bottom-right-radius: calc(var(--radius-card) - var(--hairline)); }
.customer-table td { border-bottom-color: var(--color-border); }  /* grey-200 dividers */
.customer-table .with-avatar { display: flex; gap: var(--space-3); }  /* a block, so a photo avatar's baseline adds no line under it and the name stays level with the row's other first lines */
.customer-table th.num-col { text-align: right; }  /* right over the figures; .table th alone would put it left */
.customer-table .customer-lifetime, .customer-table .customer-jobs { width: auto; }  /* as wide as their headers and figures, not .num-col's 110px */
.customer-table .customer-lifetime { white-space: nowrap; }
.customer-lifetime-total { font-size: var(--text-base); font-weight: var(--weight-medium); }  /* the figure Top spend sorts by stands out, on the row's 20px line */
/* Between the stacked rows and the fixed columns the table sizes itself: a company too long for the Customer column breaks anywhere
   rather than squeeze Contact or widen the page, while the name still breaks only between its words. */
@media (min-width: 961px) and (max-width: 1199px) { .customer-table td:first-child .cell-sub { overflow-wrap: anywhere; } }
/* From a laptop's width the columns are fixed, so they stay put whatever the chip lists; Customer and Contact share the rest, and a
   name or company too long for its column breaks rather than run under Contact. */
@media (min-width: 1200px) {
  .customer-table { table-layout: fixed; }
  .customer-table td:first-child .cell-stack { min-width: 0; overflow-wrap: anywhere; }
  .customer-table th.customer-lifetime { width: var(--customer-lifetime-w); }
  .customer-table th.customer-jobs { width: var(--space-16); }
  .customer-table th.customer-last-shoot { width: var(--customer-last-shoot-w); }
  .customer-table th.customer-actions { width: calc(var(--space-3) + var(--control-h-md) + var(--space-6)); }  /* the menu button inside the cell's insets */
}
/* Where the table is tight, a long email breaks anywhere rather than widen it, taking the slack, and a last shoot keeps to one
   line; once the rows stack it may break, but only between its date and how long ago. */
.customer-table .customer-list-contact { overflow-wrap: anywhere; }
.customer-last-shoot time, .customer-last-shoot time > span { white-space: nowrap; }
.customer-jobs-unit { display: none; }  /* under the Jobs header the count says enough */
/* Last shoot more than 30 days ago: the danger tone's ink and dot, outside a pill, the dot on the first line if the date breaks. */
.last-shoot-lapsed { display: inline-flex; align-items: baseline; gap: var(--space-2); font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--rpp-orange-900); --pill-dot-color: var(--rpp-orange-500); }
/* Below 960px the six columns don't fit beside the rail, and a card with menus can't scroll sideways, so each row stacks: the
   name and its menu, then contact, lifetime, jobs (with their unit, the header gone) and last shoot. */
@media (max-width: 960px) {
  .customer-table thead { display: none; }
  .customer-table, .customer-table tbody { display: block; }
  .customer-table tbody tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-1) var(--space-3); padding: var(--space-3) var(--space-6); border-bottom: var(--hairline) solid var(--color-border); }
  .customer-table tbody tr:last-child { border-bottom: 0; }
  .card-flush .customer-table td, .card-flush .customer-table td:first-child, .card-flush .customer-table td:last-child { display: block; grid-column: 1; padding: 0; border: 0; text-align: left; }
  .card-flush .customer-table td.row-action { grid-column: 2; grid-row: 1; }
  .customer-jobs-unit { display: inline; }
  .customer-last-shoot time { white-space: normal; }
  .customer-no-average { display: none; }  /* without the header, a bare dash under $0.00 says nothing */
  .customer-table tbody tr:hover td { background: none; }  /* the row band would tint only the cells, in blocks */
}
@media (max-width: 600px) { .customer-table tbody tr { padding-inline: var(--space-4); } }  /* the phone's inset */

/* ---------- Products list ---------- */
/* The kind chips on the left, the search and Filters on the right; when they don't fit beside the chips, the search and Filters take
   a row of their own, at its right end, the search filling it on a phone. Then the count of the rows shown, 12px over the row cards,
   each with its description on one line, cut short, then its category after a dot, whole up to half the line (a long one on a phone
   is cut short too, so the description keeps the other half), and its price over its time on site, the price 16px Medium as the
   Customers list's figure.
   The Live on booking form switch sits between the price and the pill. The pill keeps a slot as wide as Inactive, as a job card's
   total keeps its own, so the prices and switches line up from row to row. At tablet widths and below the switch leaves the name too
   little room, so the row stacks at the app's 960px breakpoint as a job card does on a phone: the price, switch, pill and menu on a
   line of their own under the picture. */
.product-list-controls { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-6); }
.product-list-find { display: flex; flex: 1 1 auto; align-items: center; justify-content: flex-end; gap: var(--space-3); }
.product-list-count, .report-section-label { margin-bottom: var(--space-3); font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text-muted); }
.product-card-meta { display: flex; gap: var(--space-1); }
.product-card-description { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.product-card-category { flex: none; max-width: 50%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.product-card-price { text-align: right; white-space: nowrap; }
.product-card-amount { font-size: var(--text-base); font-weight: var(--weight-medium); color: var(--color-text); }
.product-card-switch { display: flex; margin: 0; }  /* the switch's own form (a span on an inactive row), centred on the row like the pill beside it */
.product-card-status { display: flex; justify-content: flex-end; min-width: var(--space-20); }
@media (max-width: 960px) {
  .product-card { grid-template-columns: var(--space-16) minmax(0, 1fr); grid-template-areas: "thumb body" "side side"; gap: var(--space-3) var(--space-4); }
  .product-card .job-card-side { flex-wrap: wrap; gap: var(--space-3); }
  .product-card-price { margin-right: auto; text-align: left; }  /* the switch, pill and menu at the row's end, as a job card's total */
  .product-card-status { min-width: 0; }
}
@media (max-width: 600px) { .product-list-find .search { flex-grow: 1; } }
/* Manage categories, a form dialog over the list: the categories as hairline rows, each its name in a field with Rename beside it and
   how many products have it under it, and the round ghost Delete at the row's end, level with the field; then Name and Add in the
   foot, which stays put as the rows scroll. A name is short, so each field keeps its button beside it on a phone too. */
.category-list li { align-items: flex-start; padding: var(--space-3) 0; }
.category-list li:first-child { padding-top: 0; }
.category-form { flex: 1; min-width: 0; }
.category-form .inline-form { flex-wrap: nowrap; }
.category-form .inline-form .input { flex-basis: 0; }

/* ---------- The product page ----------
   The header on one row while it fits: back to Products, the name at the toolbar title's size (h4) with its kind and pill, then the
   Live on booking form switch (its 14px Medium label 10px after it), Cancel and Save changes. Under it three columns 24px apart:
   the section list at the settings nav's width, sticky as that is; the form's section cards, 24px apart; and the Client booking
   preview at the side panel's width, sticky too, the booking page's card under a 14px Medium title and over a centred hint. Below
   1200px the preview goes under the form, beside the list, still no wider, and scrolls with the page; below 960px it all stacks
   and the list scrolls with the page too. */
.product-page .page-header { align-items: center; }
.product-page .page-title { font-size: var(--text-h4); line-height: var(--lh-h4); }
.product-live-switch { display: inline-flex; align-items: center; gap: var(--gap-control); font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text); cursor: pointer; }
.product-live-switch:has(.toggle:disabled) { cursor: default; }  /* an inactive product's, greyed out */
.product-page-body { display: grid; grid-template-columns: var(--settings-nav-w) minmax(0, 1fr) var(--side-panel-w); grid-template-areas: "nav main preview"; gap: var(--space-6); align-items: start; }
.product-page-body > .section-nav { grid-area: nav; }
.product-page-main { grid-area: main; min-width: 0; }
.product-form { gap: var(--space-6); }
.product-section { scroll-margin-top: var(--space-8); }  /* a section jumped to stands where the sticky list's top does */
.product-preview { grid-area: preview; position: sticky; top: var(--space-8); display: flex; flex-direction: column; gap: var(--space-3); }
.product-preview-note { text-align: center; }
@media (max-width: 1199px) {
  .product-page-body { grid-template-columns: var(--settings-nav-w) minmax(0, 1fr); grid-template-areas: "nav main" "nav preview"; }
  .product-preview { position: static; max-width: var(--side-panel-w); }
}
@media (max-width: 960px) {
  .product-page-body { grid-template-columns: minmax(0, 1fr); grid-template-areas: "nav" "main" "preview"; }
  .product-page-body > .section-nav { position: static; }
}
/* The section list, on the product page and as the settings nav (shared/_settings_frame): a card of menu items (.menu-item) with
   card corners. The links are grey-800 until the menu item's hover makes them grey-900; the section in view, or the settings
   category open, is on orange-100 with an orange-900 name and an orange icon. A section a refused save left a problem in has the
   orange attention dot at its link's end. Under a hairline, Archive product, a menu's danger item, or Unarchive product. */
.section-nav { position: sticky; top: var(--space-8); padding: var(--space-2); }
.section-nav-links { display: flex; flex-direction: column; }
.section-nav .menu-item { border-radius: var(--radius-card); }
.section-nav .section-nav-link { color: var(--color-text-muted); }
.section-nav .section-nav-link[aria-current] { background: var(--rpp-orange-100); color: var(--rpp-orange-900); }
.section-nav-link[aria-current] .icon { color: var(--color-primary); }
.section-nav-link .attention-dot { margin: 0 0 0 auto; }
.section-nav-foot { margin-top: var(--space-2); padding-top: var(--space-2); border-top: var(--hairline) solid var(--color-border); }
.section-nav-foot form { margin: 0; }
/* A field's foot: its hint and, at the right, its count against the limit (Description), orange-900 past it. A refused field, on the
   product page, in Create product or in Manage categories: its shell's border orange and its reasons under it, each a 12px
   orange-900 line (Rails wraps the field and its label in a box that keeps no place of its own). */
.field-foot { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); }
.field-count { flex: none; margin-inline-start: auto; }  /* at the right, also with no hint beside it (the rating card's Your review) */
.field-count.is-over { color: var(--color-primary-hover); }
.field-error { color: var(--color-primary-hover); font-size: var(--text-xs); line-height: var(--lh-xs); }
.product-form .field_with_errors { display: contents; }
:is(.product-form, .category-form) .field_with_errors > :is(.input, .select) { border-color: var(--color-primary); }
.product-twilight { display: contents; }  /* Twilight timing's add-on box keeps no place of its own, so with its fields hidden for Digital no empty gap is left */

.stats { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: var(--space-6); margin-bottom: var(--space-6); }  /* the dashboard's month figures in one row: five, or a photographer's four */
@media (max-width: 1360px) { .stats:has(.stat:nth-child(4)) { grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr)); } }  /* four or five across leave a money figure (Revenue, a lifetime value, an average price) too little room; a photographer's four, with none, wrap the same so the dashboard keeps one shape */
@media (max-width: 960px) { .stats, .stats:has(.stat:nth-child(4)) { grid-auto-flow: row; grid-template-columns: minmax(0, 1fr); } }
.stat { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-6); background: var(--color-surface); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); box-shadow: var(--shadow-card); color: var(--color-text); }
.stat-label { font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text-muted); }
.stat-value { margin-top: var(--space-1); font-size: var(--text-h3); line-height: var(--lh-h3); font-weight: var(--weight-light); letter-spacing: var(--tracking-heading); font-variant-numeric: tabular-nums; }
.stat-sub { font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); min-height: var(--lh-xs); }
.stat-star { margin-inline-start: var(--space-2); color: var(--color-primary); fill: currentColor; }  /* after the dashboard's Average rating */
.stat-value-long { font-size: var(--text-h4); }  /* a Reports figure, or the dashboard's revenue, too long for a card of a row of five at h3; the line height stays, so the row lines up */
/* Job reports' figures carry their glyph, orange on an orange-100 tile, 16px over the label. */
.stat-icon { display: flex; align-items: center; justify-content: center; width: var(--avatar-36); height: var(--avatar-36); margin-bottom: var(--space-3); border-radius: var(--radius-tile); background: var(--color-primary-tint); color: var(--color-primary); }

/* ---------- Dashboard briefing ----------
   Under the figures, three columns: Revenue over Pipeline, Needs your attention, Today over Next 7 days; each column's last card
   grows so the columns end level. From 1360px down, two columns with Today and Next 7 days side by side under them; below 960px,
   one. Recent deliveries runs full width under them. A card's list starts at its head's margin, so its first row drops its top
   padding. */
.briefing { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-6); margin-bottom: var(--space-6); }
.briefing-column { display: flex; flex-direction: column; gap: var(--space-6); min-width: 0; }
.briefing-column > :last-child { flex: 1 0 auto; }
@media (max-width: 1360px) {
  .briefing { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .briefing-column:last-child { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 960px) { .briefing, .briefing-column:last-child { grid-template-columns: minmax(0, 1fr); } }

/* Revenue: the period menu, the legend and the period's total, then the chart (an SVG drawn by the server, its lines stretched to the
   card and kept 2px) or a line saying there is nothing to draw. The tip floats beside the hairline, on the side with more room. */
.revenue { display: block; }
.revenue[busy] .revenue-summary, .revenue[busy] .revenue-chart, .revenue[busy] .revenue-empty { opacity: .5; transition: opacity var(--duration-fast) var(--easing-standard); }  /* the last period's card stays while the next one loads */
.revenue-period { display: flex; align-items: center; width: auto; font-weight: var(--weight-medium); list-style: none; cursor: pointer; }
.revenue-period::-webkit-details-marker { display: none; }
.revenue-menu .menu-panel { width: max-content; min-width: 100%; }
.revenue-summary { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--space-2) var(--space-4); }
.revenue-legend { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-4); font-size: var(--text-xs); line-height: var(--lh-xs); }
.revenue-legend li { display: inline-flex; align-items: center; gap: var(--space-2); }
.revenue-key { flex: none; width: var(--space-4); height: var(--chart-line); background: var(--color-primary); }
.revenue-key-previous { background: repeating-linear-gradient(90deg, var(--rpp-grey-400) 0 var(--space-1), transparent var(--space-1) var(--space-2)); }  /* the line's own dashes */
.revenue-total { display: flex; flex-direction: column; align-items: flex-end; margin-left: auto; font-size: var(--text-xs); line-height: var(--lh-xs); }
.revenue-total-value { font-size: var(--text-h4); line-height: var(--lh-h4); font-weight: var(--weight-light); letter-spacing: var(--tracking-heading); color: var(--color-text); }
.revenue-empty { margin-top: var(--space-4); padding: var(--space-12) 0; border-top: var(--hairline) solid var(--color-border); text-align: center; font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--color-text-subtle); }
.revenue-chart { display: flex; gap: var(--space-2); margin-top: var(--space-4); }
.revenue-ticks { flex: none; width: var(--space-16); overflow: visible; }  /* the dollar labels, right-aligned beside their gridlines */
.revenue-plot { position: relative; flex: 1 1 0; min-width: 0; border-radius: var(--radius-xs); }
.revenue-plot:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.revenue-svg { display: block; overflow: visible; }
.revenue-grid, .revenue-crosshair { stroke-width: var(--hairline); shape-rendering: crispEdges; }
.revenue-grid { stroke: var(--color-border); }
.revenue-crosshair { stroke: var(--color-border-strong); }
.revenue-line { fill: none; stroke-width: var(--chart-line); stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.revenue-line-current { stroke: var(--color-primary); }
.revenue-line-previous { stroke: var(--rpp-grey-400); stroke-dasharray: var(--space-1) var(--space-1); stroke-linecap: butt; }  /* square ends, so its dashes match the legend's key */
.revenue-dot { fill: var(--color-primary); stroke: var(--color-surface); stroke-width: calc(2 * var(--chart-line)); paint-order: stroke; }
.revenue-axis { fill: var(--color-text-subtle); font-size: var(--text-xs); font-variant-numeric: tabular-nums; }
.revenue-tip { position: absolute; top: 0; z-index: 2; display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-2) var(--space-3); background: var(--color-surface); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); box-shadow: var(--shadow-pop); font-size: var(--text-xs); line-height: var(--lh-xs); white-space: nowrap; pointer-events: none; transform: translateX(var(--space-3)); }
.revenue-tip.is-flipped { transform: translateX(calc(-100% - var(--space-3))); }
.revenue-tip-day { font-weight: var(--weight-medium); color: var(--color-text); }
.revenue-tip-row { display: flex; align-items: center; gap: var(--space-2); }
.revenue-tip-value { font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text); font-variant-numeric: tabular-nums; }

/* Pipeline: a row per status, its count and a thin bar against the largest count (Project status's bar on the lighter track). */
.pipeline { list-style: none; padding: 0; }
.pipeline li { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-3) 0; border-bottom: var(--hairline) solid var(--color-border); }
.pipeline li:first-child, .attention li:first-child .attention-row { padding-top: 0; }
.pipeline-row { display: flex; justify-content: space-between; gap: var(--space-3); font-size: var(--text-sm); line-height: var(--lh-sm); }
.pipeline-count { font-weight: var(--weight-medium); color: var(--color-text); font-variant-numeric: tabular-nums; }
.pipeline-bar, .pipeline-bar::-webkit-progress-bar { background: var(--color-surface-alt); }

/* Needs your attention: a row per job ready for delivery, an orange dot by its name and its orders under it. */
.attention { list-style: none; padding: 0; }
.attention li { border-bottom: var(--hairline) solid var(--color-border); }
.attention-row { display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-3) 0; color: var(--color-text); }
.attention-row:hover { color: var(--color-text); }
.attention-row:hover .attention-name { color: var(--color-primary); }
.attention-text { display: flex; flex-direction: column; min-width: 0; }
.attention-name { font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); overflow-wrap: anywhere; }

/* Recent deliveries: four photo tiles across, two below 960px, one on a phone; square-cornered, the name under each. */
.recent-tiles { list-style: none; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-5); }
.recent-tile { display: flex; flex-direction: column; gap: var(--space-2); color: var(--color-text); }
.recent-tile:hover { color: var(--color-primary); }
.recent-cover { display: flex; align-items: center; justify-content: center; aspect-ratio: 2 / 1; border-radius: var(--radius-image); background: var(--rpp-grey-200); color: var(--color-text-subtle); overflow: hidden; }
.recent-cover img { width: 100%; height: 100%; object-fit: cover; display: block; transition: opacity var(--duration-fast) var(--easing-standard); }
.recent-tile:hover .recent-cover img { opacity: .88; }
.recent-name { font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 960px) { .recent-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) { .recent-tiles { grid-template-columns: minmax(0, 1fr); } }

/* ---------- Agenda and definition lists ---------- */
.agenda { list-style: none; padding: 0; display: flex; flex-direction: column; }
.agenda li + li { border-top: var(--hairline) solid var(--color-surface-alt); }
.agenda-item { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) 0; color: var(--color-text); }
.agenda-item:hover .agenda-title { color: var(--color-primary); }
.agenda-time { flex: 0 0 auto; min-width: var(--agenda-time-w); font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text); white-space: nowrap; }
.agenda-body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.agenda-title { font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.agenda-sub { font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); }
@media (max-width: 600px) { .agenda-item { flex-wrap: wrap; row-gap: var(--space-1); } .agenda-time { flex-basis: 100%; } }  /* the time sits on its own line so the address keeps its width */

.dl { display: grid; grid-template-columns: 90px minmax(0, 1fr); gap: var(--space-2) var(--space-3); font-size: var(--text-sm); line-height: var(--lh-sm); margin-top: var(--space-3); }
.dl-wide { grid-template-columns: 130px minmax(0, 1fr); margin-top: 0; }
.dl > div { display: contents; }  /* a term and its value wrapped together, so the pair can be hidden as one */
.dl dt { color: var(--color-text-subtle); }
.dl dd { margin: 0; min-width: 0; overflow-wrap: anywhere; color: var(--color-text); display: flex; align-items: center; gap: var(--space-1); flex-wrap: wrap; }
.dl dd.stack-sm { flex-direction: column; align-items: flex-start; gap: var(--space-1); }
.page-meta { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); margin-top: var(--space-2); }
.page-meta .icon { color: var(--color-text-subtle); }
.meta-sep { color: var(--color-text-disabled); }

/* ---------- Calendar ---------- */
/* The toolbar: paging, Today and the dates shown on the left, the view switch and the actions on the right. From 1440px they share
   one row whatever the view (a title that ever runs out of room ends in an ellipsis); below it the right group always takes a row of
   its own, so paging to a longer title never moves the grid, and the Filters and New panels open under the group's right end rather
   than their own button, which may sit at the start of a line. */
.cal-toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3) var(--space-5); margin-bottom: var(--space-5); }
.cal-toolbar-start, .cal-toolbar-end { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); min-width: 0; }
.cal-paging { display: flex; gap: var(--space-1); }
.cal-nav { display: inline-flex; align-items: center; justify-content: center; width: var(--rail-link); height: var(--rail-link); border-radius: var(--radius-tile); color: var(--color-text); transition: background var(--duration-fast) var(--easing-standard); }  /* the rail's ghost tile */
.cal-nav:hover { background: var(--color-surface-alt); color: var(--color-text); }
.cal-nav:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.cal-title { margin-left: var(--space-2); font-size: var(--text-h4); line-height: var(--lh-h4); font-weight: var(--weight-light); letter-spacing: var(--tracking-heading); }
/* The Filters panel, here and on the Products list: fieldsets of choices, then Reset (Clear all) and Apply. */
.cal-filters-panel { width: var(--filters-w); max-width: calc(100vw - 2 * var(--space-4)); padding: var(--space-4); gap: var(--space-4); }
.cal-filters-group { margin: 0; padding: 0; border: 0; min-width: 0; }
.cal-filters-group legend, .cal-filters-label { display: block; padding: 0; margin-bottom: var(--space-2); font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text); }
.cal-filters-group .check { color: var(--color-text); }
.cal-filters-group .dot { width: var(--swatch); height: var(--swatch); }  /* the person's colour, as on the legend */
.cal-filters-group .check + .check { margin-top: var(--space-2); }
.cal-filters-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.menu-panel .cal-filters-foot .btn { width: auto; }
@media (min-width: 1440px) {
  .cal-toolbar, .cal-toolbar-start { flex-wrap: nowrap; }
  .cal-toolbar-end { flex: none; }
  .cal-title { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}
@media (max-width: 1439px) {
  .cal-toolbar-start { flex-basis: 100%; }
  .cal-toolbar-end { position: relative; }
  .cal-toolbar-end > .menu { position: static; }
}
@media (max-width: 600px) { .cal-title { flex-basis: 100%; margin-left: 0; } }

/* The grid beside the side panel, which goes under it below 1200px: beside it (the rail, page insets, panel and gap take 480px) a
   week's seven --cal-col-min days and the gutter would scroll sideways on a laptop below 1146px, and be cramped just above. */
.cal-body { display: grid; grid-template-columns: minmax(0, 1fr) var(--side-panel-w); gap: var(--space-5); align-items: start; }
.cal-main { min-width: 0; }
@media (max-width: 1199px) { .cal-body { grid-template-columns: minmax(0, 1fr); } }

/* The time grid: an hour gutter and a column per day (or person), each hour --cal-hour-h tall with a hairline at its top. An entry
   sits at --start for --span minutes (from the grid's first hour), in lane --lane of --lanes. Lanes sit side by side while each gets
   --cal-event-min; in a narrower column each later lane is inset and drawn over the one before with a white edge, the last ending at
   the column's right, and the one under the pointer or focus comes to the front. Week's seven days share the card; in Day and By
   photographer a column is wider where --col-lanes entries side by side need --cal-event-min each, and a grid wider than its card
   scrolls inside it, the hour gutter staying put. */
.cal-grid { display: grid; grid-template-columns: var(--space-12) repeat(var(--columns), minmax(auto, 1fr)); overflow-x: auto; }
.cal-grid-row { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; }
.cal-col-head { display: flex; flex-direction: column; align-items: center; justify-content: center; min-width: 0; padding: var(--space-2) var(--space-1); border-left: var(--hairline) solid var(--color-border); text-align: center; }
.cal-col-head.is-today { background: var(--color-primary-tint); }
.cal-day-name { font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text-subtle); }
.cal-col-head.is-today .cal-day-name { color: var(--color-primary); }
.cal-day-num { display: inline-flex; align-items: center; justify-content: center; min-width: var(--avatar-32); height: var(--avatar-32); border-radius: var(--radius-circle); font-size: var(--text-lg); line-height: 1; font-weight: var(--weight-light); color: var(--color-text); }
.cal-col-head.is-today .cal-day-num { background: var(--color-primary); color: var(--color-text-inverse); font-size: var(--text-base); font-weight: var(--weight-medium); }
.cal-grid.is-people .cal-col-head { padding: var(--space-3) var(--space-2); }
.cal-col-person { display: inline-flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--space-1) var(--space-2); max-width: 100%; font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text); }  /* a name too long for its column goes under its colour */
.cal-allday { border-top: var(--hairline) solid var(--color-border); }
.cal-gutter { position: sticky; left: 0; z-index: 1; background: var(--color-surface); }  /* over the columns scrolled under it */
.cal-allday-label { align-self: stretch; display: flex; align-items: center; justify-content: flex-end; padding-right: var(--space-2); text-align: right; font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text-subtle); }
.cal-allday-cell { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; padding: var(--space-1); border-left: var(--hairline) solid var(--color-border); }
.cal-allday .event-time { display: none; }  /* the row says All day */
/* Each column (and the gutter) starts with a --space-2 band the first hour's label sits in; the band takes the column's colour, so
   today stays grey from its head down. */
.cal-grid-body .cal-gutter { display: flex; flex-direction: column; padding-top: var(--space-2); }
.cal-hour { position: relative; top: calc(var(--lh-xs) / -2); flex: none; height: var(--cal-hour-h); padding-right: var(--space-2); text-align: right; font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text-subtle); }  /* centred on its hour's line */
.cal-col { position: relative; isolation: isolate; min-width: max(var(--cal-col-min), calc(var(--col-lanes, 1) * (var(--cal-event-min) + var(--space-1)) + var(--space-1) + var(--hairline))); height: calc(var(--hours) * var(--cal-hour-h) + var(--space-2)); padding-top: var(--space-2); border-left: var(--hairline) solid var(--color-border); background: repeating-linear-gradient(to bottom, var(--color-border) 0 var(--hairline), transparent var(--hairline) var(--cal-hour-h)) no-repeat content-box border-box; }
.cal-col.is-today, .cal-allday-cell.is-today { background-color: var(--color-surface-alt); }
.cal-col .event { --event-w: max(var(--cal-event-min), calc((100% - var(--space-1)) / var(--lanes) - var(--space-1))); position: absolute; z-index: var(--lane); top: calc(var(--space-2) + var(--start) * var(--cal-hour-h) / 60 + var(--hairline)); left: calc(var(--space-1) + var(--lane) * (100% - 2 * var(--space-1) - var(--event-w)) / max(var(--lanes) - 1, 1)); width: var(--event-w); height: max(calc(var(--lh-xs) + 2 * var(--hairline)), calc(var(--span) * var(--cal-hour-h) / 60 - var(--space-1))); box-shadow: 0 0 0 var(--hairline) var(--color-surface); }
.cal-col .event:hover, .cal-col .event:focus-visible { z-index: var(--lanes); }
.cal-col .event:not(.is-tall) .event-sub { display: none; }  /* an entry under an hour and a half has no room for its sub line */

/* An event card: white with a hairline and the person's colour as its left bar (the one coloured-bar card the design allows). The
   time and a Google entry's EXT, then the title, then the person and customer; .is-short puts the time, title and EXT on one line.
   The time never gives way, and EXT stays while it fits beside the time: on one line the title takes only the room they leave (its
   basis 0, and its gap a margin on its text, so a title with no room costs EXT nothing), ellipsed down to --event-title-min. Each is
   whole (or ellipsed) or not at all: too narrow, it wraps onto a hidden second line. */
.event { display: flex; flex-wrap: wrap; align-content: flex-start; column-gap: var(--space-1); min-width: 0; padding: var(--space-1) var(--space-2); border: var(--hairline) solid var(--color-border-strong); border-left: var(--event-bar-w) solid var(--event-bar, var(--color-border-strong)); border-radius: var(--radius-xs); background: var(--color-surface); color: var(--color-text); font: inherit; font-size: var(--text-xs); line-height: var(--lh-xs); text-align: left; overflow: hidden; cursor: pointer; transition: filter var(--duration-fast) var(--easing-standard); }
.event:hover { color: var(--color-text); filter: brightness(.97); }
.event:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.event-time { flex: none; font-weight: var(--weight-medium); color: var(--color-text); white-space: nowrap; }
.event-tag { flex: 1 1000 0; min-width: 0; max-height: 1lh; overflow: hidden; text-align: right; font-weight: var(--weight-medium); color: var(--color-text-subtle); text-decoration: none; cursor: inherit; }
.event-tag::before { content: "\200B"; }  /* EXT shows whole or not at all: too narrow, it wraps onto the hidden second line */
.event-title { flex: 1 0 100%; min-width: 0; max-height: 1lh; font-weight: var(--weight-light); color: var(--color-text-body); overflow: hidden; }
.event-title::before { content: "\200B"; }
.event-title > span { display: inline-block; vertical-align: top; min-width: var(--event-title-min); max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-decoration: inherit; }  /* an inline block takes no strike-through from its parent, so it inherits a cancelled job's */
.event-sub { flex: 1 0 100%; min-width: 0; color: var(--color-text-subtle); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.event.is-short { flex-wrap: nowrap; align-items: center; column-gap: 0; padding-block: 0; }
.event.is-short .event-title { flex: 1 1 0; }
.event.is-short .event-title > span { max-width: calc(100% - var(--space-1)); margin-left: var(--space-1); }
.event.is-short .event-tag { flex: 0 1 auto; order: 1; margin-left: var(--space-1); }
.cal-allday .event.is-short .event-title > span { max-width: 100%; margin-left: 0; }  /* no time before it */
.event.is-short .event-sub { display: none; }
.event.is-cancelled .event-time, .event.is-cancelled .event-title { color: var(--color-text-subtle); }
.event.is-cancelled .event-title { text-decoration: line-through; }
.event-1 { --event-bar: var(--rpp-orange-500); }
.event-2 { --event-bar: var(--tint-blue-fg); }
.event-3 { --event-bar: var(--status-green-fg); }
.event-4 { --event-bar: var(--tint-purple-fg); }
.event-5 { --event-bar: var(--tint-amber-fg); }
.event-6 { --event-bar: var(--rpp-grey-800); }
.event-0 { --event-bar: var(--rpp-grey-400); }
/* Blocked-out time: a grey card with a grey bar, whoever's it is, its title Medium. */
.event-block { --event-bar: var(--color-border-strong); background: var(--color-surface-alt); }
.event-block .event-title { font-weight: var(--weight-medium); }

/* A Google entry's or a block's details: the dialog shell at the confirm width, its rows stacked. */
.confirm-dialog.cal-detail .app-dialog-body { flex-direction: column; align-items: stretch; gap: var(--space-4); }
.cal-detail .app-dialog-title { min-width: 0; overflow-wrap: anywhere; }  /* a block's or a Google event's title as typed */
.cal-detail-row { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--inset-control); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); font-size: var(--text-sm); line-height: var(--lh-sm); }
.cal-detail-row > .icon { color: var(--color-text-subtle); }
.cal-detail-row > .text-strong { min-width: 0; overflow-wrap: anywhere; }  /* a Google event's place as typed, even a long web address */
.cal-detail-row > .tag { margin-left: auto; }
.cal-detail-note { padding: var(--space-2) var(--inset-control); border-radius: var(--radius-card); background: var(--color-surface-alt); }
.cal-detail-notes { font-size: var(--text-sm); line-height: var(--lh-sm); white-space: pre-line; overflow-wrap: anywhere; }  /* a block's notes, their lines kept */
.cal-detail-when { justify-content: space-between; }
.cal-detail-label { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text-subtle); }
.cal-detail-value { display: flex; flex-direction: column; align-items: flex-end; text-align: right; }

/* Month: Monday-first weeks; a day lists up to three entries on one line each. On a phone only their count stays. */
.cal-month-week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.cal-month-week + .cal-month-week { border-top: var(--hairline) solid var(--color-border); }
.cal-month-dow { padding: var(--space-2) var(--space-1); text-align: center; font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text-subtle); text-decoration: none; }
.cal-month-day { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; min-height: var(--cal-day-min-h); padding: var(--space-1) var(--space-1) var(--space-2); }
.cal-month-day + .cal-month-day { border-left: var(--hairline) solid var(--color-border); }
.cal-month-day.is-today { background: var(--color-surface-alt); }
.cal-month-num { align-self: flex-start; display: inline-flex; align-items: center; justify-content: center; min-width: var(--avatar-24); height: var(--avatar-24); border-radius: var(--radius-circle); font-size: var(--text-xs); font-weight: var(--weight-medium); color: var(--color-text); transition: background var(--duration-fast) var(--easing-standard); }
.cal-month-num:hover { background: var(--color-border); color: var(--color-text); }
.cal-month-num:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.cal-month-day.is-outside .cal-month-num { color: var(--color-text-disabled); }
.cal-month-day.is-today .cal-month-num { background: var(--color-primary); color: var(--color-text-inverse); }
.cal-more { padding: 0 var(--space-2); font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); }
.cal-month-count { display: none; align-items: center; gap: var(--space-1); font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); }
@media (max-width: 600px) {
  .cal-month-day { align-items: center; min-height: var(--space-16); }
  .cal-month-num { align-self: center; }
  .cal-month-day .event, .cal-more { display: none; }
  .cal-month-count { display: inline-flex; }
}

/* Agenda: each day's entries as rows of event cards, the times, titles and pills in columns down the whole week. */
.cal-agenda { display: grid; grid-template-columns: max-content minmax(0, 1fr) auto; row-gap: var(--space-6); }
.cal-agenda > .empty { grid-column: 1 / -1; }
.cal-agenda-group, .cal-agenda-list, .cal-agenda-list > li, .event.cal-agenda-item { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; }
.cal-agenda-day { grid-column: 1 / -1; margin-bottom: var(--space-3); font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); }
.cal-agenda-list { list-style: none; padding: 0; row-gap: var(--space-2); }
.event.cal-agenda-item { align-items: center; column-gap: var(--space-4); padding: var(--space-3) var(--space-4); font-size: var(--text-sm); line-height: var(--lh-sm); }
.cal-agenda-item > :last-child { justify-self: end; }
.cal-agenda-body { display: flex; flex-direction: column; min-width: 0; }
.cal-agenda-item .event-title { font-weight: var(--weight-medium); color: var(--color-text); }
.cal-agenda-item .event-sub { font-size: var(--text-xs); line-height: var(--lh-xs); }
@media (max-width: 600px) {
  .cal-agenda { grid-template-columns: minmax(0, 1fr) auto; }
  .cal-agenda-item .event-time { grid-column: 1 / -1; }
}

/* The side panel: the mini month, Photographers, Today and Google Calendar cards. */
.cal-mini-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); margin-bottom: var(--space-3); }
.cal-mini-head .card-title { margin-bottom: 0; }
.cal-mini-nav { display: flex; gap: var(--space-1); }
.cal-mini-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.cal-mini-dow { padding-bottom: var(--space-1); text-align: center; font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text-subtle); text-decoration: none; }
.cal-mini-day { display: flex; align-items: center; justify-content: center; height: var(--mini-day-h); font-size: var(--text-xs); font-weight: var(--weight-light); color: var(--color-text-body); }
.cal-mini-day > span { display: inline-flex; align-items: center; justify-content: center; width: var(--avatar-24); height: var(--avatar-24); border-radius: var(--radius-circle); transition: background var(--duration-fast) var(--easing-standard); }
.cal-mini-day:hover { color: var(--color-text); }
.cal-mini-day:hover > span { background: var(--color-border); }
.cal-mini-day:focus-visible { outline: none; }
.cal-mini-day:focus-visible > span { box-shadow: var(--focus-ring); }
.cal-mini-day.is-shown { background: var(--color-surface-alt); font-weight: var(--weight-medium); color: var(--color-text); }
.cal-mini-day.is-today > span { background: var(--color-primary); color: var(--color-text-inverse); font-weight: var(--weight-medium); }
.legend { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--color-text-body); }
.legend li { display: flex; align-items: center; gap: var(--gap-control); }
.swatch { display: inline-block; width: var(--swatch); height: var(--swatch); border-radius: var(--radius-xs); flex: none; }
.legend .num { margin-left: auto; font-weight: var(--weight-medium); color: var(--color-text); }
.cal-summary { margin: 0; }
.cal-summary > div { display: flex; justify-content: space-between; gap: var(--space-3); padding: var(--space-2) 0; border-bottom: var(--hairline) solid var(--color-border); font-size: var(--text-sm); line-height: var(--lh-sm); }
.cal-summary dt { color: var(--color-text-body); }
.cal-summary dd { margin: 0; font-weight: var(--weight-medium); color: var(--color-text); }
.cal-summary + .btn { margin-top: var(--space-4); }  /* Today's Block out time, under its rows */
.cal-google-status { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3); }
.cal-google .hint { margin-top: var(--space-2); }

/* ---------- Orders and files ---------- */
.subhead { font-size: var(--text-base); font-weight: var(--weight-medium); margin: var(--space-5) 0 var(--space-3); display: flex; align-items: center; gap: var(--space-2); }
.dropzone { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); border: var(--hairline) dashed var(--rpp-orange-200); border-radius: var(--radius-card); background: var(--color-primary-wash); color: var(--color-text); text-align: left; cursor: pointer; transition: border-color var(--duration-fast) var(--easing-standard), background var(--duration-fast) var(--easing-standard); }
.dropzone:hover, .dropzone.over { border-color: var(--color-primary); background: var(--color-primary-tint); }
.dropzone:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.dropzone-badge { width: var(--space-10); height: var(--space-10); border-radius: var(--radius-circle); background: var(--color-surface); color: var(--color-primary-hover); display: flex; align-items: center; justify-content: center; flex: none; }
.dropzone-text { display: flex; flex-direction: column; gap: var(--space-1); }
.dropzone-title { font-size: var(--text-sm); font-weight: var(--weight-medium); }
.dropzone-title .hint-strong { color: var(--color-primary-hover); }
.dropzone .hint { color: var(--color-text-muted); }
.upload-list { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.upload-list:empty { display: none; }
.upload-row { display: grid; grid-template-columns: minmax(0, 1fr) 70px 90px 120px auto; gap: var(--space-3); align-items: center; font-size: var(--text-sm); line-height: var(--lh-sm); padding: var(--space-2) var(--space-3); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); }
.upload-name { color: var(--color-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.upload-size, .upload-status { color: var(--color-text-subtle); font-size: var(--text-xs); }
.upload-row.done .upload-status { color: var(--status-green-fg); }
.upload-row.failed .upload-status { color: var(--color-primary-hover); }
.upload-bar { height: 6px; background: var(--color-surface-alt); border-radius: var(--radius-pill); overflow: hidden; }
.upload-fill { display: block; height: 100%; width: 0; background: var(--color-primary); transition: width var(--duration-fast) linear; }
.upload-row.done .upload-fill { width: 100%; background: var(--status-green); }
.file-grid { list-style: none; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(var(--file-tile-min), 100%), 1fr)); gap: var(--space-4); }
.file-tile { position: relative; border: var(--hairline) solid var(--color-border); border-radius: var(--radius-image);  /* a tile that opens with a photo is square, like the photo */ overflow: hidden; display: flex; flex-direction: column; background: var(--color-surface); }
.file-thumb { display: block; aspect-ratio: 4 / 3; background: var(--color-surface-alt); overflow: hidden; }
.file-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.file-icon { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; gap: var(--space-1); color: var(--color-text-subtle); font-size: var(--text-xs); font-weight: var(--weight-medium); }
.file-meta { padding: var(--space-2) var(--space-3) var(--space-3); display: flex; flex-direction: column; }
.file-name { font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.file-size { font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); }
.file-tag { align-self: flex-start; margin-top: var(--space-2); }
.file-actions { position: absolute; top: var(--space-2); right: var(--space-2); display: flex; gap: var(--space-1); opacity: 0; transition: opacity var(--duration-fast) var(--easing-standard); }
.file-tile:hover .file-actions, .file-tile:focus-within .file-actions { opacity: 1; }
.file-actions form { margin: 0; }
.file-action { display: inline-flex; align-items: center; justify-content: center; width: var(--control-h-xs); height: var(--control-h-xs); border: 0; border-radius: var(--radius-tile); background: var(--overlay-ink); color: var(--color-text-inverse); cursor: pointer; }
.file-action:hover { background: var(--rpp-grey-900); color: var(--color-text-inverse); }
.file-action:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.archive-actions { margin-bottom: var(--space-4); }

/* ---------- Public pages ---------- */
.public-body { background: var(--color-bg); }
.public-header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); padding: var(--space-4) var(--space-8); border-bottom: var(--hairline) solid var(--color-border); background: var(--color-surface); }
.public-brand { display: flex; align-items: center; gap: var(--space-4); min-width: 0; color: var(--color-text); }
.public-logo { height: var(--logo-h); width: auto; display: block; flex: none; }
.public-studio { padding-left: var(--space-4); border-left: var(--hairline) solid var(--color-border); font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--color-text); }
.public-page { max-width: calc(var(--public-w) + 2 * var(--space-6)); margin: 0 auto; padding: var(--space-8) var(--space-6) var(--space-16); }
.public-hero { text-align: left; margin-bottom: var(--space-8); display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); }
.public-hero.centred { text-align: center; align-items: center; }
.public-kicker { font-size: var(--text-xs); line-height: var(--lh-eyebrow); font-weight: var(--weight-medium); letter-spacing: var(--tracking-eyebrow); text-transform: uppercase; color: var(--color-primary); }
.public-title { font-weight: var(--weight-light); font-size: var(--text-h3); line-height: var(--lh-h3); letter-spacing: var(--tracking-heading); max-width: var(--title-max); }
@media (min-width: 900px) { .public-title { font-size: var(--text-h2); line-height: var(--lh-h2); } }
.public-footer { text-align: center; color: var(--color-text-subtle); font-size: var(--text-xs); line-height: var(--lh-xs); margin-top: var(--space-12); }
.public-notice { max-width: 480px; margin: 10vh auto; padding: var(--space-8); }
.hint-inverse { opacity: .75; font-weight: var(--weight-regular); }
.btn.is-waiting { cursor: progress; }
.menu-item.is-waiting { color: var(--color-text-subtle); cursor: progress; }
.menu-item.is-waiting:hover { background: none; color: var(--color-text-subtle); }
/* The delivery page's header, the one dark field: the job's cover behind it, shaded so white type reads over any photo, the lock-up,
   the studio's name and the Download menu, then the hero. Its content lines up with the page's. A layer of its own, so the cover sits
   under its content, and over the page under it (the tiles and their downloads, at z-index 1), so its Download menu opens over the
   tiles. */
.delivery-header { position: relative; z-index: 2; background: var(--rpp-grey-900); color: var(--color-text-inverse); }
.delivery-cover { position: absolute; inset: 0; z-index: -1; width: 100%; height: 100%; object-fit: cover; }
.delivery-header.has-cover::after { content: ""; position: absolute; inset: 0; z-index: -1; background: var(--overlay-cover); }
.delivery-header-inner { max-width: calc(var(--public-w) + 2 * var(--space-6)); margin: 0 auto; padding: var(--space-6) var(--space-6) var(--space-16); }
.delivery-bar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.delivery-header .public-studio { color: var(--color-text-inverse); border-left-color: var(--overlay-line); }
.delivery-hero { margin: var(--space-16) 0 0; gap: var(--space-4); }
.delivery-hero .public-kicker { display: flex; align-items: center; gap: var(--space-2); }
.delivery-header .public-title { color: var(--color-text-inverse); }
.delivery-lede { max-width: var(--lede-max); color: var(--rpp-grey-300); }
.delivery-counts { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-6); margin-top: var(--space-2); padding: 0; list-style: none; font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--rpp-grey-200); }
.delivery-counts li { display: inline-flex; align-items: center; gap: var(--space-2); }
/* Its Download menu, an inverse pill; while payment is due the pill can't open and says why under it, out of the bar's way. */
.delivery-downloads { flex: none; }
.delivery-downloads .menu-panel { width: var(--side-panel-w); max-width: calc(100vw - 2 * var(--space-4)); }
.delivery-downloads.is-locked { position: relative; }
.delivery-downloads-note { position: absolute; right: 0; top: calc(100% + var(--space-1)); white-space: nowrap; font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--rpp-grey-300); }
/* The page under it: the notices and the payment banner, then a section per visible top-level folder, in the job's order, its eyebrow,
   name and count over a grid of tiles, four across (two below 960px, one below 600px); a child's files are another grid in its
   parent's section, picked by the Includes: chips below. */
.delivery-page { padding-top: var(--space-10); padding-bottom: var(--space-12); }
.public-actions { display: flex; flex-direction: column; gap: var(--space-4); }
.public-actions:has(.callout, .pay-banner, .btn) { margin-bottom: var(--space-10); }  /* only when it holds something */
.public-actions .callout { margin-bottom: 0; }
.pay-frame { display: flex; flex-direction: column; gap: var(--space-4); }
.pay-frame > :empty { display: none; }  /* the notice's holder, empty while there is none */
.pay-banner { display: flex; align-items: center; gap: var(--space-4); padding: var(--space-5) var(--space-6); background: var(--rpp-orange-100); border: var(--hairline) solid var(--rpp-orange-300); border-radius: var(--radius-card); }
.pay-banner-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.pay-banner-title { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); color: var(--color-text); }
.pay-banner-title .icon { color: var(--color-primary); }
.pay-banner-sub { margin-top: var(--space-1); font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-body); }
.pay-banner a { color: inherit; text-decoration: underline; }  /* orange is too faint on the orange-100 fill; a link reads as one by its underline, as in a callout */
.pay-banner .pay-amount { margin-top: var(--space-2); }
.pay-banner form { margin: 0; flex: none; }
.pay-amount { font-size: var(--text-lg); line-height: var(--lh-lg); font-weight: var(--weight-light); color: var(--color-text); letter-spacing: var(--tracking-heading); }
.delivery-folder + .delivery-folder { margin-top: var(--space-10); }
.delivery-folder > .page-header { margin-bottom: var(--space-8); }
/* A section whose folder's children hold files: under its title the folder glyph, "Includes:" and a chip per grid (the folder's own
   files, then each child's), tags that switch the grid shown, the shown one's pressed like the current secondary button. */
.delivery-includes { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-bottom: var(--space-8); font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text-body); }
.delivery-includes > .icon { color: var(--color-text-subtle); }
.delivery-chip { min-width: 0; max-width: 100%; border: var(--hairline) solid transparent; cursor: pointer; transition: background var(--duration-fast) var(--easing-standard), border-color var(--duration-fast) var(--easing-standard), color var(--duration-fast) var(--easing-standard); }
.delivery-chip[aria-pressed="false"]:hover { background: var(--color-border-strong); }
.delivery-chip:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.delivery-chip-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }  /* a child's long name shortens inside the section on a phone; the chip's title has it whole */
.gallery { list-style: none; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-6) var(--space-3); align-items: start; }
/* A tile: the thumbnail filling it, square-cornered, or the grey tile with its kind's icon in a white circle; locked, the circle
   holds the lock, over the thumbnail too. Unlocked, a download shows on hover, or always on touch. */
.gallery-item { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.gallery-tile { position: relative; aspect-ratio: 3 / 2; overflow: hidden; border-radius: var(--radius-image); background: var(--rpp-grey-200); }
.gallery-thumb { display: block; width: 100%; height: 100%; background: none; border: 0; padding: 0; cursor: zoom-in; }
.gallery-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: opacity var(--duration-fast) var(--easing-standard); }
.gallery-tile:hover .gallery-thumb:not(.is-locked) img { opacity: .88; }
.gallery-thumb:focus-visible { outline: none; box-shadow: inset var(--focus-ring); }
.gallery-video, .gallery-file { cursor: pointer; transition: background var(--duration-fast) var(--easing-standard); }
.gallery-video:hover, .gallery-file:not(.is-locked):hover { background: var(--color-border-strong); }
.gallery-file.is-locked { cursor: default; }
.gallery-thumb.is-locked img { filter: saturate(.6); opacity: .85; }
.gallery-glyph { position: absolute; inset: 0; margin: auto; display: flex; align-items: center; justify-content: center; width: var(--avatar-36); height: var(--avatar-36); border-radius: var(--radius-circle); background: var(--color-surface); color: var(--color-text); pointer-events: none; }
.gallery-tile .file-actions { z-index: 1; }
.gallery-tile:hover .file-actions, .gallery-tile:focus-within .file-actions { opacity: 1; }
/* Touch has no hover, so the download stays visible. This must sit after every rule above that sets its opacity, since a media query
   adds no specificity. */
@media (hover: none) { .file-actions { opacity: 1; } }
/* Under the tile its name beside Comments, then its size line across, so "unlocks after payment" keeps to one line. An image's
   Comments opens the lightbox with its Pin a revision panel; any other file's opens its thread in a dialog: the file, then the thread
   and the form in a body that scrolls, laid out in reverse so it opens at the latest comment above the form. No avatars there: the
   dialog is kept as it was; only the image's panel has initials, on the brand tint. */
.gallery-meta { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; column-gap: var(--space-2); }
.gallery-meta .file-name { grid-area: 1 / 1; }
.gallery-meta .file-size { grid-area: 2 / 1 / 3 / 3; }
.gallery-comments { grid-area: 1 / 2; }
@media (max-width: 960px) { .gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) {
  .gallery { grid-template-columns: minmax(0, 1fr); }
  .public-page, .delivery-header-inner { padding-left: var(--space-4); padding-right: var(--space-4); }
  .delivery-bar .public-brand { flex-direction: column; align-items: flex-start; gap: var(--space-1); }  /* the studio's name under the lock-up, so it keeps to a line beside Download */
  .delivery-bar .public-studio { padding-left: 0; border-left: 0; }
  .pay-banner { flex-direction: column; align-items: flex-start; }
}
/* "How did we do?" under the folders, headed like a folder's section over a white card: a 16px Medium title over a 14px Light line,
   then Overall rating, five 20px stars that are a radio group, grey-300 outlines, each in a 28px target reaching past the row (so the
   outlines line up with the label and keep the field's spacing); the chosen one and those before it fill orange, and while the
   pointer is over them, the one under it and those before it instead, except while the arrow keys choose (the focus ring shows),
   when the stars follow the keys though the pointer rests on one. Given, the card thanks the client over the stars filled to the
   rating, read only, as on the job page's Client review card, and the review's text as typed, with Edit. */
.delivery-review { margin-top: var(--space-10); scroll-margin-top: var(--space-8); }
.delivery-review > .page-header { margin-bottom: var(--space-8); }
.review-form { display: flex; flex-direction: column; gap: var(--space-4); }
.review-form > .callout { margin-bottom: 0; }  /* the form's gap spaces it */
.review-title { font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); color: var(--color-text); }
.review-sub { margin-top: var(--space-1); font-size: var(--text-sm); line-height: var(--lh-sm); }
.review-stars { display: inline-flex; align-self: flex-start; margin: calc(-1 * var(--space-1)); }  /* only as wide as its stars, so they light only with the pointer over one */
.review-star { position: relative; display: flex; align-items: center; justify-content: center; width: var(--control-h-sm); height: var(--control-h-sm); border-radius: var(--radius-field); color: var(--color-border-strong); cursor: pointer; transition: color var(--duration-fast) var(--easing-standard); }
.review-star .icon { fill: var(--star-fill, none); }
.review-stars:has(:checked) .review-star, .review-stars:hover:not(:has(:focus-visible)) .review-star { --star-fill: currentColor; color: var(--color-primary); }
.review-stars:is(:not(:hover), :has(:focus-visible)) .review-star:has(:checked) ~ .review-star, .review-stars:not(:has(:focus-visible)) .review-star:hover ~ .review-star { --star-fill: none; color: var(--color-border-strong); }
.review-star:has(:focus-visible) { box-shadow: var(--focus-ring); }
.review-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.review-given { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); }
.review-rating { display: inline-flex; gap: var(--space-2); color: var(--color-border-strong); }
.review-rating .is-on { color: var(--color-primary); fill: currentColor; }
.comments-dialog-head { align-items: center; padding: var(--space-5) var(--space-5) var(--space-4); border-bottom: var(--hairline) solid var(--color-border); flex: none; }
.comments-dialog-file { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.comments-dialog-name { overflow-wrap: anywhere; }
.comments-dialog-body { display: flex; flex-direction: column-reverse; min-height: 0; overflow: auto; padding: var(--space-5); }
.comments-dialog-content { display: flex; flex-direction: column; gap: var(--space-5); }
.comments-dialog .comments { margin: 0; }
.comments-plain .comment { grid-template-columns: minmax(0, 1fr); }
/* A file at 48px, square: its thumbnail, or its kind's icon (a thread's file here and on the job's Client feedback tab). */
.file-mini { display: flex; align-items: center; justify-content: center; width: var(--space-12); height: var(--space-12); flex: none; object-fit: cover; border-radius: var(--radius-image); background: var(--color-surface-alt); color: var(--color-text-muted); }
/* The lightbox: the file's name over its place in its folder and size, Download (none while payment is due) and Close at the top
   right, the photo fitted between Previous and Next, each control a 44px tile, and right of Next the image's Pin a revision panel.
   The photo is the box its pins are placed in, fractions of it: it fits the stage (a size container, as the Notes dialog's canvas),
   crosshaired and focusable, its pins drawn as a bracket's are. Below 960px the panel goes under the photo and the lightbox scrolls;
   the photo keeps its shape, at most 60% of the screen's height. On a phone Previous and Next go under the photo too. */
.lightbox { border: 0; padding: 0; background: transparent; width: 100vw; height: 100vh; max-width: none; max-height: none; color: var(--color-text-inverse); }
.lightbox[open] { display: flex; flex-direction: column; }
.lightbox::backdrop { background: var(--overlay-ink); }
.lightbox-top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); padding: var(--space-5) var(--space-8); }
.lightbox-title { display: flex; flex-direction: column; min-width: 0; }
.lightbox-name { font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lightbox-count { font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--rpp-grey-400); }
.lightbox-actions { display: flex; gap: var(--space-2); flex: none; }
.lightbox-body { flex: 1; min-height: 0; display: flex; align-items: center; gap: var(--space-8); padding: 0 var(--space-8) var(--space-8); }
.lightbox-stage { flex: 1; min-width: 0; align-self: stretch; container-type: size; display: flex; align-items: center; justify-content: center; }
.lightbox-photo { position: relative; line-height: 0; cursor: crosshair; }
.lightbox-photo img { display: block; max-width: 100cqw; max-height: 100cqh; border-radius: var(--radius-image); }
.lightbox-photo:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.lightbox-pins { position: absolute; inset: 0; }
.lightbox-tile { display: inline-flex; align-items: center; justify-content: center; flex: none; width: var(--rail-link); height: var(--rail-link); border: 0; border-radius: var(--radius-tile); background: var(--overlay-tile); color: var(--color-text-inverse); cursor: pointer; transition: background var(--duration-fast) var(--easing-standard); }
.lightbox-tile:hover { background: var(--overlay-line); color: var(--color-text-inverse); }
.lightbox-tile:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* The panel: white on the dark, card corners, its title over a hint, the thread (scrolling inside it, laid out in reverse so it opens at
   the latest comment, which an auto margin keeps at the top while it is short), then the form at its foot. Each comment as the
   design's: 28px initials on the brand tint, the 18px pin badge when pinned, the name and time ago at 12px over the comment. */
.lightbox-panel { flex: none; align-self: stretch; display: flex; flex-direction: column; gap: var(--space-4); width: var(--side-panel-w); min-height: 0; padding: var(--space-5); border-radius: var(--radius-card); background: var(--color-surface); color: var(--color-text-body); }
.lightbox-panel-title { font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); }
.lightbox-panel-sub { margin-top: var(--space-1); }
.lightbox-panel-thread { flex: 1; min-height: 0; display: flex; flex-direction: column-reverse; overflow: auto; }
.lightbox-panel-content { display: flex; flex-direction: column; gap: var(--space-4); margin-bottom: auto; }
.lightbox-panel-empty { font-size: var(--text-sm); line-height: var(--lh-sm); }  /* an empty thread at the comments' 14/20, under the 16px title rather than matching it */
.lightbox-comments { display: flex; flex-direction: column; gap: var(--space-4); margin: 0; }
.lightbox-comments .comment { padding: 0; border-top: 0; }
.lightbox-comments .comment-head { align-items: center; font-size: var(--text-xs); line-height: var(--lh-xs); }
.comment-as, .lightbox-pin-note { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--space-2); font-size: var(--text-sm); line-height: var(--lh-sm); overflow-wrap: anywhere; }
.lightbox-pin-status { display: contents; }  /* a live region round the pin's line, which takes no gap of the form's while it is hidden */
@media (max-width: 960px) {
  .lightbox[open] { overflow-y: auto; }
  .lightbox-body { flex: none; flex-wrap: wrap; row-gap: var(--space-6); }
  .lightbox-stage { align-self: center; container-type: inline-size; }
  .lightbox-photo img { max-height: 60svh; }
  .lightbox-panel { flex-basis: 100%; width: auto; }
  .lightbox-panel-thread { overflow: visible; }
}
@media (max-width: 600px) {
  .lightbox-top { padding: var(--space-4); }
  /* Previous and Next drop under the photo, one at each end: beside it they took a third of the width and left the photo narrower than
     its own tile on the page. */
  .lightbox-body { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); padding: 0 var(--space-4) var(--space-4); }
  .lightbox-stage { grid-column: 1 / -1; grid-row: 1; }
  .lightbox-next { justify-self: end; }
  .lightbox-panel { grid-column: 1 / -1; margin-top: var(--space-2); }
}
.link-row { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-4); }
.link-row .label { flex: none; }
.link-row .input { flex: 1; }
@media (max-width: 600px) { .link-row { flex-wrap: wrap; } .link-row .input { flex-basis: 100%; } }

/* ---------- Payments and settings ---------- */
.setting-row { display: flex; align-items: center; gap: var(--space-4); padding: var(--space-3) var(--space-4); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); box-shadow: var(--shadow-card); background: var(--color-surface); cursor: pointer; }
.setting-badge { width: var(--avatar-40); height: var(--avatar-40); border-radius: var(--radius-circle); background: var(--tint-blue-bg); color: var(--tint-blue-fg); display: flex; align-items: center; justify-content: center; flex: none; }
.setting-badge-purple { background: var(--tint-purple-bg); color: var(--tint-purple-fg); }
.setting-badge-amber { background: var(--tint-amber-bg); color: var(--tint-amber-fg); }
.setting-text { flex: 1; display: flex; flex-direction: column; gap: var(--space-1); }
.setting-title { font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text); }
.setting-desc { font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-muted); }
.toggle { appearance: none; width: var(--toggle-w); height: var(--toggle-h); margin: 0; border-radius: var(--radius-pill); background: var(--rpp-grey-300); position: relative; cursor: pointer; transition: background var(--duration-fast) var(--easing-standard); flex: none; }
.toggle::after { content: ""; position: absolute; top: var(--thumb-inset); left: var(--thumb-inset); width: var(--toggle-thumb); height: var(--toggle-thumb); border-radius: var(--radius-circle); background: var(--color-surface); box-shadow: var(--shadow-sm); transition: transform var(--duration-fast) var(--easing-standard); }
.toggle:checked { background: var(--color-primary); }
.toggle:checked::after { transform: translateX(calc(var(--toggle-w) - var(--toggle-thumb) - 2 * var(--thumb-inset))); }
.toggle:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* A switch that can't be flipped (an inactive product's Live on booking form) is greyed out: grey-200 off, grey-300 on, its knob
   still showing which way it is set. */
.toggle:disabled { cursor: default; background: var(--rpp-grey-200); }
.toggle:checked:disabled { background: var(--rpp-grey-300); }
/* Integrations' tiles, side by side while each has --integration-tile-min, sharing their row: a card at a 20px inset, its name in
   16px Medium with its status pill on the right, the line saying what it does under it, then its state, its actions at its foot.
   A Xero connection takes the whole row for its defaults, and the tiles after it share the next. */
.integration-tiles { display: flex; flex-wrap: wrap; gap: var(--space-4); }
.integration-tile { flex: 1 1 var(--integration-tile-min); min-width: 0; display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-5); }
.integration-tile-wide { flex-basis: 100%; }
.integration-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.integration-name { font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); }
.integration-tile .dl { margin-top: 0; }
.integration-tile .inline-form { flex-wrap: wrap; }
.integration-tile > .callout { margin-bottom: 0; }  /* the tile's gap spaces it */
.integration-actions { margin-top: auto; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.integration-actions form { margin: 0; }

/* ---------- Settings frame ----------
   The settings nav beside the page, 24px from it: the section list's card (above) at a 16px and 12px inset, the title in 16px
   Medium after its ink glyph, Search settings 12px under it and 16px over the first group, each group's label in the topbar
   eyebrow's style. Search settings' matches lie over the page on a menu panel at least 320px wide, as the topbar search's rows. In
   .settings-main the cards are 24px apart and fill the column, their field labels are 14px, two-field rows 16px apart (a dialog
   opened from a card keeps a dialog's own 12px labels and pairs), setting rows a hairline without a shadow, the description
   grey-600 straight under the name (as in a card's head), and a table stands inside its card's inset. Below 960px the nav sits
   above the page and scrolls with it. */
.settings { display: grid; grid-template-columns: var(--settings-nav-w) minmax(0, 1fr); gap: var(--space-6); align-items: start; }
/* Sticky (.section-nav) makes the nav a stacking context of its own, so Search settings' list, which reaches past the nav over
   the page, is drawn no higher than the nav. The z-index lifts the nav, and the list with it, over the page's positioned content
   (a service area's colour picker and map). */
.settings-nav { z-index: 1; padding: var(--space-4) var(--space-3); }
.settings-nav-title { display: flex; align-items: center; gap: var(--gap-control); padding: var(--space-1) var(--space-2); font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); color: var(--color-text); }
.settings-nav-group { padding: var(--space-3) var(--space-3) var(--space-1); font-size: var(--text-xs); line-height: var(--lh-eyebrow); font-weight: var(--weight-medium); letter-spacing: var(--tracking-eyebrow); text-transform: uppercase; color: var(--color-text-subtle); }
.settings-search { position: relative; margin: var(--space-3) 0 var(--space-4); }
.settings-search .search-panel { top: calc(100% + var(--space-1)); left: 0; right: auto; width: max(100%, var(--side-panel-w)); }  /* under its field at every width; below 960px the topbar search's moves under the whole topbar */
.settings-main > .stack { gap: var(--space-6); }
.settings-main .card { scroll-margin-top: var(--space-8); }  /* a card Search settings opens stands clear of the window's top */
.settings-main .record-card-bar > .section-head { flex: 1 1 var(--section-head-min); }  /* a long line under the title wraps beside the card's actions, which drop under it only on a narrow card */
.settings-main .label:not(.app-dialog *) { font-size: var(--text-sm); line-height: var(--lh-sm); }
.settings-main .grid-2:not(.app-dialog *) { gap: var(--space-4); }
.settings-main .setting-row { box-shadow: none; }
.settings-main .setting-row .setting-desc { color: var(--color-text-subtle); }
.settings-main .setting-text { gap: 0; }
.settings-main .link-row { margin-bottom: 0; }
.settings-main .stack > .callout { margin-bottom: 0; }  /* the stack's gap spaces it */
/* A table in a settings card (Team's members), as drawn: inside the card's inset, its head without the grey band, a hairline under
   every row, the last included. */
.settings-main .card:not(.card-flush) > .table { margin: 0; width: 100%; }
.settings-main .card:not(.card-flush) > .table :is(th, td) { padding: var(--space-3) var(--space-4) var(--space-3) 0; border-bottom: var(--hairline) solid var(--color-border); background: none; }
.settings-main .card:not(.card-flush) > .table th { padding-top: 0; }
.settings-main .card:not(.card-flush) > .table :is(th, td):last-child { padding-right: 0; }
/* Team: Invite member opens the invitation form over the members table, on a grey-100 band at a 20px inset, its title 16px Medium;
   each member is their 36px avatar 12px from the name over the email. */
.invite-panel { margin-bottom: var(--space-5); padding: var(--space-5); border-radius: var(--radius-card); background: var(--color-surface-alt); }
.invite-panel > h3 { margin-bottom: var(--space-3); font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); color: var(--color-text); }
.invite-panel .form-actions { padding-top: 0; }  /* Send invitation 16px under the fields, the form's own gap */
.members-table .with-avatar { gap: var(--space-3); }
/* A team's name as Master typed it, and a pending invitation's name or email as the admin typed it, break anywhere rather than push
   the row past its card. */
.editing-teams li > :last-child, .invitation-list li > :first-child { min-width: 0; overflow-wrap: anywhere; }
/* Each member's row ends in their Manage menu, so the card can't scroll sideways: a long name or email breaks anywhere rather than
   widen the table beside the settings nav. */
.members-table .cell-stack { min-width: 0; overflow-wrap: anywhere; }
/* From a laptop's width, as the Customers list's, Role and Status are fixed as drawn and Manage keeps to its button, so the member
   takes the rest; the widths hold each cell's 16px gap to the next. */
@media (min-width: 1200px) {
  .members-table th:nth-child(2) { width: calc(var(--member-role-w) + var(--space-4)); }
  .members-table th:nth-child(3) { width: calc(var(--member-status-w) + var(--space-4)); }
  .members-table th:last-child { width: var(--control-h-md); }
}
/* Below 960px, as the Customers list's do, each row stacks: the member with Manage at the row's end, then the role tag and status
   pill under the email, from the name's edge. */
@media (max-width: 960px) {
  .members-table thead { display: none; }
  .members-table, .members-table tbody { display: block; }
  .members-table tbody tr { display: grid; grid-template-columns: var(--avatar-36) auto minmax(0, 1fr) auto; gap: var(--space-2) var(--space-3); padding: var(--space-3) 0; border-bottom: var(--hairline) solid var(--color-border); }
  .settings-main .card:not(.card-flush) > .members-table td, .settings-main .card:not(.card-flush) > .members-table td:last-child { display: block; padding: 0; border: 0; }
  .members-table td:first-child { grid-column: 1 / 4; }
  .members-table td:nth-child(2) { grid-column: 2; grid-row: 2; }
  .members-table td:nth-child(3) { grid-column: 3; grid-row: 2; }
  .members-table td.row-action { grid-column: 4; grid-row: 1; }
}
@media (max-width: 960px) { .settings { grid-template-columns: minmax(0, 1fr); } .settings-nav { position: static; } }

/* ---------- Online booking ---------- */
.narrow-page { max-width: 760px; }

/* ---------- Settings groups, question rows, hours table ---------- */
.section-head { display: flex; align-items: flex-start; gap: var(--space-4); }
.section-head .setting-title { font-size: var(--text-base); line-height: var(--lh-base); display: flex; align-items: center; gap: var(--space-2); }
.setting-badge-tile { border-radius: var(--radius-tile); background: var(--color-primary-tint); color: var(--color-primary); }  /* a settings card's head */
.setting-badge-orange { background: var(--tint-orange-bg); color: var(--tint-orange-fg); }
.slug-field { display: flex; align-items: center; }
.slug-prefix { height: var(--control-h-md); display: inline-flex; align-items: center; padding: 0 var(--inset-control); border: var(--hairline) solid var(--color-border-strong); border-right: 0; border-radius: var(--radius-field) 0 0 var(--radius-field); background: var(--color-surface-alt); color: var(--color-text-muted); font-size: var(--text-sm); white-space: nowrap; }
.slug-field .input { border-radius: 0 var(--radius-field) var(--radius-field) 0; }
.unit-field { display: flex; align-items: center; gap: var(--space-2); }
.unit-field .input { width: var(--unit-input-w); }
.unit-field .unit { color: var(--color-text-muted); font-size: var(--text-sm); }
.question-row { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-4); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface); }
/* A repeated row's foot (booking questions, team members): its Remove, the last item, ends it, alone or after Required. */
.question-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.question-foot > :last-child { margin-inline-start: auto; }
.select-time { width: var(--time-select-w); }
.card-foot { padding: var(--space-4) var(--space-6); border-top: var(--hairline) solid var(--color-border); }
/* A file field: its button drawn as a small secondary pill, centred in the field's height, then the chosen file's name. */
.input-file { padding: calc((var(--control-h-md) - var(--control-h-sm)) / 2 - var(--hairline)); }
.input-file::file-selector-button { height: var(--control-h-sm); margin-right: var(--space-3); padding: 0 var(--space-3); border: var(--hairline) solid var(--color-primary-tint); border-radius: var(--radius-button); background: var(--color-surface); box-shadow: var(--shadow-sm); font: inherit; font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--color-text); cursor: pointer; transition: background var(--duration-fast) var(--easing-standard), border-color var(--duration-fast) var(--easing-standard); }
.input-file::file-selector-button:hover { background: var(--color-surface-alt); border-color: var(--rpp-orange-200); }  /* as .btn-secondary:hover */
.product-image-preview { width: var(--product-preview-w); height: auto; display: block; border-radius: var(--radius-image); }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.req { color: var(--color-primary); }

/* ---------- Public booking page ----------
   A fixed top bar (mark, progress, tally), one step at a time, a fixed action
   bar. Widths per step: contact and property narrow, services wide. */
.book { min-height: 100vh; padding: calc(var(--book-bar-h) + var(--space-8)) var(--space-4) calc(var(--book-actions-h) + var(--space-8)); }
.book-bar { position: fixed; top: 0; left: 0; right: 0; height: var(--book-bar-h); display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; padding: 0 var(--space-6); background: var(--color-surface); border-bottom: var(--hairline) solid var(--color-border); z-index: 5; }
.book-mark { height: var(--space-8); width: auto; }
.book-studio { justify-self: end; font-weight: var(--weight-medium); color: var(--color-text); }
.book-progress { list-style: none; margin: 0; padding: 0; display: flex; align-items: center; gap: var(--space-2); }
.book-progress li { width: var(--dot); height: var(--dot); border-radius: var(--radius-pill); background: var(--rpp-grey-300); transition: width var(--duration-normal) var(--easing-standard), background var(--duration-fast) var(--easing-standard); }
.book-progress li.is-done { background: var(--color-primary); }
.book-progress li.is-current { width: var(--book-pill-w); background: var(--color-primary); }
.book-tally { justify-self: end; display: flex; align-items: center; gap: var(--space-2); color: var(--color-text-subtle); }
.book-total { font-weight: var(--weight-medium); color: var(--color-text); }
.book-counter { font-size: var(--text-xs); }
.book-form { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: calc(100vh - var(--book-bar-h) - var(--book-actions-h) - 2 * var(--space-8)); }
.book-form > .callout { width: 100%; max-width: var(--book-w-mid); margin-bottom: var(--space-6); }
.book-step { width: 100%; display: flex; flex-direction: column; gap: var(--space-5); animation: book-in var(--duration-normal) var(--easing-standard); }
.book-w-narrow { max-width: var(--book-w-narrow); }
.book-w-mid { max-width: var(--book-w-mid); }
.book-w-wide { max-width: var(--book-w-wide); }
@keyframes book-in { from { opacity: 0; transform: translateY(var(--space-2)); } to { opacity: 1; transform: none; } }
.book-head { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-2); }
.book-title { font-weight: var(--weight-light); font-size: var(--text-h3); line-height: var(--lh-h3); letter-spacing: var(--tracking-heading); }  /* a step title is a page title; the narrow steps have no room for the greeting size */
.book-sub { color: var(--color-text-muted); font-size: var(--text-base); line-height: var(--lh-base); }
.book-h2 { font-size: var(--text-lg); line-height: var(--lh-lg); font-weight: var(--weight-light); letter-spacing: var(--tracking-heading); margin-bottom: var(--space-3); }
.book-questions { display: flex; flex-direction: column; gap: var(--space-4); padding-top: var(--space-4); border-top: var(--hairline) solid var(--color-border); }
.book-continuing { padding: var(--space-3) var(--space-4); border-radius: var(--radius-card); background: var(--color-surface-alt); color: var(--color-text-muted); font-size: var(--text-sm); }
.book-grid { gap: var(--space-4); }

/* Address suggestions */
.places { position: relative; }
.places-list { list-style: none; margin: 0; font-size: var(--text-sm); line-height: var(--lh-sm); padding: var(--space-1); position: absolute; left: 0; right: 0; top: calc(100% + var(--space-1)); background: var(--color-surface); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); box-shadow: var(--shadow-pop); z-index: 4; max-height: var(--places-max-h); overflow: auto; }
.places-list li { display: flex; flex-direction: column; padding: var(--space-2) var(--space-3); border-radius: var(--radius-field); cursor: pointer; }
.places-list li:hover, .places-list li.is-active { background: var(--color-surface-alt); }
.places-main { font-weight: var(--weight-medium); color: var(--color-text); }
.places-secondary { font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); }

/* Product cards */
.book-section-head { display: flex; align-items: baseline; gap: var(--space-3); }
.book-products { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); }
@media (max-width: 960px) { .book-products { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) { .book-products { grid-template-columns: 1fr; } }
.book-product { display: flex; flex-direction: column; background: var(--color-surface); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-image); box-shadow: var(--shadow-card); overflow: hidden; transition: border-color var(--duration-fast) var(--easing-standard), box-shadow var(--duration-fast) var(--easing-standard); }
.book-product.is-selected { border-color: var(--color-primary); box-shadow: var(--shadow-glow); }
.book-product-media { position: relative; aspect-ratio: 3 / 2; background: var(--color-surface-alt); }
.book-product-media img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: var(--radius-image); }
.book-product-hatch { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: var(--space-4); text-align: center; color: var(--color-text-subtle); font-size: var(--text-xs); background: repeating-linear-gradient(135deg, var(--color-surface) 0 var(--hatch), var(--color-surface-alt) var(--hatch) calc(2 * var(--hatch))); }
.book-product-badge { position: absolute; left: var(--space-3); top: var(--space-3); height: var(--tag-h); display: inline-flex; align-items: center; padding: 0 var(--inset-pill); border-radius: var(--radius-pill); background: var(--color-surface); color: var(--color-primary-hover); font-size: var(--text-xs); font-weight: var(--weight-medium); box-shadow: var(--shadow-sm); }
.book-product-check { position: absolute; right: var(--space-3); top: var(--space-3); width: var(--avatar-24); height: var(--avatar-24); border-radius: var(--radius-circle); background: var(--color-primary); color: var(--color-text-inverse); display: none; align-items: center; justify-content: center; }
.book-product.is-selected .book-product-check { display: inline-flex; }
.book-product-body { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-4); flex: 1; }
.book-product-title { font-size: var(--text-lg); line-height: var(--lh-lg); font-weight: var(--weight-light); letter-spacing: var(--tracking-heading); }
.book-product-meta { display: inline-flex; align-items: center; gap: var(--space-1); color: var(--color-text-subtle); font-size: var(--text-sm); }
.book-product-desc { color: var(--color-text-muted); font-size: var(--text-sm); line-height: var(--lh-sm); }
.book-product-foot { margin-top: auto; padding-top: var(--space-3); display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.book-product-price { display: flex; flex-direction: column; font-weight: var(--weight-medium); color: var(--color-text); }
.book-stepper { display: inline-flex; align-items: center; border: var(--hairline) solid var(--color-border-strong); border-radius: var(--radius-pill); padding: 0 var(--space-1); }
.book-stepper-qty { min-width: var(--space-6); text-align: center; font-weight: var(--weight-medium); color: var(--color-text); }

/* Calendar and slots */
.book-cal-head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.book-cal-month { font-size: var(--text-lg); line-height: var(--lh-lg); font-weight: var(--weight-light); letter-spacing: var(--tracking-heading); }
.book-cal-nav { display: inline-flex; gap: var(--space-1); margin-right: auto; }
.btn.is-disabled { color: var(--color-text-disabled); pointer-events: none; }
.book-days { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--space-2); }
.book-day { display: flex; flex-direction: column; align-items: center; gap: var(--space-1); padding: var(--space-3) 0; border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface); color: var(--color-text); box-shadow: var(--shadow-sm); transition: border-color var(--duration-fast) var(--easing-standard), background var(--duration-fast) var(--easing-standard); }
.book-day:hover { border-color: var(--rpp-orange-200); color: var(--color-text); }
.book-day.is-selected { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-text-inverse); box-shadow: var(--shadow-glow); }
.book-day.is-off { background: var(--color-surface-alt); color: var(--color-text-disabled); box-shadow: none; }
.book-day-name { font-size: var(--text-xs); line-height: var(--lh-xs); }
.book-day-num { font-size: var(--text-base); font-weight: var(--weight-medium); }
.book-day-dot { width: var(--dot); height: var(--dot); border-radius: var(--radius-circle); background: var(--color-primary); }
.book-day.is-selected .book-day-dot { background: var(--color-text-inverse); }
.book-day-title { font-size: var(--text-lg); line-height: var(--lh-lg); font-weight: var(--weight-light); letter-spacing: var(--tracking-heading); margin-top: var(--space-2); }
.book-period { display: flex; flex-direction: column; gap: var(--space-2); }
.book-slots { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--slot-min-w), 1fr)); gap: var(--space-2); }
.slot { position: relative; }
.slot input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.slot span { display: flex; align-items: center; justify-content: center; height: var(--control-h-md); border: var(--hairline) solid var(--color-border-strong); border-radius: var(--radius-field); background: var(--color-surface); color: var(--color-text); font-size: var(--text-sm); font-variant-numeric: tabular-nums; box-shadow: var(--shadow-sm); transition: border-color var(--duration-fast) var(--easing-standard), background var(--duration-fast) var(--easing-standard); }
.slot input:checked + span { border-color: var(--color-primary); background: var(--color-primary); color: var(--color-text-inverse); font-weight: var(--weight-medium); box-shadow: var(--shadow-glow); }
.slot input:focus-visible + span { box-shadow: var(--focus-ring); }
.slot.is-off span { background: var(--color-surface-alt); border-color: var(--color-border); color: var(--color-text-disabled); box-shadow: none; }
.book-after-time { display: flex; flex-direction: column; gap: var(--space-4); }

/* Review and confirmation */
.book-summary { margin: 0; display: flex; flex-direction: column; }
.book-summary-row { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-3) 0; border-bottom: var(--hairline) solid var(--color-border); }
.book-summary-row dt { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); }
.book-summary-row dd { margin: 0; font-weight: var(--weight-medium); color: var(--color-text); }
.book-lines { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.book-lines li { display: flex; justify-content: space-between; gap: var(--space-3); padding: var(--space-2) 0; color: var(--color-text); }
.book-total-row { display: flex; justify-content: space-between; align-items: baseline; padding-top: var(--space-4); border-top: var(--hairline) solid var(--color-border-strong); }
.book-grand { font-size: var(--text-lg); line-height: var(--lh-lg); font-weight: var(--weight-light); color: var(--color-text); }
.book-done-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; padding-top: var(--space-2); }
.book-done { padding-bottom: var(--space-8); }
.book-done .book-step { margin: 0 auto; }

/* Action bar */
.book-actions { position: fixed; left: 0; right: 0; bottom: 0; height: var(--book-actions-h); display: flex; align-items: center; justify-content: center; padding: 0 var(--space-4); background: var(--color-surface); border-top: var(--hairline) solid var(--color-border); z-index: 5; }
.book-actions-inner { display: flex; gap: var(--space-2); width: 100%; max-width: var(--book-w-narrow); }
.book-next { flex: 1; }
@media (max-width: 600px) {
  .book-bar { padding: 0 var(--space-4); }
  .book-counter { display: none; }
  .book-days { gap: var(--space-1); }
}

/* ---------- Radio cards, on the settings screens and the booking page ---------- */
/* A radio card is a label.choice, or a div.choice holding a label.choice-pick
   and the field that goes with that choice (a deposit amount). Settings ›
   Booking's stand side by side (.choices-2, and Automatic assignment's three
   in .choices-3); Deposit's, which hold a field, one under another. */
.choices { display: flex; flex-direction: column; gap: var(--space-2); }
.choice { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-3); padding: var(--space-3) var(--space-4); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface); box-shadow: var(--shadow-card); transition: border-color var(--duration-fast) var(--easing-standard), background var(--duration-fast) var(--easing-standard); }
label.choice, .choice-pick { cursor: pointer; }
.choice-pick { flex: 1; display: flex; align-items: center; gap: var(--space-3); }
.choice input:is([type="radio"], [type="checkbox"]) { width: var(--space-4); height: var(--space-4); margin: 0; accent-color: var(--color-primary); flex: none; }
.choice:has(input:is([type="radio"], [type="checkbox"]):checked) { border-color: var(--color-primary); background: var(--color-primary-wash); }
.choice:has(input:is([type="radio"], [type="checkbox"]):focus-visible) { box-shadow: var(--focus-ring); }
.choices-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 600px) { .choices-2 { grid-template-columns: minmax(0, 1fr); } }  /* a card shrinks to the phone, not the phone's page to the card */
/* Three radio cards in a row (one under another on a phone): Settings › Booking's Automatic assignment, as plain radio cards, or each
   standing as a card (Create product's What are you creating?): its 36px icon tile (grey-100, an ink glyph) over the name and a
   line saying what it is, the radio at its top right. The chosen card has the chosen choice's orange border and wash, its tile
   orange-100 with an orange glyph. */
.choices-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 600px) { .choices-3 { grid-template-columns: minmax(0, 1fr); } }
.choice-card { position: relative; flex-direction: column; align-items: flex-start; flex-wrap: nowrap; padding: var(--space-4); }
.choice.choice-card input[type="radio"] { position: absolute; top: var(--space-3); right: var(--space-3); }
.choice-card-icon { display: flex; align-items: center; justify-content: center; width: var(--avatar-36); height: var(--avatar-36); border-radius: var(--radius-tile); background: var(--color-surface-alt); color: var(--color-text); transition: background var(--duration-fast) var(--easing-standard), color var(--duration-fast) var(--easing-standard); }
.choice-card:has(:checked) .choice-card-icon { background: var(--color-primary-tint); color: var(--color-primary); }
.choice-card .choice-text { gap: var(--space-1); }
/* A record's switches that save one at a time (a customer's Editing preferences): setting rows two to a row (one on a phone, as
   .choices-2) 12px apart, each in its own form, the rows of a line the same height, the badge and switch 12px from the name, the
   description in grey-600 right under it; a row switched on is drawn selected, with the orange border and orange-100 fill. */
.preference-switches { gap: var(--space-3); }
.preference-switches > form { display: flex; flex-direction: column; gap: var(--space-2); }
.preference-switches .setting-row { flex: 1; gap: var(--space-3); transition: border-color var(--duration-fast) var(--easing-standard), background var(--duration-fast) var(--easing-standard); }
.preference-switches .setting-row:has(.toggle:checked) { border-color: var(--color-primary); background: var(--color-primary-tint); }
.preference-switches .setting-text { gap: 0; }
.preference-switches .setting-desc { color: var(--color-text-subtle); }
/* Settings › Editing preferences: a card per preference (DESIGN.md Option card), three to a row, two below 1360px, one below 960px,
   12px under the count, which the section head's own margin sets 20px under it. A card is the 36px blue-tinted tile with the ink
   sparkles 12px from the name over two lines kept for its description, then a foot of two equal 40px halves split by a hairline,
   each centring a small pill: Edit (ghost, ink) and Delete (danger, without its outline). Its own grid, not .grid-3, so the
   1360px step doesn't override .grid-3's (as .record-figures). */
.option-cards-count { margin-bottom: var(--space-3); }
.option-cards { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); }
.option-card { display: flex; flex-direction: column; min-width: 0; border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface); overflow: hidden; }
.option-card-body { flex: 1; display: flex; gap: var(--space-3); padding: var(--space-4); }
.option-card-icon { flex: none; display: flex; align-items: center; justify-content: center; width: var(--avatar-36); height: var(--avatar-36); border-radius: var(--radius-tile); background: var(--tint-blue-bg); color: var(--color-text); }
.option-card-text { display: flex; flex-direction: column; min-width: 0; }
.option-card-name { font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); color: var(--color-text); overflow-wrap: anywhere; }
.option-card-desc { min-height: calc(2 * var(--lh-xs)); font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); overflow-wrap: anywhere; }
.option-card-foot { display: grid; grid-template-columns: 1fr 1fr; border-top: var(--hairline) solid var(--color-border); }
.option-card-foot > div { display: flex; align-items: center; justify-content: center; min-height: var(--control-h-md); }
.option-card-foot > div + div { border-left: var(--hairline) solid var(--color-border); }
.option-card-foot .btn-ghost { color: var(--color-text); }
.option-card-foot .btn-danger { border-color: transparent; }
.option-cards-limits { margin-top: var(--space-4); }
@media (max-width: 1360px) { .option-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 960px) { .option-cards { grid-template-columns: minmax(0, 1fr); } }

/* ---------- Settings › Service area ----------
   The area cards, the six-colour picker and the Google map the corners are
   drawn on. Their sizes are named in the :root block at the top. An area's
   card: its swatch, its name in 16px Medium and its Active switch over the
   products kept to it and Edit, at the setting row's inset; an active area's
   card is drawn selected, with the orange border and orange-100 fill, as a
   switched-on preference row. With the map of every area the cards stand in
   a column beside it, the map taking the rest of the row (under the cards
   below 1200px); without it, or when Google doesn't load and
   area_overview_controller.js hides it, the cards fill the card. */
.area-overview { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-6); align-items: start; }
.area-overview:has(> .area-overview-map:not([hidden])) { grid-template-columns: var(--area-cards-w) minmax(0, 1fr); }
.area-list { display: flex; flex-direction: column; gap: var(--space-3); }
.area-cards { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(var(--area-cards-w), 100%), 1fr)); gap: var(--space-3); }
.area-card { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-3) var(--space-4); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface); transition: border-color var(--duration-fast) var(--easing-standard), background var(--duration-fast) var(--easing-standard); }
.area-card:has(.toggle:checked) { border-color: var(--color-primary); background: var(--color-primary-tint); }
.area-card-head { display: flex; align-items: center; gap: var(--space-2); }
.area-card-head form { margin-left: auto; display: flex; align-items: center; gap: var(--space-2); }
.area-card-name { min-width: 0; overflow-wrap: anywhere; font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); color: var(--color-text); }
.area-card-meta { margin-top: auto; display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }  /* at the foot of a card stretched to its row */
.area-card-products { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); }
@media (max-width: 1200px) { .area-overview:has(> .area-overview-map:not([hidden])) { grid-template-columns: minmax(0, 1fr); } }
.area-colours { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.area-colour { position: relative; display: inline-flex; }
.area-colour input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.area-colour span { width: var(--area-pick); height: var(--area-pick); border-radius: var(--radius-circle); transition: box-shadow var(--duration-fast) var(--easing-standard); }
.area-colour input:checked + span { box-shadow: 0 0 0 var(--area-ring) var(--color-surface), 0 0 0 calc(2 * var(--area-ring)) var(--color-text); }
.area-colour input:focus-visible + span { box-shadow: var(--focus-ring); }
.area-map { height: var(--area-map-h); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); overflow: hidden; background: var(--color-surface-alt); }
.area-overview-map { height: var(--area-overview-h); border: 0; background: var(--rpp-grey-200); }  /* after .area-map, which it resizes; as drawn, no outline */
.place-map { height: var(--place-map-h); }

/* ---------- Dashboard › Run mode ----------
   The tall map in its own panel fills the row beside the run card, the two
   as tall as each other; with no map, or when Google doesn't load and
   run_map_controller.js hides it, the card fills the row. Below 1200px, as
   the calendar's side panel does, the map sits over the card, shorter, both
   full width. A visit's number is a circle in its first person's colour
   (.swatch-N); the map's pins copy it. Grey (nobody on it) takes ink. */
.run-mode { display: flex; gap: var(--space-6); align-items: stretch; }
.run-map { flex: 1 1 0; min-width: 0; height: auto; min-height: var(--run-map-h); box-shadow: var(--shadow-card); }
.run-card { flex: 1 1 0; min-width: 0; }
.run-map:not([hidden]) ~ .run-card { flex: 0 0 var(--run-card-w); }
@media (max-width: 1199px) {
  .run-mode { flex-direction: column; }
  .run-map, .run-map:not([hidden]) ~ .run-card { flex: none; }
  .run-map { min-height: var(--run-map-h-narrow); }
  .run-mode[data-run-map-area-value] > .run-card { order: -1; } /* a map with no pin: the card, which says why, comes first */
}
.run-number { display: inline-flex; align-items: center; justify-content: center; flex: none; width: var(--avatar-24); height: var(--avatar-24); border-radius: var(--radius-circle); color: var(--color-text-inverse); font-size: var(--text-xs); font-weight: var(--weight-medium); line-height: 1; }
.run-number.swatch-0 { color: var(--color-text); }
/* Each person's run in the run card: the colour of their line, name and visits, then the drive and any quicker order on hint lines under the name. */
.run-legend li { flex-wrap: wrap; row-gap: 0; }
.run-legend .hint { flex-basis: 100%; padding-left: calc(var(--swatch) + var(--gap-control)); }

/* ---------- Products: variation rows and checkbox lists ----------
   A variation is one row of name, price and duration, ended by Remove
   (or "On jobs", level with the fields); the customer, roster and area
   choices scroll inside a bordered list. */
.option-row { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) auto; gap: var(--space-3); align-items: end; }
.option-row > :last-child { grid-column: -2; }  /* Remove or "On jobs" keeps the row's end while Digital hides the duration before it */
.option-row-booked { line-height: var(--control-h-md); white-space: nowrap; }
@media (max-width: 600px) { .option-row { grid-template-columns: 1fr; } }
.check-list { display: flex; flex-direction: column; gap: var(--space-2); max-height: var(--check-list-max-h); overflow: auto; padding: var(--space-3); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-field); background: var(--color-surface); }

/* ---------- Booking page: the product dialog, the photographer picker,
   the twilight visit and the payment due now ---------- */
.book-product-option { color: var(--color-primary-hover); font-size: var(--text-sm); font-weight: var(--weight-medium); }
.book-product-option:empty { display: none; }
.book-dialog, .app-dialog { width: min(var(--book-dialog-w), calc(100vw - 2 * var(--space-4))); max-height: calc(100vh - 2 * var(--space-8)); padding: 0; border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface); color: var(--color-text); box-shadow: var(--shadow-pop); overflow: hidden; }
.book-dialog[open], .app-dialog[open] { display: flex; flex-direction: column; }
.book-dialog::backdrop, .app-dialog::backdrop { background: var(--overlay-scrim); }
.book-dialog-media { position: relative; aspect-ratio: 3 / 1; background: var(--color-surface-alt); flex: none; }
.book-dialog-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.book-dialog-close { position: absolute; right: var(--space-3); top: var(--space-3); }
.book-dialog-body, .app-dialog-body { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-5); overflow: auto; }
.book-dialog-title, .app-dialog-title { font-size: var(--text-lg); line-height: var(--lh-lg); font-weight: var(--weight-light); letter-spacing: var(--tracking-heading); }
.book-dialog-foot, .app-dialog-foot { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-4); padding: var(--space-4) var(--space-5); border-top: var(--hairline) solid var(--color-border); flex: none; }
.app-dialog-foot.end { justify-content: flex-end; gap: var(--space-3); }
.app-dialog-foot.end > .hint { margin-right: auto; }  /* a line about the buttons (Create product's), at the foot's start */
@media (max-width: 600px) { .app-dialog-foot.end > .hint { flex-basis: 100%; } }  /* on a line of its own over them, so Cancel and the action keep one line */
.app-dialog-sub { font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--color-text-muted); }
/* A yes-or-no question: its glyph beside the title and the question. Set
   left and wrapping, whatever the cell it sits in (a list row's .row-action). */
.confirm-dialog { width: min(var(--confirm-dialog-w), calc(100vw - 2 * var(--space-4))); white-space: normal; text-align: left; }
.confirm-dialog .app-dialog-body { flex-direction: row; align-items: flex-start; gap: var(--space-4); }
.confirm-text { display: flex; flex-direction: column; gap: var(--space-2); overflow-wrap: anywhere; }  /* a long email or name in the question breaks rather than run past the dialog */
.confirm-form { margin: 0; }
.book-dialog-total { display: flex; flex-direction: column; margin-left: auto; text-align: right; font-weight: var(--weight-medium); }
.book-dialog .choice .num { font-weight: var(--weight-medium); }
.book-included { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--text-sm); color: var(--color-text-muted); }
.book-included li { display: flex; align-items: flex-start; gap: var(--space-2); }
.book-included .icon { color: var(--color-primary); }
.book-block { display: flex; flex-direction: column; gap: var(--space-2); }
.book-people { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.book-person { position: relative; }
.book-person input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.book-person > span { display: inline-flex; align-items: center; gap: var(--space-2); height: var(--control-h-md); padding: 0 var(--space-4) 0 var(--space-2); border: var(--hairline) solid var(--color-border-strong); border-radius: var(--radius-pill); background: var(--color-surface); font-size: var(--text-sm); font-weight: var(--weight-medium); }
.book-person input:checked + span { border-color: var(--color-primary); box-shadow: var(--shadow-glow); }
.book-person input:focus-visible + span { box-shadow: var(--focus-ring); }
.book-twilight .book-slots { max-width: calc(2 * var(--slot-min-w)); }
.book-due-row { display: flex; justify-content: space-between; color: var(--color-primary-hover); font-weight: var(--weight-medium); }
.book-due { display: contents; }  /* the Review step's payment frame: its rows keep the step's spacing */

/* ---------- Upload to Editor ---------- */
/* The four steps: the stepper under the page header, a centred step heading,
   a column per step, and a bar pinned to the foot of the window. The wizard
   fills the page down to the window's foot, and the bar, its last child,
   sticks there from the rail's edge to the window's: it keeps its own place
   at the end, so the last card always scrolls clear of it however its
   contents wrap. The desktop rail sits over it, so a rail tip isn't hidden. */
/* Focus and scrolling into view stop short of the bar, at its measured
   height (wizard_bar_controller.js) however its tallies and buttons wrap. */
html:has(.wizard-bar) { scroll-padding-bottom: calc(var(--wizard-bar-h, var(--wizard-bar-h-fallback)) + var(--space-4)); }
.page:has(> .wizard) { flex: 1; display: flex; flex-direction: column; padding-bottom: 0; }
.wizard { flex: 1; display: flex; flex-direction: column; gap: var(--space-6); }
.wizard-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); }
.wizard-step-sep { display: flex; color: var(--color-text-disabled); }
.wizard-step-link { display: inline-flex; align-items: center; gap: var(--gap-control); padding: 0; border: 0; background: none; font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text-subtle); cursor: default; }
button.wizard-step-link:focus-visible, a.wizard-step-link:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-pill); }
.wizard-step-dot { display: inline-flex; align-items: center; justify-content: center; width: var(--avatar-24); height: var(--avatar-24); border-radius: var(--radius-circle); background: var(--color-surface-alt); color: var(--color-text-muted); font-size: var(--text-xs); }
.wizard-step-dot .icon { display: none; }
.wizard-step.is-current .wizard-step-link, .wizard-step.is-done .wizard-step-link { color: var(--color-text); }
.wizard-step.is-current .wizard-step-dot { background: var(--color-primary); color: var(--color-text-inverse); }
.wizard-step.is-done .wizard-step-dot { background: var(--rpp-grey-900); color: var(--color-text-inverse); }
.wizard-step.is-done .wizard-step-num { display: none; }
.wizard-step.is-done .wizard-step-dot .icon { display: block; }
.wizard-step.is-done .wizard-step-link { cursor: pointer; }
.wizard-step:not(.is-done) button.wizard-step-link { pointer-events: none; }
.wizard-body { width: 100%; margin: 0 auto; }
.wizard-narrow { max-width: var(--book-w-mid); }
.wizard-wide { max-width: var(--book-w-wide); }
.wizard-head { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); text-align: center; margin-bottom: var(--space-2); }
.wizard-title { font-weight: var(--weight-light); font-size: var(--text-h3); line-height: var(--lh-h3); letter-spacing: var(--tracking-heading); }
.wizard-sub { color: var(--color-text-body); font-size: var(--text-base); line-height: var(--lh-base); }
.wizard-search { flex: none; width: 100%; }
/* The job search's matches, as the booking page's address suggestions, over
   the cards and the bar. */
.job-matches { z-index: 30; }
.job-matches li { flex-direction: row; align-items: center; justify-content: space-between; gap: var(--space-3); }
.job-matches li:not(.is-active):hover { background: none; }  /* hovering moves the highlight, so only one shows */
.job-matches li.is-empty { cursor: default; color: var(--color-text-subtle); background: none; }
.wizard-job { flex-wrap: nowrap; }
.wizard-job-pin { display: flex; align-items: center; justify-content: center; width: var(--avatar-32); height: var(--avatar-32); flex: none; border-radius: var(--radius-tile); background: var(--tint-orange-bg); color: var(--tint-orange-fg); }
.wizard-job-text { flex: 1; min-width: 0; }
.wizard-job-text .text-strong { overflow-wrap: anywhere; }  /* a street, or a name as the partner typed it, as on the jobs list's cards */
.wizard-job-text .cell-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Every group's tiles share one four-column grid, so tiles are one size;
   a group with one or two tiles takes half a row. */
.wizard-groups { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-6) var(--space-4); }
.wizard-group { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; row-gap: var(--space-3); align-content: start; }
.wizard-group.is-half { grid-column: span 2; }
.wizard-group-head { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: var(--gap-control); min-height: var(--control-h-sm); }
.wizard-tiles { display: contents; }
@media (max-width: 960px) { .wizard-groups { grid-template-columns: repeat(2, minmax(0, 1fr)); } .wizard-group.is-half { grid-column: 1 / -1; } }
/* A square photo tile: the service's photo, or its group's glyph on the
   photo scrim, with the name and partner price over the scrim. */
.wizard-tile { position: relative; aspect-ratio: 1; display: flex; align-items: flex-end; overflow: hidden; border: var(--hairline) solid var(--color-border); border-radius: var(--radius-image); background: var(--scrim-photo), var(--rpp-grey-600); box-shadow: var(--shadow-card); cursor: pointer; transition: border-color var(--duration-fast) var(--easing-standard), box-shadow var(--duration-fast) var(--easing-standard); }
.wizard-tile-photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: opacity var(--duration-fast) var(--easing-standard); }
.wizard-tile:hover .wizard-tile-photo { opacity: .88; }
.wizard-tile-caption { position: relative; width: 100%; display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-8) var(--space-4) var(--space-4); background: var(--scrim-photo); color: var(--color-text-inverse); font-size: var(--text-xs); line-height: var(--lh-xs); }
.wizard-tile-name { font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); }
.wizard-tile-badge { position: absolute; left: var(--space-3); top: var(--space-3); display: flex; align-items: center; justify-content: center; width: var(--avatar-28); height: var(--avatar-28); border-radius: var(--radius-tile); background: var(--color-surface); color: var(--color-text-muted); }
.wizard-tile-check { position: absolute; right: var(--space-3); top: var(--space-3); width: var(--space-5); height: var(--space-5); margin: 0; accent-color: var(--color-primary); cursor: pointer; }
.wizard-tile:has(.wizard-tile-check:checked) { border-color: var(--color-primary); box-shadow: var(--shadow-glow); }
.wizard-tile:has(.wizard-tile-check:checked) .wizard-tile-badge { background: var(--color-primary); color: var(--color-text-inverse); }
.wizard-tile:has(.wizard-tile-check:focus-visible) { box-shadow: var(--focus-ring); }
.wizard-tile:has(.wizard-tile-check:disabled) { cursor: default; opacity: .6; }
.wizard-note { display: flex; align-items: flex-start; gap: var(--space-2); }
/* Step 3: underline tabs, one per ticked line, under their order's caption. */
.wizard-tabs { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-6); border-bottom: var(--hairline) solid var(--color-border); }
.wizard-tab-group { display: flex; flex-direction: column; gap: var(--gap-control); }
.wizard-tab-row { display: flex; flex-wrap: wrap; gap: var(--space-5); }
.wizard-tab { display: inline-flex; align-items: center; gap: var(--space-2); margin-bottom: calc(-1 * var(--hairline)); padding: 0 0 var(--space-3); border: 0; border-bottom: var(--tab-underline) solid transparent; background: none; font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text-subtle); cursor: pointer; white-space: nowrap; }
.wizard-tab:hover { color: var(--color-text); }
.wizard-tab:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.wizard-tab[aria-selected="true"] { color: var(--color-text); border-bottom-color: var(--color-primary); }
.wizard-tab-add { margin-left: auto; color: var(--color-primary); }
.wizard-tab-add:hover { color: var(--color-primary-hover); }
/* The job page: a header row (Back to jobs, the status and the actions), the
   eyebrow and title, then the wizard's underline tabs as links, one per view
   of the record, which scroll inside their row on a phone. */
.text-link { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); white-space: nowrap; }
.record-header { margin-bottom: var(--space-6); }
.record-header .eyebrow { margin-bottom: var(--space-2); }
.record-header .page-title { overflow-wrap: anywhere; }
/* The eyebrow, the title and a named job's address under it, with the cover beside them (a smaller square on a phone). */
.record-title { display: grid; grid-template-columns: minmax(0, 1fr) var(--cover-w); align-items: start; gap: var(--space-6); }
.record-address { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-2); color: var(--color-text-muted); overflow-wrap: anywhere; }
.record-address .icon { color: var(--color-text-subtle); }
.record-cover { display: flex; align-items: center; justify-content: center; aspect-ratio: 3 / 2; border: var(--hairline) solid var(--color-border); border-radius: var(--radius-image); background: var(--color-surface); box-shadow: var(--shadow-card); color: var(--color-text-subtle); overflow: hidden; }  /* a photo tile with a card's plane, so the placeholder reads on the ground */
@media (max-width: 600px) {
  .record-title { grid-template-columns: minmax(0, 1fr) var(--space-16); gap: var(--space-4); }
  .record-cover { aspect-ratio: 1; }
}
.record-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3) var(--space-4); min-height: var(--control-h-md); margin-bottom: var(--space-6); }  /* a button's height even when only the pill shows, so the title never moves */
.record-actions { justify-content: flex-end; margin-left: auto; }  /* stays on the right when it wraps under the back link */
.record-tabs { display: flex; gap: var(--space-6); margin-bottom: var(--space-6); padding-top: var(--space-1); overflow-x: auto; box-shadow: inset 0 calc(-1 * var(--hairline)) 0 var(--color-border); }  /* the baseline is a shadow so the scroll box doesn't clip the active underline */
.record-tabs .wizard-tab { margin-bottom: 0; color: var(--color-text-muted); }
.record-tabs .wizard-tab:hover { color: var(--color-text); }
.record-tabs .wizard-tab[aria-current="page"] { color: var(--color-text); border-bottom-color: var(--color-primary); }
.record-tabs .count { font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-light); color: var(--color-text-subtle); }  /* at the label's size, as on the filter chips */
/* Tabs wider than the page (a phone, a tablet) scroll, and the strip opens on
   the current tab (tab_strip_controller.js). An edge with more tabs past it
   fades out, so the strip reads as one that scrolls. A mask uses only alpha:
   the opaque colour in it is never drawn. While a tab has keyboard focus
   there is no fade, so neither it nor its focus ring is ever faded. */
.record-tabs:is(.more-start, .more-end), .report-tabs:is(.more-start, .more-end) { mask-image: linear-gradient(to right, transparent, var(--color-text) var(--tabs-fade-start, 0px), var(--color-text) calc(100% - var(--tabs-fade-end, 0px)), transparent); }
.record-tabs.more-start, .report-tabs.more-start { --tabs-fade-start: var(--space-12); }
.record-tabs.more-end, .report-tabs.more-end { --tabs-fade-end: var(--space-12); }
.record-tabs:has(:focus-visible), .report-tabs:has(:focus-visible) { mask-image: none; }
/* The job page's Project status: the card's title with how many tasks are
   complete, Edit tasks on the right, a thin progress line, then a tile per
   task, its name over its status (beside it on a phone). A tile's menu opens
   under the tile and as wide as it, so it never runs off the page. */
.project-status { display: block; margin-bottom: var(--space-6); }
.project-status .stack > .callout { margin-bottom: 0; }  /* the stack's gap spaces it */
.project-status-bar { display: block; width: 100%; height: var(--space-1); border: 0; border-radius: var(--radius-pill); background: var(--rpp-grey-200); appearance: none; overflow: hidden; }
.project-status-bar::-webkit-progress-bar { background: var(--rpp-grey-200); }
.project-status-bar::-webkit-progress-value { background: var(--color-primary); border-radius: var(--radius-pill); }
.project-status-bar::-moz-progress-bar { background: var(--color-primary); border-radius: var(--radius-pill); }
button.text-link { padding: 0; border: 0; background: none; color: var(--color-primary); cursor: pointer; }
button.text-link:hover { color: var(--color-primary-hover); }
button.text-link:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-pill); }
.task-tiles { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(var(--task-tile-min), 100%), 1fr)); gap: var(--space-3); }
.task-tile { position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); padding: var(--space-3); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); }
.task-tile .label { color: var(--color-text-muted); }
.task-tile .menu { position: static; }
.task-tile .menu-panel { left: 0; right: 0; width: auto; }
.task-select > summary { list-style: none; cursor: pointer; padding-right: var(--space-2); transition: box-shadow var(--duration-fast) var(--easing-standard); }
.task-select > summary::-webkit-details-marker { display: none; }
.task-select > summary .icon { color: var(--color-text-subtle); }
.task-select > summary:hover, .task-select[open] > summary { box-shadow: inset 0 0 0 var(--hairline) var(--color-primary); }  /* as the grid's cells */
.task-select > summary:focus-visible { outline: none; box-shadow: var(--focus-ring); }
@media (max-width: 600px) {
  .task-tile { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: space-between; }
}
.record-card-head { margin-bottom: var(--space-5); }
.record-card-head .card-title { margin-bottom: var(--space-1); }
.activity-list { list-style: none; margin: 0; padding: 0; }
.activity-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: start; gap: var(--space-4); padding: var(--space-4) 0; border-top: var(--hairline) solid var(--color-border); }
.activity-row:first-child { border-top: 0; padding-top: 0; }
.activity-icon { display: flex; align-items: center; justify-content: center; width: var(--space-8); height: var(--space-8); border-radius: var(--radius-circle); background: var(--tint-orange-bg); color: var(--tint-orange-fg); }
.activity-text { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; font-size: var(--text-sm); line-height: var(--lh-sm); }
.activity-detail { color: var(--color-text-body); overflow-wrap: anywhere; }
.activity-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); overflow-wrap: anywhere; }
.activity-time { font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); white-space: nowrap; }
/* Internal comms: the comments as rows like Activity's, their text as typed (line breaks kept, never read as HTML) with the people
   mentioned marked as names are (Medium, not a link's orange: nothing to click), then the post box, its @ list dropping under it as the
   address suggestions do. The comment a link opens at (a mention in the bell, a post) sits clear of the top and on the hover tint. */
.comments { list-style: none; margin: 0 0 var(--space-5); padding: 0; }
.comment { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; gap: var(--space-3); padding: var(--space-4) 0; border-top: var(--hairline) solid var(--color-border); scroll-margin-top: var(--space-8); }
.comment:first-child { border-top: 0; padding-top: 0; }
.comment:target { background: var(--color-surface-alt); border-radius: var(--radius-card); border-top-color: transparent; margin-inline: calc(-1 * var(--space-3)); padding: var(--space-4) var(--space-3); }
.comment-text { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; font-size: var(--text-sm); line-height: var(--lh-sm); }
.comment-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2); }
.comment-head > .text-strong { min-width: 0; overflow-wrap: anywhere; }  /* a client's name is as they typed it */
.comment-time { font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-subtle); }
.comment-body, .review-body { white-space: pre-wrap; overflow-wrap: anywhere; }
.mention { font-weight: var(--weight-medium); color: var(--color-text); }
.comment-form { display: flex; flex-direction: column; gap: var(--space-3); }
.comment-form > .callout { margin-bottom: 0; }  /* the form's gap spaces it */
.comment-box { position: relative; display: flex; flex-direction: column; }
.mention-list li { flex-direction: row; align-items: center; gap: var(--space-2); }
.mention-list .places-secondary { margin-left: auto; }
.mention-list li:not(.is-active):hover { background: none; }  /* hovering moves the highlight, so only one shows */
.comment-actions { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.comment-actions .is-over { color: var(--color-primary-hover); }
/* The job page's Client feedback: one bordered row per file with comments (its thumbnail, then a link that opens it with the name, the
   comment count, the status and a chevron, then Archive conversation or Bring back, under the rest on a phone). Open, the comments as
   Internal comms draws them, Delete at each one's end for partner admins, then the Reply box with its count, and the status menu beside Reply.
   Archived ones fold away under Archived conversations, a tinted band like the design's. */
.feedback-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.feedback-thread { padding: var(--space-4); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface); scroll-margin-top: var(--space-8); }
.feedback-thread-head { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--space-2) var(--space-3); }
.feedback-thread-file { display: flex; border-radius: var(--radius-field); }
.feedback-thread-open { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); min-width: 0; border-radius: var(--radius-field); color: var(--color-text); font-size: var(--text-sm); line-height: var(--lh-sm); }
.feedback-thread-file:focus-visible, .feedback-thread-open:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.feedback-thread-open:hover { color: var(--color-text); }
.feedback-thread-open:hover .feedback-thread-name { color: var(--color-primary); }
.feedback-thread-name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.feedback-thread-meta { display: flex; align-items: center; gap: var(--space-2); margin-left: auto; }
.feedback-thread-meta > .icon { color: var(--color-text-subtle); }
.feedback-thread-action { margin: 0; }
.feedback-thread-body { display: flex; flex-direction: column; gap: var(--space-4); margin-top: var(--space-4); padding-top: var(--space-4); border-top: var(--hairline) solid var(--color-border); }
.feedback-thread .comments { margin: 0; }
.feedback-thread-body > .pin-stage { align-self: flex-start; }  /* an image thread's pins: the stage wraps the image, never the row's width */
.comments-deletable .comment { grid-template-columns: auto minmax(0, 1fr) auto; }
.feedback-reply { display: flex; flex-direction: column; gap: var(--space-3); }
.feedback-reply-actions { flex-wrap: wrap; justify-content: flex-end; }
.feedback-reply-actions > .hint { margin-right: auto; }  /* the count at the start; the menu and Reply at the end, Reply alone below on a phone */
.feedback-status { display: flex; align-items: center; gap: var(--space-2); margin: 0; }
.feedback-status .select { width: auto; }
.feedback-archived { margin-top: var(--space-3); }
.feedback-archived > summary { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-3) var(--space-4); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface-alt); cursor: pointer; list-style: none; font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text); }
.feedback-archived > summary::-webkit-details-marker { display: none; }
.feedback-archived > summary > .icon { color: var(--color-text-subtle); }
.feedback-archived > summary:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.feedback-archived-chevron { margin-left: auto; transition: transform var(--duration-normal) var(--easing-standard); }
.feedback-archived[open] .feedback-archived-chevron { transform: rotate(180deg); }
.feedback-archived[open] > summary { margin-bottom: var(--space-3); }
/* Edit customer's sections (customers/_edit_form.html.erb): native details, each opened on its own, its chevron pointing right and
   turning down when open, the open one's summary on the orange wash, as a selected choice. No overflow: hidden, so the summary's focus ring isn't clipped. */
.accordion { display: flex; flex-direction: column; gap: var(--space-2); }
.accordion-section { border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface); }
.accordion-section > summary { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-3) var(--space-4); border-radius: var(--radius-card); list-style: none; cursor: pointer; font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text); }
.accordion-section > summary::-webkit-details-marker { display: none; }
.accordion-section > summary:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.accordion-section[open] > summary { background: var(--color-primary-wash); border-radius: var(--radius-card) var(--radius-card) 0 0; }
.accordion-chevron { color: var(--color-text-subtle); flex: none; transition: transform var(--duration-normal) var(--easing-standard); }
.accordion-section[open] .accordion-chevron { transform: rotate(90deg); }  /* right when closed, down when open */
.accordion-body { display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-4); border-top: var(--hairline) solid var(--color-border); }
/* On a phone the row's parts line up at the name's edge: the thumbnail at the top, the count, status and chevron after the name, and
   Archive's icon under the name's first letter. */
@media (max-width: 600px) {
  .feedback-thread-head { grid-template-columns: auto minmax(0, 1fr); align-items: start; }
  .feedback-thread-meta { margin-left: 0; }
  .feedback-thread-action { grid-column: 2; justify-self: start; }
  .feedback-thread-action .btn { margin-left: calc(-1 * var(--space-3)); }
}
.appointment-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.appointment-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--space-2) var(--space-4); padding: var(--space-4); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); }
.appointment-text { display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--text-sm); line-height: var(--lh-sm); }
.appointment-day { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.appointment-products { color: var(--color-text); }
.date-tile { display: flex; flex-direction: column; align-items: center; width: var(--space-12); padding: var(--space-1) 0; border-radius: var(--radius-tile); background: var(--color-primary-wash); }
.date-tile-month { font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-primary-hover); }
.date-tile-day { font-size: var(--text-lg); line-height: var(--lh-lg); font-weight: var(--weight-light); color: var(--color-text); }  /* a value, Light as on the stat cards */
.record-answers { margin-top: var(--space-5); }
/* The Appointments tab's head with Schedule appointment on the right (under
   the title on a phone), each row's pill beside its Edit and Remove, which
   wrap together, and the Schedule appointment dialog's product rows. */
.record-card-bar { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--space-3) var(--space-4); }
.record-card-bar > .text-link { min-height: var(--lh-lg); }  /* View all or Edit centred on the card title's line */
.appointment-end { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-2); }
.appointment-actions, .appointment-qty { display: flex; align-items: center; gap: var(--space-2); }
/* The Deliverables tab: the Add links (under the title on a phone), and the
   folders as setting rows in the job's order: the handle (lighter than the
   menu's button), the folder's link that opens it, with a Hidden from
   delivery tag under its name for those who only read, and its Visible on
   delivery switch and menu (on a line of their own on a phone), with an
   open folder's files or links under its row, its child folders' rows
   over its files. A tour link's title sits over its host, here and on the
   delivery page. The dragged folder lifts; an Add dialog's upload rows put
   the bar and Remove on a line of their own on a phone. */
.folder-adds { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-5); }
.folder-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.folder-row { flex-wrap: wrap; gap: var(--space-2) var(--space-3); cursor: auto; }
.folder-handle { cursor: grab; touch-action: none; color: var(--color-text-subtle); }
.is-dragging > .folder-row { box-shadow: var(--shadow-pop); }  /* the dragged row only, not an open folder's children */
.is-dragging > .folder-row .folder-handle { cursor: grabbing; }
.folder-open { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--space-3); border-radius: var(--radius-field); }
.folder-open > .icon { color: var(--color-text-subtle); }
.folder-open .setting-title { overflow-wrap: anywhere; }
.folder-open .tag { align-self: flex-start; }
.folder-open:hover .setting-title { color: var(--color-primary); }
.folder-open:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.folder-controls { display: flex; align-items: center; gap: var(--space-3); }
.folder-visibility { display: flex; align-items: center; gap: var(--space-3); margin: 0; }
.folder-contents { padding: var(--space-4) 0 var(--space-2); }
.folder-children { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-5); padding-left: var(--space-10); }  /* over the folder's own files, set in under its row so they read as inside it */
.folder-link { display: flex; flex-direction: column; min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 600px) {
  .folder-controls { flex-basis: 100%; justify-content: space-between; }
  .folder-children { padding-left: var(--space-4); }
  .folder-add-dialog .upload-row { grid-template-columns: minmax(0, 1fr) auto auto; }
  .folder-add-dialog .upload-actions { grid-column: 2 / -1; justify-self: end; }
}
/* The Billing tab: the product select beside Custom line (above it on a
   phone), each line's quantity and unit price fields sized for their
   values, and a money field's $ inside its shell, before the amount. */
.bill-add { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin-bottom: var(--space-5); }
.bill-add-product { flex: 1 1 var(--bill-select-min); min-width: 0; }
.money-field { position: relative; display: flex; align-items: center; }
.money-field-sign { position: absolute; left: var(--inset-control); font-size: var(--text-sm); color: var(--color-text-muted); pointer-events: none; }
.money-field .input { padding-left: calc(var(--inset-control) + var(--space-3)); }
.bill-table th.qty-col, .bill-table th.num-col { text-align: right; }
.bill-table .money-field { display: inline-flex; }
.bill-table .money-field .input { width: var(--unit-input-w); text-align: right; }
.bill-table .row-action { width: var(--control-h-sm); }  /* the remove X beside the line's total, the spare width left to the name */
.customer-head { display: flex; align-items: flex-start; gap: var(--space-4); }
.customer-text { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.customer-text .card-title { margin-bottom: 0; overflow-wrap: anywhere; }
/* The customer page: its header card holds the avatar, the name over the contact line (company, email and phone with their icons,
   then tags) and the actions, which wrap under them once the line would be narrower than --profile-text-min; under a hairline
   the four figures in the stat cards' labels with h4 values, a step under the name, money and counts never broken: two to a row
   below 1360px (where a quarter can't hold a seven-figure total or "September 2026") and one to a row below 960px, as grids stack
   there. Its Jobs tab lists the jobs as the jobs list's rows without their cards at row size, the meta line right under the name,
   divided by hairlines and closed by one, the placeholder on grey-200 and the chevron grey-600. */
.customer-profile .customer-head { flex-wrap: wrap; gap: var(--space-4) var(--space-5); }  /* the name 20px from the avatar; wrapped actions 16px under the tags */
.customer-profile .customer-text { flex: 1 1 var(--profile-text-min); }
.customer-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--color-text-muted); }
.customer-meta-item { display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; overflow-wrap: anywhere; }
.customer-meta-item .icon { flex: none; color: var(--color-text-subtle); }
.record-figures { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-6); margin: var(--space-6) 0 0; padding-top: var(--space-6); border-top: var(--hairline) solid var(--color-border); }
.record-figures > div, .report-figures > div { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; color: var(--color-text); }  /* also Revenue overview's Profit and Loss figures */
.record-figures dd, .report-figures dd { margin-inline: 0; }
.record-figures .stat-value, .report-figures .stat-value { font-size: var(--text-h4); line-height: var(--lh-h4); }
.record-figures .stat-value.num, .report-figures .stat-value.num { white-space: nowrap; }
@media (max-width: 1360px) { .record-figures { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 960px) { .record-figures { grid-template-columns: minmax(0, 1fr); } }
.job-rows { list-style: none; margin: 0; padding: 0; }
.job-rows .job-card { padding: var(--space-3) 0; border-top: var(--hairline) solid var(--color-border); }
.job-rows .job-card:first-child { border-top: 0; padding-top: 0; }
.job-rows .job-card:last-child { border-bottom: var(--hairline) solid var(--color-border); }
.job-rows .job-card-thumb { background: var(--rpp-grey-200); }  /* the placeholder reads on the white card */
.job-rows .job-card-address { display: block; width: fit-content; font-size: var(--text-sm); line-height: var(--lh-sm); }  /* a block of its own lines, so the body's 24px line doesn't set them */
.job-rows .job-card-meta { margin-top: 0; }
.job-rows .job-card-side .btn-icon { margin-left: auto; color: var(--color-text-subtle); }  /* on a phone, where the side takes the row's width, the chevron stays at its end */
.job-rows .job-card-side .btn-icon:hover { color: var(--color-text); }
/* The Jobs tab's Internal note: a note panel rather than a card, the warning tint inside its state colour's hairline, its label
   with Edit (or Add note) on one line, the note under it at row size, then who wrote it and when; its label and hints grey-800 on
   the tint. */
.note-panel { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-4); border: var(--hairline) solid var(--status-yellow); border-radius: var(--radius-card); background: var(--status-yellow-bg); }
.note-panel-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
.note-panel .label, .note-panel .hint { color: var(--color-text-muted); }
.customer-note { overflow-wrap: anywhere; font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--color-text); }  /* a link pasted into the Internal note breaks rather than widening the page */
/* The job page's Customer tab: the customer's card with Contact under its
   subhead, beside Lifetime (its figures one under another while it sits in
   the side column, side by side once it drops under the card) and Recent
   jobs (each job's name over its status); a long email or name wraps. */
.customer-tab .subhead + .wizard-facts { margin-top: 0; }
.customer-tab .wizard-facts dd { overflow-wrap: anywhere; }
.customer-tab .side-panel .wizard-facts { margin-top: 0; }
@media (min-width: 961px) { .customer-tab .side-panel .wizard-facts { grid-template-columns: minmax(0, 1fr); } }
.recent-jobs li { flex-direction: column; align-items: flex-start; gap: var(--space-2); }
.recent-jobs a { overflow-wrap: anywhere; }
/* A customer's Team tab: rows 12px apart on grey-200 hairlines, each
   member's 36px avatar 12px from their name over their email, then at the
   row's end the role and the ⋯ menu (Edit, Remove), which wrap under them
   when the row runs short, still ending the row, a long role from the
   name's edge. On a phone the ⋯ stays at the row's end, level with the
   name's first line however far the name and email wrap, and the role
   goes under the email. A long email breaks and a role shortens
   to leave the menu room (in full on hover and in Edit) rather than
   widening the page. */
.team-list li { flex-wrap: wrap; padding: var(--space-3) 0; border-bottom-color: var(--color-border); }
.team-list li:first-child { padding-top: 0; }
.team-list li:last-child { padding-bottom: 0; }
.team-list .with-avatar { gap: var(--space-3); }
.team-list .with-avatar, .team-list .page-actions { min-width: 0; }
.team-list .page-actions { flex: auto; flex-wrap: nowrap; justify-content: flex-end; margin-inline-start: calc(var(--avatar-36) + var(--space-3)); }
.team-list .cell-stack { min-width: 0; overflow-wrap: anywhere; }
.team-list .tag { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; line-height: var(--tag-h); }
@media (max-width: 600px) {
  .team-list li { display: grid; grid-template-columns: var(--avatar-36) minmax(0, 1fr) auto; gap: var(--space-2) var(--space-3); }
  .team-list .with-avatar { grid-column: 1 / 3; }
  .team-list .page-actions { display: contents; }
  .team-list .menu { grid-area: 1 / 3; align-self: start; }
  .team-list .tag { grid-area: 2 / 2 / 3 / 4; justify-self: start; }
}
.app-dialog-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
.app-dialog-head > .btn-ghost.btn-icon, .app-dialog-head-end > .btn-ghost.btn-icon { color: var(--color-text-subtle); }  /* Close: a grey-600 glyph, ink on hover */
.app-dialog-head > .btn-ghost.btn-icon:hover, .app-dialog-head-end > .btn-ghost.btn-icon:hover { color: var(--color-text); }
/* A form dialog's head: the subtitle 4px under the title, and an eyebrow, where there is one (Create customer, Edit customer,
   Create job), 8px over it. */
.app-dialog-form .app-dialog-head .confirm-text { gap: var(--space-1); }
.app-dialog-head .confirm-text > .eyebrow { margin-bottom: var(--space-1); }
.app-dialog-form { display: contents; }  /* its body scrolls and its foot stays put, as in any dialog */
.app-dialog-form > .app-dialog-body { padding: var(--space-6); gap: var(--space-5); }  /* a form at the card's inset (the dialog's own on a phone), its fields apart */
.app-dialog-form > .app-dialog-foot { padding: var(--space-4) var(--space-6); }
.app-dialog .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: inherit; column-gap: var(--space-4); }  /* a pair of fields 16px apart, side by side until a phone, where it stacks at its column's own gap */
.app-dialog-form .link-row { margin-bottom: 0; }
.app-dialog .field_with_errors { display: contents; }  /* Rails wraps a refused field and its label; the field keeps its place */
.app-dialog .callout { margin-bottom: 0; }  /* the dialog's own gaps space it */
.app-dialog-body > .dl { margin-bottom: 0; }  /* the body's gap spaces it, not a list's own margin */
.app-dialog-body:focus-visible { outline: none; box-shadow: inset var(--focus-ring); }  /* a body long enough to scroll takes focus as the dialog opens */
.app-dialog .list .cell-stack { min-width: 0; overflow-wrap: anywhere; }  /* a long email in a list (Import's skipped rows) breaks rather than widening the dialog */
.app-dialog .stack > .hint { overflow-wrap: anywhere; }  /* a long heading in Import's ignored columns breaks rather than widening the dialog */
/* The Create job dialog: its four steps as bars over the head, the step
   count and close button beside the title, Create customer's button and
   note and the fields it opens, a client card's email and phone (the phone
   wrapping under a long email), the check beside a validated address, and
   the address suggestions, which open in the step's flow so the dialog's
   scrolling body never cuts them off. */
.dialog-progress { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-2); }
.dialog-progress li { height: var(--space-1); border-radius: var(--radius-pill); background: var(--rpp-grey-200); transition: background var(--duration-fast) var(--easing-standard); }
.dialog-progress li.is-done, .dialog-progress li.is-current { background: var(--color-primary); }
.app-dialog-head-end { display: flex; align-items: center; gap: var(--space-3); flex: none; }
/* Create customer and Create job: their steps' fields 20px apart (Create customer's Team step's head over its rows too). Create
   customer heads each step in 14px Medium sentence case, the photo 12px under its heading, and sets the Notes step on a grey band. */
.dialog-step, .dialog-step > .team-fields { gap: var(--space-5); }
.dialog-step > .form-section-title + .photo-field { margin-top: calc(-1 * var(--space-2)); }
.form-section-title { font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text); }
.notes-band { padding: var(--space-4); border-radius: var(--radius-card); background: var(--color-surface-alt); }
.create-customer-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); }
.create-customer-bar .hint { flex: 1; }
.create-customer { display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-4); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); }
.customer-contact { display: flex; flex-wrap: wrap; column-gap: var(--space-4); }
.customer-contact > span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.places-verified { display: flex; align-items: center; gap: var(--space-1); }
.places-verified .icon { color: var(--status-green-fg); }
/* The customer forms' fields (Create customer, Edit customer, Create job's Create customer). The photo: the avatar beside Upload
   photo over its hint, one label round the hidden file field, ringed while that has the keyboard's focus, and holding it (as a
   choice chip holds its radio), so the field scrolls with the dialog's body and focus brings the ring into view; its other lines
   (choose it again, Remove photo) start under the hint. The email: the envelope inside its field, placed as search's glass; its status
   takes no room while empty, and a free address reads in ink, its tick too. Billing: the suburb and state share the row beside a
   postcode as wide as a unit field, its hint under them as a field's is; on a phone one to a row, as far apart as the form's
   fields (20px in Create's steps, 16px in Edit's sections); the State prompt is grey like a placeholder, the states in ink.
   Create customer's accounting panel: grey-100, its title and line beside Connect Xero, Reconnect or Connected, which go under
   them on a phone. */
.photo-field { display: flex; flex-direction: column; gap: var(--space-2); }
.photo-field > :not(.photo-pick) { margin-inline-start: calc(var(--avatar-56) + var(--space-4)); }
.photo-pick { position: relative; display: flex; align-items: center; gap: var(--space-4); align-self: flex-start; max-width: 100%; padding-inline-end: var(--space-4); border-radius: var(--radius-pill); cursor: pointer; }
.photo-pick:has(:focus-visible) { box-shadow: var(--focus-ring); }
.photo-pick-text { display: flex; flex-direction: column; min-width: 0; overflow-wrap: anywhere; }
.photo-pick-title { font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text); }
.email-field { position: relative; }
.email-field > .icon { position: absolute; left: var(--inset-control); top: 50%; transform: translateY(-50%); color: var(--color-text-subtle); pointer-events: none; }  /* a press on it reaches the field */
.email-field .input { padding-left: var(--space-10); }
.email-status:not(:has(.hint)) { margin-top: calc(-1 * var(--space-2)); }  /* the field's gap above an empty status */
.email-available { color: var(--color-text); }
.email-available .icon { color: currentColor; }
.billing-place { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) var(--unit-input-w); gap: var(--space-4); }
.dialog-step .billing-place { row-gap: var(--space-5); }
.select-prompt:has(option[value=""]:checked) { color: var(--color-text-subtle); }
.select-prompt option { color: var(--color-text); }
.accounting-panel { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); padding: var(--inset-control) var(--space-4); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface-alt); }
.accounting-panel > .cell-stack { min-width: 0; font-size: var(--text-sm); overflow-wrap: anywhere; }
.accounting-panel > .btn, .accounting-panel > .pill { flex: none; }
@media (max-width: 600px) {
  .app-dialog-form > .app-dialog-body, .app-dialog-form > .app-dialog-foot { padding-inline: var(--space-5); }  /* the dialog's own inset, so a foot's two buttons keep one line */
  .app-dialog .grid-2, .billing-place { grid-template-columns: minmax(0, 1fr); }
  .accounting-panel { flex-direction: column; align-items: flex-start; }
}
.app-dialog .places-list { position: static; margin-top: var(--space-1); }
@media (max-width: 600px) {
  .record-actions { justify-content: flex-start; width: 100%; margin-left: 0; position: relative; }
  .record-actions .menu { position: static; }  /* a menu opens under the whole row, which fits a phone */
  .record-actions .menu-panel { left: 0; right: auto; max-width: 100%; }
  .customer-profile .customer-text { flex-basis: 0; }  /* the name stays beside the avatar; only the actions drop to their own row */
  .appointment-row { grid-template-columns: auto minmax(0, 1fr); align-items: start; }
  .activity-row { grid-template-columns: auto minmax(0, 1fr); }
  .activity-time { grid-column: 2; }
  .appointment-row .date-tile { grid-row: span 2; }
  .appointment-row > .appointment-end { grid-column: 2; justify-content: flex-start; }
  /* Create job: the note goes under Create customer, and the review's
     labels over their values, each pair apart. */
  .create-customer-bar .hint { flex-basis: 100%; }
  /* A head with a step count and close button (Create job, Create
     customer): those take a line of their own, the close button still in
     the corner, and the eyebrow, title and subtitle under them keep the
     dialog's whole width rather than a sliver beside them. */
  .app-dialog-head:has(> .app-dialog-head-end) { flex-wrap: wrap; row-gap: var(--space-2); }
  .app-dialog-head:has(> .app-dialog-head-end) > * { flex-basis: 100%; }
  .app-dialog-head-end { order: -1; justify-content: space-between; }
  .app-dialog .dl-wide { grid-template-columns: minmax(0, 1fr); row-gap: var(--space-1); }
  .app-dialog .dl-wide > div:not(:first-child) > dt { margin-top: var(--space-2); }
}
.wizard-line { margin: 0; padding: 0; border: 0; min-width: 0; }
.wizard-finals .input { width: var(--unit-input-w); }
/* A line's add-ons: checkbox chips that wrap, the name over its price. */
.wizard-addons { margin: 0; padding: 0; border: 0; min-width: 0; }
.wizard-addons-head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-1) var(--space-4); width: 100%; padding: 0; margin-bottom: var(--space-2); }
.choices-wrap { flex-direction: row; flex-wrap: wrap; }
.choice-text { display: flex; flex-direction: column; font-size: var(--text-sm); line-height: var(--lh-sm); }
.choice-name { font-weight: var(--weight-medium); color: var(--color-text); }
label.choice:has(input:disabled) { cursor: default; }
/* The photo order's brief: its customer preferences under the photo tabs,
   and what comes back (sizes, one custom size) beside the estimate. */
.order-brief { margin-bottom: var(--space-4); }
/* On an order's page its preferences, each over its description, fill their column, the first in line with its term. */
.order-brief .list { flex: 1; }
.order-brief .list li:first-child { padding-top: 0; }
.order-brief .list li:last-child { padding-bottom: 0; }
/* A floated legend leaves the fieldset's top border and joins its column. */
.wizard-brief { padding-top: var(--space-4); border-top: var(--hairline) solid var(--color-border); }
.wizard-brief > legend { float: left; }
/* Each choice's text fills its card, so a description wraps in its column and the box stays on the right. */
.wizard-outputs .choice-text, .wizard-brief .choice-text { flex: 1; }
.wizard-custom-size { display: flex; align-items: center; gap: var(--space-2); }
.wizard-custom-size .input { flex: 1; min-width: 0; }
/* A line's brackets: a tile per bracket, square-cornered like the photo it
   opens with, its notes over the picture on the photo scrim. The tile keeps
   no overflow so its ⋯ menu can float out of it. */
.wizard-brackets-summary { font-size: var(--text-sm); line-height: var(--lh-sm); }
.bracket-tiles { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(var(--file-tile-min), 100%), 1fr)); gap: var(--space-3); }
.bracket-tile { display: flex; flex-direction: column; min-width: 0; border: var(--hairline) solid var(--color-border); border-radius: var(--radius-image); background: var(--color-surface); box-shadow: var(--shadow-card); }
.bracket-thumb { position: relative; display: flex; align-items: center; justify-content: center; aspect-ratio: 4 / 3; overflow: hidden; background: var(--color-surface-alt); color: var(--color-text-subtle); }
.bracket-thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.bracket-thumb-caption { position: absolute; left: 0; right: 0; bottom: 0; padding: var(--space-6) var(--space-3) var(--space-2); background: var(--scrim-photo); color: var(--color-text-inverse); font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); }
.bracket-body { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-3); min-width: 0; }
.bracket-name { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text); }
.bracket-range { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bracket-actions { display: flex; gap: var(--space-2); margin-top: var(--space-2); }
.bracket-actions .menu-panel { left: 0; right: auto; gap: var(--space-2); }
/* Numbered pins over a bracket's preview, or a delivered image on the Client feedback tab, placed as fractions of it. */
.pin-stage { position: relative; display: inline-block; max-width: 100%; margin: 0; line-height: 0; }
.pin-stage img { display: block; max-width: 100%; height: auto; border-radius: var(--radius-image); }
.pin-stage.is-editing { cursor: crosshair; }
.pin { position: absolute; left: var(--pin-x); top: var(--pin-y); transform: translate(-50%, -50%); display: flex; align-items: center; justify-content: center; width: var(--avatar-24); height: var(--avatar-24); padding: 0; border: 0; border-radius: var(--radius-circle); background: var(--color-primary); color: var(--color-text-inverse); box-shadow: var(--shadow-sm); font-size: var(--text-xs); line-height: 1; font-weight: var(--weight-medium); }
button.pin { cursor: pointer; }
button.pin:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* A pinned comment's number, at the start of its writer's name so a long name wraps after it; the pin's colours at the bell count's
   18px. */
.pin-badge { display: inline-flex; align-items: center; justify-content: center; margin-right: var(--space-2); width: var(--badge-h); height: var(--badge-h); border-radius: var(--radius-circle); background: var(--color-primary); color: var(--color-text-inverse); font-size: var(--text-xs); line-height: 1; font-weight: var(--weight-medium); }
/* The Notes dialog: the preview beside the note, stacked on a narrow screen. */
.notes-dialog { width: min(var(--book-w-wide), calc(100vw - 2 * var(--space-4))); }
.notes-dialog-head-end, .notes-dialog-actions { display: flex; align-items: center; gap: var(--space-2); }
.notes-dialog-actions { margin-left: auto; }
.notes-dialog-grid { display: grid; grid-template-columns: minmax(0, 1fr) var(--side-panel-w); gap: var(--space-6); }
.notes-tools { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.notes-tools .hint { margin-right: auto; }
.notes-canvas { container-type: size; aspect-ratio: 3 / 2; display: flex; align-items: center; justify-content: center; background: var(--color-surface-alt); }
.notes-canvas img { max-width: 100cqw; max-height: 100cqh; }
.notes-canvas-status { padding: var(--space-4); text-align: center; }
.notes-caption { display: flex; align-items: flex-start; gap: var(--space-2); }
.notes-chips, .notes-files { display: flex; flex-wrap: wrap; gap: var(--space-2); }
@media (max-width: 960px) { .notes-dialog-grid { grid-template-columns: minmax(0, 1fr); } }
/* The order pages: each line's brackets, with the note beside the pinned preview. */
.bracket-group { display: flex; flex-direction: column; gap: var(--space-2); }
.bracket-group-head { display: flex; align-items: baseline; gap: var(--space-2); }
.bracket-notes { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--space-4); padding: var(--space-4); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); }
.bracket-notes .pin-stage { max-width: min(100%, var(--book-w-narrow)); }
.bracket-note { flex: 1; min-width: min(100%, var(--file-tile-min)); font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--color-text); }
.upload-summary { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface); box-shadow: var(--shadow-card); font-size: var(--text-sm); line-height: var(--lh-sm); }
.upload-summary .btn { margin-left: auto; }
.upload-summary-badge { display: flex; align-items: center; justify-content: center; width: var(--avatar-32); height: var(--avatar-32); border-radius: var(--radius-tile); background: var(--tint-green-bg); color: var(--tint-green-fg); }
.upload-summary.is-busy .upload-summary-badge { background: var(--color-surface-alt); color: var(--color-text); }
.upload-actions { display: flex; gap: var(--space-1); justify-content: flex-end; }
/* The estimate beside step 3 and the review on step 4. */
.wizard-sums { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--text-sm); line-height: var(--lh-sm); }
.wizard-sums li { display: flex; justify-content: space-between; gap: var(--space-3); }
.wizard-sums li > :last-child { color: var(--color-text); font-weight: var(--weight-medium); }
.wizard-sums-included > :first-child { color: var(--color-text-subtle); }
.wizard-sums-totals { padding-top: var(--space-2); border-top: var(--hairline) solid var(--color-border); }
.wizard-sums-totals .text-strong, .wizard-sums-grand li { color: var(--color-text); font-weight: var(--weight-medium); }
.wizard-sums-grand { padding-top: var(--space-3); border-top: var(--hairline) solid var(--color-border-strong); }
.wizard-card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.wizard-card-head .card-title { margin-bottom: 0; }
.wizard-order-title { margin: 0; }
.wizard-review-line { padding: var(--space-4); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-card); }
.wizard-facts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); margin: var(--space-4) 0 0; font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--color-text); }
.wizard-facts dt.label { color: var(--color-text-muted); }  /* lighter than the value under it, which leads */
.wizard-facts dd { margin: var(--space-1) 0 0; }
.wizard-facts-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }  /* facts that pair up (a customer's Billing); one column on a phone, below */
.wizard-facts-2 dd, .xero-contact { overflow-wrap: anywhere; }  /* a long email breaks rather than widening the page */
.xero-contact > .callout { margin-bottom: 0; }  /* the stack's gap spaces it */
.wizard-grand { font-size: var(--text-lg); line-height: var(--lh-lg); color: var(--color-text); }
.wizard-bar { position: sticky; bottom: 0; z-index: 5; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4) var(--space-10); margin: auto calc(-1 * var(--space-8)) 0; padding: var(--space-4) var(--space-8); border-top: var(--hairline) solid var(--color-border); background: var(--color-surface); box-shadow: var(--shadow-card); }
.wizard-tally { display: flex; flex-direction: column; gap: var(--space-1); }
.wizard-tally-value { font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); color: var(--color-text); }
.wizard-bar-actions { display: flex; gap: var(--space-2); margin-left: auto; }
@media (max-width: 600px) { .wizard-facts { grid-template-columns: minmax(0, 1fr); } .wizard-bar { margin-inline: calc(-1 * var(--space-4)); padding-inline: var(--space-4); } .wizard-bar-actions { margin-left: 0; width: 100%; } .wizard-bar-actions .btn { flex: 1; } }

/* ---------- Order status grid: one row per job, a hand-set status select per
   category. Rows are Turbo Frames, so the grid is CSS grid rather than a table;
   the head matches .table th. The nine columns need 1136px (their minimums and
   gaps) plus row padding, card border, page padding, rail and a scrollbar, so
   below 1360px each row wraps its cells under the property, four across (two
   on a tablet or phone), each with its own label. A menu opens from its cell's
   left edge, or its right edge in the right-hand columns, so it stays on
   screen. ---------- */
.status-grid-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--space-4); margin-bottom: var(--space-4); }
.status-grid-head .card-title { margin-bottom: var(--space-1); }
.status-grid { margin: 0 calc(-1 * var(--space-6)) calc(-1 * var(--space-6)); font-size: var(--text-sm); line-height: var(--lh-sm); }
.status-row { display: grid; grid-template-columns: minmax(var(--status-property-min), 2.4fr) var(--status-date-w) var(--status-notes-w) repeat(6, minmax(var(--status-cell-min), 1fr)); gap: var(--space-3); align-items: center; padding: var(--space-3) var(--space-6); border-bottom: var(--hairline) solid var(--color-surface-alt); }
.status-row:last-child { border-bottom: 0; }
.status-row-head { padding-top: var(--space-2); padding-bottom: var(--space-2); background: var(--color-surface-alt); border-bottom-color: var(--color-border); font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text-muted); }
.status-property { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.status-property-text { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); min-width: 0; }
.status-property-text > .menu { max-width: 100%; }
.status-property .menu-panel { width: max-content; }
.status-property .menu-item { white-space: nowrap; }
.status-property-name { display: flex; align-items: center; gap: var(--space-1); list-style: none; cursor: pointer; min-width: 0; }
.status-property-name::-webkit-details-marker, .status-select > summary::-webkit-details-marker, .status-notes-count::-webkit-details-marker { display: none; }
.status-property-name .text-strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.status-property-name:hover .text-strong { color: var(--color-primary); }
.status-property-name .icon, .status-select > summary .icon { color: var(--color-text-subtle); flex: none; }
.status-property .menu-panel, .status-select .menu-panel, .status-notes { left: 0; right: auto; }
.status-cell:nth-of-type(n+8) .menu-panel { left: auto; right: 0; }  /* Video and Deliver job, the last two columns */
.status-cell-label { display: none; font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-medium); color: var(--color-text-muted); }
.status-notes-count { display: inline-flex; align-items: center; gap: var(--space-1); color: var(--color-text-subtle); list-style: none; cursor: pointer; }
.status-notes { width: var(--side-panel-w); }
.status-select > summary { display: inline-flex; align-items: center; gap: var(--space-2); height: var(--pill-h); padding: 0 var(--space-2) 0 var(--inset-pill); border: var(--hairline) solid var(--color-border-strong); border-radius: var(--radius-pill); background: var(--color-surface); font-size: var(--text-xs); font-weight: var(--weight-medium); color: var(--rpp-grey-900); white-space: nowrap; list-style: none; cursor: pointer; transition: border-color var(--duration-fast) var(--easing-standard); }
.status-select > summary:hover, .status-select[open] > summary { border-color: var(--color-primary); }
.status-dot { width: var(--pill-dot); height: var(--pill-dot); border-radius: var(--radius-circle); background: var(--pill-dot-color, var(--rpp-grey-400)); flex: none; }
.cell-status-in-progress { --pill-dot-color: var(--status-blue); }
.cell-status-on-hold { --pill-dot-color: var(--status-yellow); }
.cell-status-complete { --pill-dot-color: var(--status-green); }
/* A status's dot outside its pill, in its tone's colour (Job reports' breakdowns). */
.status-dot-neutral, .status-dot-off { --pill-dot-color: var(--rpp-grey-400); }
.status-dot-info { --pill-dot-color: var(--status-blue); }
.status-dot-warning { --pill-dot-color: var(--status-yellow); }
.status-dot-success { --pill-dot-color: var(--status-green); }
.status-dot-danger { --pill-dot-color: var(--rpp-orange-500); }
.status-select > summary:focus-visible, .status-property-name:focus-visible, .status-notes-count:focus-visible, .menu-item:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.menu-list { gap: 0; padding: var(--space-2); }
.status-choices { display: contents; }
.menu-item { display: flex; align-items: center; gap: var(--inset-pill); width: 100%; padding: var(--space-2) var(--space-3); border: 0; border-radius: var(--radius-field); background: none; font: inherit; font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text); text-align: left; list-style: none; cursor: pointer; }
.menu-item::-webkit-details-marker { display: none; }
.menu-item:hover { background: var(--color-surface-alt); color: var(--color-text); }
.menu-item.is-current { background: var(--rpp-orange-100); }
.menu-item .pill { margin-left: auto; }
.menu-list > .hint { padding: var(--space-2) var(--space-3); }
.menu-divider { border: 0; border-top: var(--hairline) solid var(--color-border); margin: var(--space-2) 0; }
.menu-item .icon { color: var(--color-text-subtle); }
.menu-item-text { display: flex; flex-direction: column; min-width: 0; }  /* a name over a 12px line saying what it does */
.menu-item-sub { font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-light); color: var(--color-text-subtle); }
.menu-item-danger, .menu-item-danger .icon, .menu-item-danger:hover { color: var(--color-primary); }
.menu-list form { margin: 0; }
.menu-list .disclosure[open] > summary { margin-bottom: var(--space-2); }
.menu-list > .callout { margin-bottom: var(--space-2); }  /* a refused write's reason, over the choices */
/* The topbar's account menu and the bell's panel. */
.account-menu > .menu-panel { width: var(--side-panel-w); }
.account-person { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-3); }
.account-person-text { display: flex; flex-direction: column; min-width: 0; font-size: var(--text-sm); line-height: var(--lh-sm); }
.account-person-text > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The bell's panel: a fixed head, then the latest notifications scrolling under it; unread ones tinted with a dot. */
.notifications-panel { width: var(--notifications-w); max-height: calc(100vh - var(--topbar-h) - var(--space-6)); padding: 0; gap: 0; overflow: hidden; }
.notifications-panel > .hint { padding: var(--space-4); }
.notifications-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-3) var(--space-4); border-bottom: var(--hairline) solid var(--color-border); flex: none; }
.notifications-title { font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); }
.notifications-head form, .notifications form { margin: 0; }
.notifications { list-style: none; padding: 0; overflow-y: auto; min-height: 0; }
.notifications li + li { border-top: var(--hairline) solid var(--color-border); }
.notification { display: flex; align-items: flex-start; gap: var(--space-3); width: 100%; padding: var(--space-3) var(--space-4); border: 0; background: var(--color-surface); text-align: left; cursor: pointer; transition: background var(--duration-fast) var(--easing-standard); }
.notification.is-unread { background: var(--color-primary-tint); }
.notification:not(.is-unread):hover { background: var(--color-surface-alt); }  /* an unread row keeps its tint under the pointer */
.notification:hover .notification-title { color: var(--color-primary); }
.notification:focus-visible { outline: none; box-shadow: inset var(--focus-ring); }
/* A notification's icon, and a topbar search match's. */
.notification-icon, .search-row-icon { display: flex; align-items: center; justify-content: center; width: var(--avatar-32); height: var(--avatar-32); border: var(--hairline) solid var(--color-border); border-radius: var(--radius-circle); background: var(--color-surface); color: var(--color-text); flex: none; }
.notification-text { display: flex; flex-direction: column; gap: var(--space-1); flex: 1; min-width: 0; font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--color-text-body); }
.notification-title { font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); color: var(--color-text); }
.notification-time { color: var(--color-text-subtle); }
.notification-dot, .attention-dot { width: var(--pill-dot); height: var(--pill-dot); margin-top: calc((var(--lh-sm) - var(--pill-dot)) / 2); border-radius: var(--radius-circle); background: var(--color-primary); flex: none; }
@media (max-width: 1360px) {
  .status-row { grid-template-columns: repeat(4, minmax(0, 1fr)); row-gap: var(--space-2); align-items: start; }
  .status-row-head { display: none; }
  .status-property { grid-column: 1 / -1; }
  .status-property, .status-cell { position: relative; }
  .status-row .menu { position: static; }
  .status-row .menu-panel { max-width: calc(100vw - 2 * var(--space-4)); }
  .status-property .menu-panel { max-width: 100%; }
  .status-property .menu-item { white-space: normal; }
  .status-cell { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); }
  .status-cell-label { display: block; }
  .status-cell:nth-of-type(4n) .menu-panel, .status-cell:nth-of-type(4n+1) .menu-panel { left: auto; right: 0; }
}
@media (max-width: 960px) {
  .status-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .status-cell:nth-of-type(even) .menu-panel { left: 0; right: auto; }
  .status-cell:nth-of-type(odd) .menu-panel { left: auto; right: 0; }
}

/* ---------- Reports ----------
   Job reports: the Filters card (the jobs list's Filters row, of labelled selects, and the dates counted under it), the Overview
   label (.report-section-label, as the Products list's count), the figures as the dashboard's (five, then four), and the breakdown
   cards 12px apart, each with a 16px inset: the title in a column of its own, then each status as its tone's dot, its name and its
   count in Medium, 24px apart and wrapping; on a phone the statuses go under the title. */
.report-filters { margin-bottom: var(--space-6); }
.report-dates { margin-top: var(--space-3); }
.report-breakdowns { display: flex; flex-direction: column; gap: var(--space-3); }
.report-breakdown { display: flex; align-items: center; gap: var(--space-3) var(--space-6); padding: var(--space-4); }
.report-breakdown-title { flex: none; width: var(--breakdown-title-w); font-size: var(--text-sm); line-height: var(--lh-sm); font-weight: var(--weight-medium); }
.report-breakdown-list { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-6); padding: 0; list-style: none; font-size: var(--text-sm); line-height: var(--lh-sm); }
.report-breakdown-list li { display: inline-flex; align-items: center; gap: var(--space-2); }
.report-breakdown-count { font-weight: var(--weight-medium); color: var(--color-text); }
@media (max-width: 600px) { .report-breakdown { flex-direction: column; align-items: flex-start; } }
/* Revenue overview: the header's period menu is the dashboard Revenue card's. Then 24px apart: the Highlights card (its title a
   subhead with the orange sparkles, each line after a grey dot), the tabs (a segmented control across the page), the Xero card while
   Xero isn't ready for reports (the dollar glyph in ink on a 40px grey-100 tile, the title over its line, then the button) or the
   warning callout when Xero couldn't be reached, and the tab's cards. The Revenue overview card holds the Profit and Loss figures (the
   stat cards' label, value a step down at h4 as the customer page's figures, and change), over income month by month as bars on the
   dashboard Revenue chart's gridlines and dollar labels, the period's months orange and the months before it orange-200; without Xero,
   the dashed panel in the chart's place. Jobs by invoice status has its legend, and the count of the deliveries listed, over the
   table. Aged receivables is a hairline row per age, its amount and invoices at the right. On a phone the period menu opens from its
   left edge, where it starts its row, the tabs scroll sideways, the Xero card's button takes a row of its own and the chart names
   every other month. */
.report-highlights, .report-tabs, .report-xero, .report-callout, .report-income, .report-invoices { margin-bottom: var(--space-6); }
.report-highlights > .subhead { margin-top: 0; }
.report-highlights > .subhead .icon { color: var(--color-primary); }
.report-highlights-list { display: flex; flex-direction: column; gap: var(--space-2); padding: 0; list-style: none; font-size: var(--text-sm); line-height: var(--lh-sm); }
.report-highlights-list li { display: flex; align-items: flex-start; gap: var(--space-2); }
.report-highlights-list li::before { content: ""; flex: none; width: var(--dot); height: var(--dot); margin-top: calc((var(--lh-sm) - var(--dot)) / 2); border-radius: var(--radius-circle); background: var(--rpp-grey-400); }  /* on the first line's middle */
.report-tabs { display: flex; }
.report-tabs .segment { flex: none; }
.report-xero { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); }
.report-xero-icon { display: flex; align-items: center; justify-content: center; flex: none; width: var(--avatar-40); height: var(--avatar-40); border-radius: var(--radius-tile); background: var(--color-surface-alt); color: var(--color-text); }
.report-xero-text { flex: 1 1 0; min-width: 0; font-size: var(--text-sm); line-height: var(--lh-sm); }
.report-xero-title { font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium); }
.report-figures { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-6); margin: 0; }
.report-bar { fill: var(--color-primary); }
.report-bar-earlier { fill: var(--rpp-orange-200); }
.report-panel { justify-content: center; min-height: var(--report-panel-h); }
.report-invoices-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-4); margin-bottom: var(--space-4); }
.report-jobs th.num-col { text-align: right; }
.report-aged { margin: 0; font-size: var(--text-sm); line-height: var(--lh-sm); }
.report-aged > div { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-1) var(--space-3); padding: var(--space-3) 0; border-bottom: var(--hairline) solid var(--color-border); }
.report-aged > div:first-child { padding-top: 0; }
.report-aged > div:last-child { padding-bottom: 0; border-bottom: 0; }
.report-aged dd { display: flex; align-items: baseline; gap: var(--space-3); margin: 0; font-weight: var(--weight-medium); color: var(--color-text); }
.report-aged dd .hint { font-weight: var(--weight-light); }
@media (max-width: 960px) { .report-figures { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 600px) {
  .page-actions .revenue-menu .menu-panel { left: 0; right: auto; }
  .report-tabs { overflow-x: auto; }
  .report-xero .btn { flex-basis: 100%; }
  .report-month:nth-last-of-type(even) { display: none; }  /* twelve months' names don't fit side by side; the period's last stays */
}

/* ---------- Print ----------
   Export PDF (print_controller.js) prints the page as it stands: without the rail, topbar, flash messages, page actions or
   <noscript> buttons, on white without shadows, three figures to a row, no card split across pages, and each filter as its label
   and the text of its choice. */
@media print {
  .rail, .rail-scrim, .topbar, .page > .callout[data-turbo-temporary], .page-actions, noscript { display: none; }
  html { background: var(--color-surface); }
  .page { padding: 0; }
  .card, .stat, .select-field { box-shadow: none; }
  .card, .stat { break-inside: avoid; }
  .stats, .stats:has(.stat:nth-child(4)) { grid-auto-flow: row; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .select-field .select { padding-right: 0; background-image: none; }
}
