:root {
  --navy: #1f3864;
  --navy-2: #2a4a7f;
  --teal: #1e5a66;
  --paper: #fafaf7;
  --card: #ffffff;
  --line: #ddddd5;
  --text: #23272d;
  --muted: #6b7280;
  --accent: #c8922b;
  --red: #b0462c;
  --red-bg: #fbeeea;
  --green: #4f7a4a;
  --green-bg: #eef5ed;
  --blue: #3a6ea5;
  --blue-bg: #eaf1f8;
  --orange: #e07b1a;
  --grey: #9aa0a6;
  --grey-bg: #f2f2ef;
}

* { box-sizing: border-box; }

/* job attention icon (urgent / problematic) — shown inline before a job's title
   on the calendar, in job lists, and on the job page. Colour via currentColor. */
.attn { display: inline-flex; vertical-align: -2px; line-height: 0; margin-right: 4px; }
.attn svg { display: block; width: 12px; height: 12px; }
.attn-urgent { color: var(--orange); }
.attn-problematic { color: var(--red); }

/* keep in-page anchor jumps clear of the sticky top bar; always keep the vertical
   scrollbar present so the centered layout (header + body) never shifts left/right
   when moving between pages that scroll and ones that fit without scrolling.
   overflow-y:scroll (not scrollbar-gutter) so machines with overlay scrollbars
   stay full-bleed, and machines with classic scrollbars show a real scrollbar
   there rather than an empty gutter beside the top bar. */
html { scroll-padding-top: 64px; overflow-y: scroll; }

body {
  margin: 0;
  font: 15px/1.5 -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--text);
  background: var(--paper);
}

.container { max-width: 1080px; margin: 0 auto; padding: 0 16px; }

/* top bar */
.topbar { position: sticky; top: 0; z-index: 100; background: linear-gradient(135deg, var(--navy), var(--teal)); border-bottom: 3px solid var(--accent); }
.topbar-inner { display: flex; align-items: center; gap: 24px; min-height: 52px; }
.brand { display: inline-flex; align-items: center; gap: 9px; color: #fff; text-decoration: none; flex: none; }
.brand-mark { flex: none; width: 38px; height: 38px; object-fit: contain; display: block; }
.brand:hover .brand-mark { opacity: .9; }
.brand-text { display: flex; flex-direction: column; line-height: 1.1; }
.brand-name { font-weight: 700; font-size: 16px; letter-spacing: .2px; }
.brand-tag { font-size: 9.5px; font-weight: 700; letter-spacing: 1.4px; text-transform: uppercase; color: var(--accent); }
.lang-switch { margin-left: auto; display: flex; gap: 4px; }
.lang-switch form { margin: 0; }
.lang-btn { background: transparent; border: 1px solid rgba(255, 255, 255, .4); color: #fff; font: inherit; font-size: 12px; font-weight: 700; line-height: 1; padding: 5px 10px; border-radius: 999px; cursor: pointer; }
.lang-btn:hover { background: rgba(255, 255, 255, .16); }
.lang-btn.active { background: #fff; color: var(--navy); border-color: #fff; }
.topbar nav { display: flex; gap: 4px; }
.topbar nav a {
  color: #e8edf5; text-decoration: none; padding: 6px 12px; border-radius: 6px; font-size: 14px;
}
.topbar nav a.active, .topbar nav a:hover { background: rgba(255, 255, 255, .14); color: #fff; }

/* right-hand icon menus (admin tools + account) */
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: 4px; }
.topbar-menu { position: relative; }
.topbar-menu > summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 8px; color: #e8edf5;
}
.topbar-menu > summary::-webkit-details-marker { display: none; }
.topbar-menu > summary:hover,
.topbar-menu > summary.active,
.topbar-menu[open] > summary { background: rgba(255, 255, 255, .16); color: #fff; }
.menu-panel {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 200; min-width: 210px;
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .18); padding: 6px; display: flex; flex-direction: column; gap: 2px;
}
.menu-panel a, .menu-btn {
  display: block; width: 100%; text-align: left; box-sizing: border-box;
  padding: 8px 10px; border-radius: 6px; font-size: 14px; color: var(--text);
  text-decoration: none; background: none; border: none; cursor: pointer; font-family: inherit;
}
.menu-panel a:hover, .menu-btn:hover { background: var(--paper); }
.menu-panel a.active { color: var(--navy); font-weight: 600; }
.menu-meta { margin: 4px 8px; font-size: 12px; color: var(--muted); line-height: 1.5; }
.menu-meta strong { color: var(--text); font-size: 13px; word-break: break-all; }
.menu-sep { border-top: 1px solid var(--line); margin: 4px -2px; }

/* mobile: collapse the whole top bar to logo + hamburger. The hamburger reuses
   the .topbar-menu dropdown (and its menu_controller) and holds nav_links +
   admin_links + account_menu — same partials as the desktop bar. */
.nav-burger { display: none; }
@media (max-width: 820px) {
  .topbar-inner .topbar-nav,
  .topbar-inner .topbar-actions { display: none; }
  .topbar-inner .nav-burger { display: block; margin-left: auto; }
}

main { padding: 24px 16px 64px; }

/* sticky secondary header (e.g. the Jobs filter bar): title · filters · action.
   Bleeds to the container edges and sticks just under the top bar. */
.page-toolbar {
  position: sticky; top: 55px; z-index: 90;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 0 -16px 18px; padding: 12px 16px;
  background: var(--paper); border-bottom: 1px solid var(--line);
}
.page-toolbar-title { margin: 0; font-size: 22px; color: var(--navy); white-space: nowrap; }
.toolbar-filters { display: flex; gap: 8px; flex: 1 1 260px; margin: 0; }
.toolbar-search, .toolbar-select {
  padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px;
  font-size: 14px; background: var(--card); color: var(--text);
}
.toolbar-search { flex: 1; min-width: 150px; }
.toolbar-add { white-space: nowrap; }

h1 { font-size: 24px; margin: 8px 0 18px; color: var(--navy); }

/* The page-type kicker — the label naming what KIND of screen this is (JOB,
   LOG JOB, CUSTOMER) on pages whose real headline is the record's own text.
   Labels and content must be different SPECIES, not different sizes of one:
   when "Job" and the job description were both big navy, nothing said which
   was the label. So the kicker speaks label (small caps, letter-spaced,
   muted, the app's gold tick) and the headline below keeps speaking content
   (big navy sentence case). Deliberately quieter than the navy Tasks band —
   that band stays the page's single loudest mark. */
h1.page-kicker {
  font-size: 11.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 10px; line-height: 1.2;
  border-left: 3px solid var(--accent); padding-left: 8px;
}
h2 { font-size: 17px; margin: 28px 0 10px; color: var(--teal); }
h3 { font-size: 14.5px; margin: 16px 0 6px; color: var(--teal); }
h3 a { color: inherit; text-decoration: none; }
h3 a:hover { text-decoration: underline; }
.subtle { color: var(--muted); font-weight: 400; font-size: .9em; }
.breadcrumb { margin: 0 0 4px; }
.breadcrumb a { color: var(--muted); text-decoration: none; }
a { color: var(--teal); }

/* stat cards */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.stat {
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  padding: 12px 14px; display: flex; flex-direction: column; gap: 2px;
}
.stat-label { font-size: 12.5px; color: var(--muted); }
.stat-value { font-size: 21px; font-weight: 700; color: var(--navy); }
.stat-value .stat-unit { font-size: 13px; font-weight: 400; color: var(--muted); }
/* today counts read amber everywhere (matches .badge-warn) */
.stat-value .stat-today { color: #8a5f10; }
/* tiles that focus their dashboard section on click */
.stat-btn { font: inherit; text-align: left; cursor: pointer; }
.stat-btn:hover { border-color: var(--teal); }
.stat-note .badge { margin-right: 4px; }
.stat-note { font-size: 12px; color: var(--muted); }
.stat-danger { border-color: var(--red); background: var(--red-bg); }
.stat-danger .stat-value { color: var(--red); }
.stat-good { border-color: var(--green); background: var(--green-bg); }
.stat-good .stat-value { color: var(--green); }

/* collapsible dashboard sections: heading is the toggle, chevron shows state */
.dash-section > summary { list-style: none; cursor: pointer; }
.dash-section > summary::-webkit-details-marker { display: none; }
.dash-section > summary h2::before, .dash-section > summary h3::before {
  content: "▾"; display: inline-block; margin-right: 7px; font-size: .75em;
  color: var(--muted); transition: transform .15s; vertical-align: 1px;
}
.dash-section:not([open]) > summary h2::before,
.dash-section:not([open]) > summary h3::before { transform: rotate(-90deg); }
/* Section names are labels, so they speak the label species (see .record-label)
   — the counts, hints and trailing links inside keep their own voices. */
.dash-section > summary h2, .dash-section > summary h3 {
  font-size: 11.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); margin: 10px 0 8px;
}
.dash-section > summary h2 .float-right { text-transform: none; letter-spacing: normal; font-weight: 400; font-size: 12.5px; }

/* focus list */
.focus-list { background: #fdf7ec; border: 1px solid var(--accent); border-radius: 10px; padding: 12px 16px 12px 36px; margin: 0; }
.focus-list li { margin: 6px 0; }

/* tables */
table { border-collapse: collapse; width: 100%; background: var(--card); font-size: 14px; margin: 8px 0 24px; }
th { background: var(--navy); color: #fff; text-align: left; padding: 8px 10px; font-weight: 600; white-space: nowrap; }
td { border-bottom: 1px solid var(--line); padding: 7px 10px; vertical-align: top; white-space: nowrap; }
td.wrap { white-space: normal; min-width: 240px; }
tfoot th { background: #eef1ea; color: var(--text); border-top: 2px solid var(--navy); }
.num { text-align: right; font-variant-numeric: tabular-nums; }
tr:hover td { background: #f6f6f1; }
tr[data-href] { cursor: pointer; } /* whole-row link (see row_link_controller) */
.row-danger td { background: var(--red-bg); }
.row-danger:hover td { background: #f7e3dd; }
.row-dim td { color: var(--muted); }
.text-danger { color: var(--red); font-weight: 600; }
/* today's rows: gold left marker + tint, kept visible through hover */
.today-row td { background: #fdf7ec; }
.today-row:hover td { background: #faf0dc; }
.today-row td:first-child { box-shadow: inset 4px 0 0 var(--accent); }
.today-divider td { color: var(--muted); font-style: italic; font-size: 13px; }

/* customer list density */
.list td { vertical-align: middle; }
/* sortable column headers: click to sort, active column carries ▲/▼ */
th a.th-sort { color: #fff; text-decoration: none; }
th a.th-sort:hover { text-decoration: underline; }
th a.th-sort-active { color: var(--accent); }
.cell-strong { font-weight: 600; }
.cell-sub { font-size: 12px; color: var(--muted); margin-top: 1px; }
.cell-chips { white-space: normal; max-width: 300px; }
.chip {
  display: inline-block; font-size: 12px; color: var(--teal); background: var(--paper);
  border: 1px solid var(--line); border-radius: 6px; padding: 1px 7px; margin: 1px 3px 1px 0;
}
h1 .count { font-size: 15px; font-weight: 400; }

/* badges */
.badge {
  display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: .3px;
  padding: 1px 8px; border-radius: 999px; text-transform: uppercase;
}
.badge-open { background: #e3ecf7; color: var(--navy); }
.badge-paid { background: var(--green-bg); color: var(--green); }
.badge-overdue { background: var(--red-bg); color: var(--red); }
.badge-uninvoiced, .badge-dim, .badge-wont_bill { background: #eceee8; color: var(--muted); }
.badge-warn, .badge-sent { background: #faf3e0; color: #8a5f10; }

/* jobs list — one continuous table with quiet month separators in a left gutter
   (replacing a repeated navy header per month), a single sticky <thead>, a
   billing-glyph column, and a +job action that only appears on row hover. */
.jobs-list { position: relative; padding-left: 84px; }
.jobs-table { margin: 0 0 24px; }
.jobs-table thead th { position: sticky; top: 112px; z-index: 20; }
.jobs-table tbody tr.month-sep td {
  position: relative; border-top: 1px solid var(--line); border-bottom: none;
  padding: 20px 10px 6px; background: var(--paper);
}
.jobs-table tbody tr.month-sep:first-child td { border-top: none; padding-top: 4px; }
.jobs-table tbody tr.month-sep:hover td { background: var(--paper); }
.month-label {
  position: absolute; left: -84px; bottom: 5px; width: 74px;
  text-align: right; font-size: 11.5px; font-weight: 700; letter-spacing: .4px;
  text-transform: uppercase; color: var(--muted); white-space: nowrap;
}
/* billing glyph (Bill column) — colour per state via currentColor */
.bill { display: inline-flex; vertical-align: -3px; line-height: 0; }
.bill svg { display: block; width: 16px; height: 16px; }
.bill-wont_bill { color: var(--muted); }
.bill-uninvoiced { color: var(--grey); }
.bill-sent { color: var(--orange); }
.bill-open { color: var(--navy); }
.bill-paid { color: var(--green); }
.bill-overdue { color: var(--red); }
/* "task N of M" badge for a multi-task job (2/3 = task two of three): a small
   rounded box shared by the calendar blocks/popover (positioning variants in
   calendar.css) and the jobs list (.row-daybadge, inline after the task's date) */
.daybadge {
  font-size: 8.5px; font-weight: 700; line-height: 1.4; letter-spacing: .2px;
  min-width: 11px; text-align: center; padding: 0 4px; border-radius: 999px;
  background: rgba(31, 56, 100, .16); color: var(--navy); text-decoration: none;
}
.row-daybadge { display: inline-block; margin-left: 5px; vertical-align: 1px; }
/* attachment count — quiet, never competes with the description link */
.clip { color: var(--muted); font-size: 12px; white-space: nowrap; margin-left: 2px; }
/* task rows (jobs list + dashboard upcoming work): the muted second line under
   the task headline — job description (when a task name led) · customer.
   Kept quiet so the task name reads first. */
.row-sub { font-size: 12px; color: var(--muted); margin-top: 1px; }
.row-sub a { color: var(--muted); }
.row-sub a:hover { color: var(--accent); }
/* job page: the bill-lifecycle strip — stage icon + label + the one next action */
.bill-track {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  margin: 0 0 16px; padding: 8px 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: 8px;
}
.bill-track .bill svg { width: 17px; height: 17px; }
/* Same metrics as .record-label — the bill row is a section like the others. */
.bill-track-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 600; }
.bill-track-status { font-weight: 600; }
.bill-track form { margin: 0; }
/* +job template action: revealed on row hover/focus, always shown where there
   is no hover (touch) so it stays reachable */
.row-action { opacity: 0; transition: opacity .12s; }
tr:hover .row-action, tr:focus-within .row-action { opacity: 1; }
@media (hover: none) { .row-action { opacity: 1; } }
@media (max-width: 640px) {
  .jobs-list { padding-left: 0; }
  .jobs-table thead th { position: static; }
  .jobs-table tbody tr.month-sep td { padding-left: 10px; }
  .month-label { position: static; width: auto; text-align: left; display: block; }
}

/* search + tabs */
.searchbar { display: flex; gap: 8px; align-items: center; margin: 0 0 16px; }
.searchbar input[type="text"] {
  padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px; font-size: 14px; width: min(320px, 70vw);
}
.searchbar button {
  padding: 7px 14px; border: none; border-radius: 8px; background: var(--navy); color: #fff; font-size: 14px; cursor: pointer;
}
.tabs { display: flex; gap: 4px; margin-bottom: 12px; }
.tabs a { text-decoration: none; padding: 6px 14px; border-radius: 999px; font-size: 13.5px; color: var(--teal); border: 1px solid var(--line); background: var(--card); }
.tabs a.active { background: var(--navy); color: #fff; border-color: var(--navy); }

/* cards / detail */
.two-col { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 14px; }
.card { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 4px 16px 14px; }
dl { display: grid; grid-template-columns: max-content 1fr; gap: 4px 16px; margin: 8px 0 0; }
/* A dt is a label by definition — same species as .fact-label. */
dt { color: var(--muted); font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; }
dd { margin: 0; }

@media (max-width: 640px) {
  td, th { white-space: normal; }
}

/* flashes */
.flash { border-radius: 8px; padding: 10px 14px; margin: 0 0 16px; }
.flash-notice { background: var(--green-bg); border: 1px solid var(--green); color: var(--green); }
.flash-alert { background: var(--red-bg); border: 1px solid var(--red); color: var(--red); }
.flash-token { background: #fdf7ec; border: 1px solid var(--accent); word-break: break-all; }

/* buttons */
.btn {
  display: inline-block; padding: 7px 14px; border-radius: 8px; font-size: 14px; cursor: pointer;
  background: var(--card); color: var(--teal); border: 1px solid var(--line); text-decoration: none;
}
.btn-primary { background: var(--navy); color: #fff; border-color: var(--navy); }
.btn-wide { width: 100%; padding: 10px; font-size: 15px; }
.btn-sm { padding: 3px 10px; font-size: 12.5px; border-radius: 6px; line-height: 1.5; white-space: nowrap; }
.btn-link { background: none; border: none; color: var(--teal); cursor: pointer; font-size: 13px; padding: 2px 4px; text-decoration: underline; }
/* icon-only buttons (the trash-can removes) */
.btn-icon { display: inline-flex; align-items: center; justify-content: center; line-height: 0; }
.btn-icon svg { width: 15px; height: 15px; }
.btn-link.btn-icon svg { width: 14px; height: 14px; }
.task-table th svg { width: 13px; height: 13px; vertical-align: -2px; }
.btn-link.danger { color: var(--red); }
.btn.danger { color: var(--red); border-color: var(--red); }
.btn[disabled] { color: var(--muted); border-color: var(--line); background: var(--paper); cursor: not-allowed; opacity: .65; }

/* Customer page — compact facts strip under the name: small address, contact
   icons, rate, boat count, owed amount. The whole strip (chevron included)
   expands to the full contact card + boat management. */
.head-boat { font-size: 14px; font-weight: 400; color: var(--muted); margin-left: 4px; }
.facts { margin: -8px 0 18px; }
.facts > summary {
  list-style: none; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px;
  cursor: pointer; color: var(--muted); font-size: 13.5px; width: max-content; max-width: 100%;
}
.facts > summary::-webkit-details-marker { display: none; }
.fact { display: inline-flex; align-items: center; gap: 5px; }
a.fact { color: var(--muted); text-decoration: none; }
a.fact:hover { color: var(--teal); }
.fact-icon svg { width: 15px; height: 15px; display: block; color: var(--teal); }
.fact-expand { display: inline-flex; color: var(--teal); font-size: 11px; transition: transform .15s; }
.facts[open] > summary .fact-expand { transform: rotate(180deg); }
.facts-body { margin-top: 12px; }

/* the customer's "what matters to them" note — always visible, quietly marked */
.customer-notes { margin: 0 0 18px; padding: 2px 0 2px 10px; border-left: 3px solid var(--accent); }

.offers-mini { margin: 8px 0 6px; font-size: 13.5px; }
.offers-mini th, .offers-mini td { padding: 6px 8px; }
.offers-mini td.wrap { min-width: 0; }

.card { overflow-x: auto; } /* a too-wide table scrolls inside its card, never bleeds out */

/* Job history master–detail: a job spanning several work sessions lists each
   visit as a quiet indented sub-row; the Details toggle (toggle_controller)
   reveals the secondary .detail layer (notes, visit times, media counts). */
.job-history .session-row td {
  background: #fbfbf7; color: var(--muted); font-size: 13px;
  padding-top: 3px; padding-bottom: 3px; border-bottom: 1px solid #eceee8;
}
.job-history .session-row:hover td { background: #f4f4ee; }
.job-history .session-date { padding-left: 22px; }
.session-state { font-size: 12px; }
.session-canceled { text-decoration: line-through; }
.job-history .detail { display: none; }
.show-details .job-history .detail { display: revert; }
.btn[aria-pressed="true"] { background: var(--navy); color: #fff; border-color: var(--navy); }

/* Boats — read-only rows on the customer page, each with edit/delete actions
   and a Turbo-frame inline editor. */
.float-right { float: right; font-size: 14px; font-weight: 400; }
.text-good { color: var(--green); }
.topbar-inner nav { flex-wrap: wrap; }

/* forms */
/* Form labels speak the same label species as the record pages' fact labels —
   the compose form's BOAT/RATE must look like the job page's BOAT/RATE, they
   are the same facts. The controls inside are content and reset the voice
   (text-transform inherits into inputs and would uppercase what people type). */
.form label, .form-row label {
  display: flex; flex-direction: column; gap: 3px; margin: 8px 0;
  font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); /* --muted, not --grey: 10.5px needs 4.5:1 contrast (AA) */
}
.form label input, .form label select, .form label textarea,
.form-row label input, .form-row label select, .form-row label textarea {
  text-transform: none; letter-spacing: normal; font-weight: 400;
}
/* Sentence-shaped labels are instructions, not fact names — a checkbox's
   clickable sentence, the customer-number strip, a label that IS a phrase.
   They keep the content voice. */
.form label.checkbox, .form-row label.checkbox,
.form label.strip-number, .form label.sentence {
  text-transform: none; letter-spacing: normal;
  font-size: 13px; font-weight: 400; color: var(--muted);
}
/* The customer portal shares the :app stylesheet bundle but not the admin's
   label system — its labels and dt/dd pairs keep their pre-sweep face
   (sentence case, full size). Scoped by the public layout's body class. */
body.public .form label, body.public dt {
  text-transform: none; letter-spacing: normal;
  font-size: 13px; font-weight: 400; color: var(--muted);
}
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 4px 16px; }
.form input[type="text"], .form input[type="number"], .form input[type="date"], .form input[type="email"],
.form input[type="tel"], .form select, .form textarea,
.form-row input, .form-row select {
  padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px; font-size: 14.5px; background: var(--card); color: var(--text);
}
/* Invalid field — the single, system-wide form error style. Add the class
   "field-invalid" to any input/select/textarea to flag a bad value: red text,
   red box. One definition, reused everywhere. */
.field-invalid,
.form input.field-invalid,
.form select.field-invalid,
.form textarea.field-invalid {
  border-color: var(--red);
  color: var(--red);
  background: var(--red-bg);
}
.field-invalid:focus {
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--red) 22%, transparent);
}
.field-invalid::placeholder { color: var(--red); opacity: .55; }
.form textarea { width: 100%; }
/* Multiline fields grow with their content — text never hides behind a
   scrollbar (field-sizing: Chrome/Edge 123+, Safari 18.4+; older browsers
   keep the fixed rows= height). Capped so a runaway paste still scrolls. */
textarea { field-sizing: content; min-height: 3.2em; max-height: 70vh; }
.form fieldset { border: 1px solid var(--line); border-radius: 8px; margin: 14px 0; }
.form legend { font-size: 13px; color: var(--muted); padding: 0 6px; }
.form-actions { margin: 16px 0; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
/* Internal notes — admin-only fields (never shown to a customer) get a warm
   tint so they read as "ours" at a glance. */
.form textarea.internal-note, .form input.internal-note,
.form-row input.internal-note, input.internal-note, textarea.internal-note {
  background: #fdf7ec; border-color: #e5d5ac;
}
/* Job page: the title IS the description, edited in place (transparent until
   hover/focus); the who/what strip under it (customer is a fact, not a
   field); all job fields on one line; the work-session table — one compact
   line per calendar day. */
.form textarea.job-title-input {
  width: calc(100% + 8px); margin: 0 0 2px -8px; padding: 4px 8px;
  font: inherit; font-size: 24px; font-weight: 700; line-height: 1.3; color: var(--navy);
  border: 1px solid transparent; border-radius: 8px; background: transparent; min-height: 0;
}
.form textarea.job-title-input:hover { border-color: var(--line); background: var(--card); }
.form textarea.job-title-input:focus { border-color: var(--teal); background: var(--card); outline: none; }
.job-strip { margin: 0 0 14px; font-size: 15px; }

/* Customer page: the same shape as the job page. The name IS the title, edited
   in place (transparent until hover/focus); the #/kind/owed strip under it are
   facts, not fields; boats reuse the compact .task-table. */
.form input.record-title-input {
  width: calc(100% + 8px); margin: 0 0 2px -8px; padding: 4px 8px;
  font: inherit; font-size: 24px; font-weight: 700; line-height: 1.3; color: var(--navy);
  border: 1px solid transparent; border-radius: 8px; background: transparent;
}
.form input.record-title-input:hover { border-color: var(--line); background: var(--card); }
.form input.record-title-input:focus { border-color: var(--teal); background: var(--card); outline: none; }
.record-strip { margin: 0 0 14px; font-size: 15px; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; }
.record-strip .strip-number { flex-direction: row; align-items: center; gap: 6px; margin: 0; }
.record-strip .strip-number input { width: 90px; }
.boats-table th:first-child, .boats-table td:first-child { width: 42%; }
/* Extra emails — separate boxes, "+ Add email" adds one, × removes one. */
.field-emails { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0; }
.field-emails-label { font-size: 13px; color: var(--muted); }
.email-list { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.email-box { display: inline-flex; align-items: center; gap: 2px; }
.email-box .email-input { width: 220px; }
.email-box .btn-link.btn-icon { line-height: 1; padding: 0 4px; }
.field-emails .add-email { flex: none; }
.portal-login { margin: 12px 0; }
.portal-login > summary { cursor: pointer; color: var(--teal); font-size: 13px; width: max-content; }
.portal-login > summary::-webkit-details-marker { display: none; }
.portal-login[open] > summary { margin-bottom: 6px; }

/* Searchable customer picker (calendar-started new job only). A text input over a
   filterable dropdown; the input inherits .form input styling. */
.customer-pick { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.customer-combo { position: relative; display: inline-block; }
.customer-combo-input { min-width: 260px; max-width: 340px; }
.customer-combo-list {
  position: absolute; z-index: 40; left: 0; top: 100%; margin: 3px 0 0;
  width: max(100%, 260px); max-height: 260px; overflow-y: auto;
  list-style: none; padding: 4px; background: var(--card);
  border: 1px solid var(--line); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.15);
}
.customer-combo-list[hidden] { display: none; }
.customer-combo-opt { padding: 6px 9px; border-radius: 6px; cursor: pointer; font-size: 14px; white-space: nowrap; }
.customer-combo-opt[hidden] { display: none; }
.customer-combo-opt:hover, .customer-combo-opt.is-active { background: var(--paper); }
.job-fields {
  display: grid; gap: 4px 12px;
  grid-template-columns: minmax(130px, 1.3fr) minmax(100px, .8fr) minmax(78px, .6fr)
                         minmax(110px, .9fr) minmax(95px, .7fr) minmax(170px, 1.6fr);
}
.job-fields label { min-width: 0; }
.job-fields input, .job-fields select { width: 100%; min-width: 0; }
@media (max-width: 900px) {
  .job-fields { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
}
.task-table-wrap { overflow-x: auto; }
.task-table { margin: 4px 0 8px; }
.task-table th { padding: 6px 8px; font-size: 12.5px; }
.task-table td { padding: 4px 8px 4px 0; vertical-align: middle; }
.task-table td:first-child { padding-left: 8px; }
.task-table tr:hover td { background: inherit; }
.task-table input, .task-table select {
  width: 100%; min-width: 0; padding: 6px 8px; border: 1px solid var(--line);
  border-radius: 8px; font-size: 14px; background: var(--card); color: var(--text);
}
.task-table input.internal-note { background: #fdf7ec; }
.task-table .col-task { min-width: 150px; }
.task-table .col-date { width: 155px; }
.task-table .col-time { width: 82px; }
.task-table .col-hours { width: 80px; }
.task-table .col-state { width: 118px; }
.task-table .col-remove { width: 34px; text-align: center; }
.job-total { margin: 4px 0 0; padding: 8px 12px; background: var(--card); border: 1px solid var(--line); border-radius: 8px; font-size: 14px; color: var(--muted); }
.job-total strong { color: var(--text); font-size: 16px; margin: 0 4px; }
.form-row { display: flex; gap: 14px; align-items: flex-end; flex-wrap: wrap; margin: 10px 0; }
.form-row .grow { flex: 1; }
.form-row input[type="submit"] { margin-bottom: 8px; }
.checkbox { flex-direction: row !important; align-items: center; gap: 8px !important; }

/* ── The calm job page (spec §2/§7): the record reads as text and each fact
   commits itself. No global Save; the only faces that appear are the ones the
   cursor is over. ─────────────────────────────────────────────────────────── */
.job-record { margin-bottom: 18px; }

/* Title — the page heading, edited in place (job description / customer name). */
.inline-title, .inline-name { display: block; }
.inline-title .inline-display, .inline-name .inline-display {
  display: inline-block; font-size: 24px; font-weight: 700; line-height: 1.3;
  color: var(--navy); margin: 2px 0; padding: 3px 8px; margin-left: -8px;
}
.inline-title .inline-display.is-blank, .inline-name .inline-display.is-blank { color: var(--muted); font-weight: 400; }
.inline-title .inline-input, .inline-name .inline-input {
  width: calc(100% + 8px); margin-left: -8px; font: inherit; font-size: 24px;
  font-weight: 700; line-height: 1.3; color: var(--navy);
}
.job-strip { margin: 0 0 14px; font-size: 15px; }

/* Facts row — small uppercase labels, bold values under (spec §4 mock). */
.job-facts {
  display: flex; flex-wrap: wrap; gap: 6px 26px;
  padding: 12px 0 14px; margin: 0 0 12px; border-top: 1px solid var(--line);
}
.job-fact { display: flex; flex-direction: column; min-width: 0; }
.fact-label {
  font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 1px;
}

/* The two faces of one field: the form hides until the field is engaged. */
.inline-edit { position: relative; }
.inline-edit .inline-form { display: none; margin: 0; }
.inline-edit.is-editing .inline-display { display: none; }
.inline-edit.is-editing .inline-form,
.inline-edit.is-select .inline-form { display: block; }

/* Resting value — plain text until the cursor says it yields (§7 hover). */
.inline-display {
  display: inline-block; font-weight: 600; color: var(--text);
  border: 1px solid transparent; border-radius: 6px; padding: 1px 6px; margin-left: -6px;
  cursor: text;
}
.inline-display:hover, .inline-display:focus-visible {
  background: #fdf7ec; border-color: #ecdfc2; outline: none;
}
.inline-display:hover::after { content: " ✎"; color: var(--accent); font-size: 12px; }
.inline-display.is-blank { color: var(--muted); font-weight: 400; }

/* Section label — one QUIET voice for the job page's supporting sections (the
   same small-caps as .fact-label), so naming a section adds a word without
   adding a box: NOTES · BILLING · BILL · PHOTOS & VIDEOS · CUSTOMER LINK all
   read in this register. Tasks is deliberately NOT one of them — the tasks
   are the job, so that section alone gets .record-title, a real heading. The
   hint drops back to sentence case: it is a sentence, not a label. */
.record-label {
  font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); margin: 14px 0 5px;
}
/* The one prominent section header on the page (Tasks): a navy band, not just
   navy text — the tasks are the job, and the bar is what makes that section
   findable from across the room. The hint keeps its quiet sentence voice but
   swaps to a light ink to stay readable on the band. */
.record-title {
  font-size: 14.5px; font-weight: 700; color: #fff; background: var(--navy);
  margin: 18px 0 8px; padding: 6px 12px; border-radius: 8px;
}
.record-title .record-label-hint { color: rgba(255, 255, 255, .72); }
.record-label-hint {
  margin-left: 8px; text-transform: none; letter-spacing: 0;
  font-size: 11.5px; font-weight: 400; color: var(--muted);
}
/* Inside a card the generic h2/h3 rules (.billing-strip h2, .share-link h3)
   outrank a lone class — this keeps the voice identical there too. */
.card h2.record-label, .card h3.record-label { margin: 8px 0 8px; font-size: 10.5px; }
/* Buttons and trailing action links riding inside a label keep their own
   voice — they are actions, not part of the label. */
.record-label .btn, .record-label .float-right {
  text-transform: none; letter-spacing: normal; font-weight: 400;
}

/* Notes — quiet italic, full width. */
.job-notes-field { margin: 8px 0 4px; }
.inline-notes .inline-display { display: block; margin-left: 0; font-style: italic; font-weight: 400; }
.inline-notes .inline-input { width: 100%; }

/* Inputs carry the app's field look (they sit outside .form, so style them here). */
/* Typed inputs get a real box while editing; selects stay quiet (rule below) —
   they are always-live and must read as plain text at rest. */
.inline-edit .inline-input {
  padding: 6px 8px; border: 1px solid var(--line); border-radius: 8px;
  font-size: 14.5px; background: var(--card); color: var(--text); font-family: inherit;
}
.inline-edit textarea { min-height: 0; }
.inline-number { width: 110px; }
.inline-edit .internal-note { background: #fdf7ec; border-color: #e5d5ac; }

/* Boat / Attention — a select that reads as text until hovered, opens natively. */
.inline-select {
  appearance: none; -webkit-appearance: none; border: 1px solid transparent;
  background-color: transparent; font-weight: 600; color: var(--text);
  padding: 1px 20px 1px 6px; margin-left: -6px; border-radius: 6px; cursor: pointer;
  max-width: 220px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'><path d='M2 3.5l3 3 3-3' fill='none' stroke='%23b6bfcc' stroke-width='1.3'/></svg>");
  background-repeat: no-repeat; background-position: right 4px center;
}
.inline-select:hover { background-color: #fdf7ec; border-color: #ecdfc2; }
.inline-select:focus { outline: none; border-color: var(--teal); background-color: var(--card); }

/* Saved ✓ — a green confirmation that holds ~4.4 s, then fades; gone at 5 s.
   Rendered only by the saved_badge helper, only inside a 200 response. Reduced
   motion still ends it — the fade becomes a snap, never a badge stuck forever. */
.inline-saved {
  color: var(--green); font-weight: 600; font-size: 12px; margin-left: 6px;
  animation: inline-saved-fade 5s ease-out forwards;
}
@keyframes inline-saved-fade { 0%, 88% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } }
@media (prefers-reduced-motion: reduce) { .inline-saved { animation-timing-function: steps(1, end); } }

/* Rejected value stays open with the reason underneath (§7 error path). */
.inline-error { display: block; color: var(--red); font-size: 12px; margin-top: 3px; }

/* Toasts — the app-wide failure voice (feedback controller on <body>). Any
   commit the server refuses or never receives pops one; it lives the same 5 s
   as Saved ✓ (the controller removes the node, so reduced motion can't strand
   it) and a click dismisses it sooner. Success never toasts — that's Saved ✓. */
.toasts {
  position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 8px; z-index: 300;
}
.toast-error {
  background: var(--red); color: #fff; font-size: 13px; font-weight: 600;
  padding: 9px 16px; border-radius: 8px; box-shadow: 0 4px 14px rgb(0 0 0 / .25);
  cursor: pointer; animation: toast-in .18s ease-out, inline-saved-fade 5s ease-out forwards;
}
@keyframes toast-in { from { opacity: 0; transform: translateY(6px); } }
@media (prefers-reduced-motion: reduce) { .toast-error { animation: none; } }

/* Silent under 200 ms; a small spinner past it. The [hidden] guard matters:
   the display rule below would otherwise override the attribute. */
.inline-spinner[hidden] { display: none; }
.inline-spinner {
  display: inline-block; width: 12px; height: 12px; margin-left: 6px; vertical-align: -2px;
  border: 2px solid var(--line); border-top-color: var(--teal); border-radius: 50%;
  animation: inline-spin .6s linear infinite;
}
@keyframes inline-spin { to { transform: rotate(360deg); } }

/* Touch / coarse pointers have no hover — keep a faint always-on tint so an
   editable value still reads as "you can change me" (§7). */
@media (pointer: coarse) {
  .inline-display { background: #fdf7ec80; border-color: #f0e6cd; }
  .inline-select { background-color: #fdf7ec80; }
}

/* ── The calm customer record (spec §4/§7): the same inline-edit contract as the
   job page. Name is the title, facts read as text, boats are per-row commit rows,
   the portal line expands into two instant toggles. ────────────────────────── */
.customer-record { margin-bottom: 18px; }
.record-facts {
  display: flex; flex-wrap: wrap; gap: 6px 26px;
  padding: 12px 0 14px; margin: 0 0 12px; border-top: 1px solid var(--line);
}
.record-fact { display: flex; flex-direction: column; min-width: 0; }
.customer-notes-field { margin: 4px 0 10px; }
.inline-hint { display: block; color: var(--muted); font-size: 11px; margin-top: 3px; }

/* Boats — a bordered list where each row commits itself (mirrors .task-list). */
.record-boats { margin: 10px 0 4px; }
/* Boats names a group of facts — label species, like the fact labels in it. */
.record-boats-h {
  margin: 0 0 6px; font-size: 10.5px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.boat-list {
  border: 1px solid var(--line); border-radius: 10px; overflow: visible;
  background: var(--card); margin: 6px 0 12px;
}
.boat-rows > turbo-frame { display: block; }
.boat-rows > turbo-frame:not(:last-child) .boat-row { border-bottom: 1px solid #eef1f6; }
.boat-display {
  display: grid; align-items: center; gap: 10px;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr) auto;
  padding: 9px 14px; font-size: 14px;
}
.boat-row.is-editing .boat-display { display: none; }
.boat-row:hover .boat-display { background: #f8fafd; }
.boat-cell {
  min-width: 0; cursor: text; border-radius: 6px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.boat-cell:hover { background: #fdf7ec; box-shadow: 0 0 0 1px #ecdfc2; }
.boat-label .cell-strong { font-weight: 600; }
.boat-notes-add { color: var(--muted); }
.boat-acts {
  justify-self: end; display: flex; gap: 8px; align-items: center;
  color: var(--grey); visibility: hidden;
}
.boat-row:hover .boat-acts, .boat-row:focus-within .boat-acts { visibility: visible; }
@media (hover: none) { .boat-acts { visibility: visible; } }
.boat-acts form { margin: 0; display: inline-flex; }
.boat-edit-row { display: none; margin: 0; }
.boat-row.is-editing .boat-edit-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 8px 12px;
}
.boat-edit-row input {
  padding: 6px 8px; border: 1px solid var(--line); border-radius: 8px;
  font-size: 14px; font-family: inherit; background: var(--card); color: var(--text);
}
.boat-edit-row .be-label { flex: 1 1 150px; min-width: 120px; }
.boat-edit-row .be-notes { flex: 2 1 180px; min-width: 140px; }
.boat-edit-row .internal-note { background: #fdf7ec; border-color: #e5d5ac; }
.boat-row-reason { margin: 0; padding: 2px 14px 8px; color: var(--red); font-size: 12px; }

/* Portal login — a quiet summary that expands into two instant toggles. */
.portal-record { margin: 10px 0; }
.portal-record > summary {
  cursor: pointer; color: var(--teal); font-size: 13px; width: max-content; list-style: none;
}
.portal-record > summary::-webkit-details-marker { display: none; }
.portal-record[open] > summary { margin-bottom: 6px; }
.portal-explain { margin: 2px 0 8px; }
.portal-toggle { margin: 2px 0; }
.portal-toggle form { margin: 0; }

/* ── The task list on the calm record (spec §4/§5): a bordered list where each
   row commits itself. Display face reads as text; clicking a cell swaps the
   whole row to inputs (Tab walks the cells). No "Save tasks" button. ────────── */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.task-list {
  border: 1px solid var(--line); border-radius: 10px; overflow: visible;
  background: var(--card); margin: 6px 0 12px;
}
.task-rows > turbo-frame { display: block; }
.task-rows > turbo-frame:not(:last-child) .task-row { border-bottom: 1px solid #eef1f6; }

/* Display face — the resting row grid (state · name · date · time · hours · acts). */
.task-display {
  display: grid; align-items: center; gap: 10px;
  grid-template-columns: 20px minmax(0, 1fr) 138px 96px 52px auto;
  padding: 9px 14px; font-size: 14px;
}
.task-row.is-editing .task-display { display: none; }
.task-row:hover .task-display { background: #f8fafd; }
.task-cell { min-width: 0; cursor: text; border-radius: 6px; }
/* Name cell holds the click-to-edit text plus the (non-editing) 📎 count beside it. */
.task-name { display: flex; align-items: baseline; gap: 6px; }
.task-name-text { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.task-name-fallback { font-weight: 400; color: var(--muted); }
.task-clip { flex: none; text-decoration: none; cursor: pointer; }
.task-clip:hover { text-decoration: underline; }
/* The hover 📎 is a file-picker label — no default button chrome, camera-cursor. */
.task-act-attach { margin: 0; display: inline-flex; }
.task-act-attach label.task-act { cursor: pointer; }
.task-date, .task-time, .task-hours {
  color: #4a5468; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.task-cell:hover { background: #fdf7ec; box-shadow: 0 0 0 1px #ecdfc2; }
/* Second-line internal note — amber chip spanning under the row (spec §4 mock). */
.task-note {
  grid-column: 2 / -1; justify-self: start; margin-top: -2px;
  font-size: 12px; color: #98700f; background: #fdf7ec;
  border-radius: 6px; padding: 2px 8px; white-space: normal;
}
.task-row.is-canceled .task-name { text-decoration: line-through; }
.task-row.is-canceled .task-date,
.task-row.is-canceled .task-time,
.task-row.is-canceled .task-hours,
.task-row.is-canceled .task-name { color: var(--muted); }

/* State dot — leftmost cell, calendar colours; a button (toggle) or a menu dot. */
.task-dot {
  width: 11px; height: 11px; padding: 0; border: none; border-radius: 50%;
  cursor: pointer; display: block; list-style: none;
}
.task-dot::-webkit-details-marker { display: none; }
.dot-planned { background: var(--blue); }
.dot-done { background: var(--green); }
.dot-canceled { background: var(--grey); }
.task-dot-form { margin: 0; display: flex; justify-self: center; }

/* Hover actions — quiet, right-aligned; revealed as the cursor crosses the row. */
.task-acts {
  justify-self: end; display: flex; gap: 8px; align-items: center;
  color: var(--grey); visibility: hidden;
}
.task-row:hover .task-acts, .task-row:focus-within .task-acts { visibility: visible; }
@media (hover: none) { .task-acts { visibility: visible; } }
.task-acts form { margin: 0; display: inline-flex; }
.task-act {
  background: none; border: none; padding: 2px; cursor: pointer;
  font-size: 14px; line-height: 1; color: inherit; font-family: inherit;
}
.task-act:hover:not([disabled]) { color: var(--navy); }
.task-act[disabled] { cursor: not-allowed; opacity: .45; }
.task-act-del { color: var(--red); }
.task-act.btn-icon svg { width: 15px; height: 15px; }
.task-menu-dots { font-size: 16px; letter-spacing: 1px; }

/* Cancel / recover menu — reuses the top-bar dropdown panel, dropped under the dot. */
.task-menu { position: relative; display: inline-flex; }
.task-menu > summary { list-style: none; cursor: pointer; }
.task-menu > summary::-webkit-details-marker { display: none; }
.task-menu-panel { right: auto; left: 0; top: calc(100% + 6px); min-width: 150px; }
.task-menu-panel form { margin: 0; }

/* Edit face — every cell an input on one wrapping line (Tab walks the row). */
.task-edit { display: none; margin: 0; }
.task-row.is-editing .task-edit {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 8px 12px;
}
.task-edit input {
  padding: 6px 8px; border: 1px solid var(--line); border-radius: 8px;
  font-size: 14px; font-family: inherit; background: var(--card); color: var(--text);
}
.task-edit .te-name { flex: 1 1 150px; min-width: 120px; }
.task-edit .te-date { width: 150px; }
.task-edit .te-times { display: inline-flex; align-items: center; gap: 4px; }
.task-edit .te-time { width: 64px; }
.task-edit .te-hours { width: 66px; }
.task-edit .te-notes { flex: 1 1 100%; }
.task-edit .internal-note { background: #fdf7ec; border-color: #e5d5ac; }
.task-row-error { flex-basis: 100%; color: var(--red); font-size: 12px; }

/* "+ Add task" — the ghost last row, where the eye stops after reading the list. */
.task-add-row {
  display: block; width: 100%; text-align: left; font-family: inherit;
  padding: 10px 14px; border: none; border-top: 1px solid #eef1f6;
  border-radius: 0 0 10px 10px; background: #fbfcfe;
  color: var(--navy); font-weight: 600; font-size: 13.5px; cursor: pointer;
}
.task-add-row:hover { background: #f4f7fb; }
.task-add-row .hint { color: var(--muted); font-weight: 400; margin-left: 12px; font-size: 12px; }

@media (max-width: 640px) {
  .task-display { grid-template-columns: 20px 1fr auto; row-gap: 2px; }
  .task-display .task-time, .task-display .task-hours { display: none; }
}

/* The read-only billing strip (spec §4) — one line per live task, all waiting. */
.billing-strip { margin: 0 0 14px; }
.billing-strip h2 { margin: 6px 0 8px; }
.billing-row {
  display: flex; align-items: center; gap: 10px; padding: 7px 0;
  border-bottom: 1px solid var(--line); font-size: 14px;
}
.billing-row:last-of-type { border-bottom: none; }
.billing-name { font-weight: 600; flex: 1; min-width: 0; }
.billing-date { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.billing-chip { flex: none; }
.billing-foot { margin: 8px 0 0; font-size: 12px; }

/* media */
.media-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; margin: 10px 0; }
.media-item { margin: 0; background: var(--card); border: 1px solid var(--line); border-radius: 8px; padding: 6px; }
.media-item img, .media-item video { width: 100%; height: 130px; object-fit: cover; border-radius: 6px; display: block; }
.media-item figcaption { display: flex; justify-content: space-between; align-items: center; font-size: 11px; margin-top: 4px; overflow: hidden; }
.media-upload { margin: 10px 0; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

/* ── Grouped gallery (spec §6): the Photos & videos card is one group per task
   with media, then a "Whole job" group. Each group is a header (linking back to
   its task row) over a row of fixed thumbnails ending in a drop/📷 add tile. ─── */
.media-gallery { margin: 4px 0 6px; }
.media-group { margin: 0 0 18px; scroll-margin-top: 70px; border-radius: 8px; }
.media-group-head { margin: 0 0 8px; font-size: 13px; color: var(--navy); }
.media-group-head .when { color: var(--muted); font-weight: 400; }
.media-group-back { color: inherit; text-decoration: none; }
.media-group-back:hover { text-decoration: underline; }
.media-thumbs { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-start; padding: 4px; border-radius: 8px; }
.media-thumbs.is-dropping { outline: 2px dashed var(--accent); outline-offset: -2px; background: #fdf7ec; }
.media-thumb { position: relative; margin: 0; width: 132px; }
.media-thumb img, .media-thumb video { width: 132px; height: 96px; object-fit: cover; border-radius: 8px; border: 1px solid var(--line); display: block; }
.media-thumb .media-file { display: block; font-size: 11px; padding: 6px; word-break: break-all; }
/* Delete tucks in the corner, revealed on hover of its thumbnail (spec §6 ③). */
.media-thumb-del { position: absolute; top: 4px; right: 4px; margin: 0; padding: 2px;
  background: rgba(255, 255, 255, .9); border-radius: 6px; line-height: 0; opacity: 0; transition: opacity .12s; }
.media-thumb-del svg { width: 14px; height: 14px; }
.media-thumb:hover .media-thumb-del, .media-thumb:focus-within .media-thumb-del { opacity: 1; }
.media-add-form { margin: 0; }
.media-add { display: flex; align-items: center; justify-content: center; text-align: center;
  width: 132px; height: 96px; border: 1.5px dashed #c9d2df; border-radius: 8px;
  color: var(--muted); font-size: 12px; cursor: pointer; }
.media-add:hover { border-color: var(--accent); color: var(--navy); background: #fdf7ec; }
@media (hover: none) { .media-thumb-del { opacity: 1; } }

/* Brief flash when a 📎 count or a group header jumps you to its counterpart
   (spec §6, the two-way link). :target fires once on navigation — enough here. */
@keyframes media-target-flash { 0% { background: #fbeecb; } 100% { background: transparent; } }
.media-group:target { animation: media-target-flash 1.6s ease-out; }
.task-rows > turbo-frame:target .task-row { animation: media-target-flash 1.6s ease-out; }
@media (prefers-reduced-motion: reduce) {
  .media-group:target, .task-rows > turbo-frame:target .task-row { animation: none; }
}

/* shareable customer link (staff job page) */
.share-link { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--line); }
.share-link h3 { margin: 0 0 2px; font-size: 14px; }
.share-link-row { display: flex; gap: 8px; margin-top: 8px; }
.share-link-row input { flex: 1; font-family: monospace; font-size: 12px; padding: 6px 8px; }
.share-send { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.share-send form { margin: 0; }

/* customer-facing share page: show media at full size, one per row */
body.public .media-grid { grid-template-columns: 1fr; }
body.public .media-item img, body.public .media-item video { height: auto; max-height: 70vh; object-fit: contain; }

/* Customer share page, told day by day (spec §6): a section per task, its photos
   in a row beneath. Read-only — no add tile, no delete (readonly _media_thumbs). */
.share-header { margin: 0 0 6px; }
.share-header h1 { margin: 0 0 2px; }
.share-day { padding: 14px 0 4px; border-top: 1px solid var(--line); }
.share-day:first-of-type { border-top: none; }
.share-day h2 { margin: 0 0 2px; font-size: 15px; color: var(--navy); }
.share-done { color: var(--green); font-weight: 700; }
.share-day .when { margin: 0 0 8px; font-size: 12.5px; color: var(--muted); }
/* Share thumbnails read at a comfortable size and never overflow the phone width. */
body.public .media-thumbs { padding: 0; }
body.public .media-thumb, body.public .media-thumb img, body.public .media-thumb video { width: min(160px, 44vw); }
body.public .media-thumb img, body.public .media-thumb video { height: auto; }

/* auth */
.auth-body { display: grid; place-items: center; min-height: 100vh; background: linear-gradient(135deg, var(--navy), var(--teal)); }
.auth-card { background: var(--paper); border-radius: 14px; padding: 28px 32px; width: min(400px, 92vw); border-bottom: 4px solid var(--accent); }

/* ── The contact log (spec §5): one surface — pinned plans, composer, stream —
   replacing the Next-contact card, the interactions form and its table. Pinned
   plans read on a paper tint; the composer is one line; the stream is typography,
   not a grid. Rows/entries are their own Turbo frames. ─────────────────────── */
.contact-log { margin-bottom: 22px; }
.contact-log > h2 { margin-bottom: 10px; }
.pinned > turbo-frame, .log-stream > turbo-frame { display: block; }

/* Pinned plans — paper-tinted block, one row per pending contact. */
.pinned { background: #fdf7ec; border: 1px solid #ecdfc2; border-radius: 10px; padding: 2px 12px; margin-bottom: 12px; }
.prow { display: flex; align-items: flex-start; gap: 8px; padding: 9px 0; border-bottom: 1px solid #ecdfc2; }
.pinned > turbo-frame:last-of-type .prow { border-bottom: none; }
.prow-ic { flex: none; font-size: 15px; line-height: 1.5; }
.prow-main { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
.prow-facts { display: inline-flex; align-items: center; gap: 3px; }
.prow-live { display: inline; margin: 0; }
.prow-kind { font-weight: 700; max-width: none; }
.prow-sep { color: var(--muted); }
.prow-date {
  border: 1px solid transparent; background: transparent; font-weight: 700; color: var(--text);
  font-family: inherit; font-size: 14px; padding: 1px 5px; margin-left: -3px; border-radius: 6px; cursor: pointer;
}
.prow-date:hover { background: #fff; border-color: #ecdfc2; }
.prow-date.text-danger { color: var(--red); }
.prow-note .inline-display { font-weight: 400; color: var(--muted); }
.prow-note .inline-display.is-blank { font-style: italic; }
.prow-note .inline-input { padding: 3px 7px; }
.prow-general { font-size: 12px; }
.prow-acts { display: flex; align-items: center; gap: 6px; flex: none; }
.prow-x-form { display: inline; margin: 0; }
.prow-x { color: var(--muted); padding: 2px 4px; }
.prow-x:hover { color: var(--red); }
.pinned-error { color: var(--red); font-size: 12.5px; margin: 6px 0; }

/* the offer-thread chip, shared by pinned rows and stream entries */
.chip-offer {
  display: inline-block; font-size: 11px; color: var(--navy); background: var(--blue-bg);
  border: 1px solid #d7e2f2; border-radius: 999px; padding: 0 8px; text-decoration: none; white-space: nowrap;
}
.chip-offer:hover { text-decoration: underline; }

/* "+ Plan a contact…" (general) and the "plan the next?" ghost — one inline line. */
.plan-add { margin: 8px 0 4px; }
.plan-add-link { list-style: none; cursor: pointer; color: var(--teal); font-size: 13px; width: max-content; }
.plan-add-link::-webkit-details-marker { display: none; }
.plan-form { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 8px 0; }
.plan-kind, .plan-date, .plan-note {
  padding: 5px 8px; border: 1px solid var(--line); border-radius: 8px; font-family: inherit;
  font-size: 13px; background: var(--card); color: var(--text);
}
.plan-note { flex: 1 1 140px; min-width: 0; }
.plan-next { margin: 8px 0 4px; padding: 8px 10px; border: 1px dashed #e0c98e; border-radius: 8px; }
.plan-next-form { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0; }
.plan-next-label { color: var(--muted); font-size: 13px; }
.plan-next-skip { border: none; background: none; color: var(--muted); text-decoration: underline; cursor: pointer; font-size: 13px; padding: 0 2px; }

/* Composer — segmented kind row (wraps above the field on narrow screens), a
   growing summary, the commit-time "now ▾" chip, and Log. */
.composer { margin: 6px 0 16px; }
.composer-seg { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 8px; }
.composer-seg .seg { position: relative; margin: 0; }
.composer-seg .seg input { position: absolute; opacity: 0; width: 0; height: 0; }
.composer-seg .seg-face {
  display: inline-flex; align-items: center; gap: 4px; font-size: 12.5px; padding: 4px 11px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--card); color: var(--muted);
  cursor: pointer; white-space: nowrap;
}
.composer-seg .seg:has(input:checked) .seg-face { background: var(--navy); color: #fff; border-color: var(--navy); }
.composer-seg .seg input:focus-visible + .seg-face { outline: 2px solid var(--accent); outline-offset: 1px; }
.composer-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.composer-summary {
  flex: 1 1 240px; min-width: 0; padding: 8px 11px; border: 1px solid var(--line); border-radius: 8px;
  font-size: 14.5px; font-family: inherit; background: var(--card); color: var(--text);
}
.composer-summary:focus { outline: none; border-color: var(--teal); }
.composer-log { flex: none; }
.composer-error { display: block; color: var(--red); font-size: 12px; margin-top: 5px; }
.composer-context {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--navy);
  background: var(--blue-bg); border: 1px solid #d7e2f2; border-radius: 999px; padding: 2px 6px 2px 10px;
}
.composer-context[hidden] { display: none; }
.composer-context-x { border: none; background: none; cursor: pointer; color: var(--navy); font-size: 12px; line-height: 1; padding: 0 2px; }
.composer-when { position: relative; flex: none; }
.composer-when > summary { list-style: none; cursor: pointer; font-size: 12.5px; color: var(--muted); border: 1px solid var(--line); border-radius: 999px; padding: 5px 11px; white-space: nowrap; }
.composer-when > summary::-webkit-details-marker { display: none; }
.composer-now::after { content: " ▾"; font-size: 9px; }
.composer-when[open] > summary { color: var(--navy); border-color: var(--navy); }
.composer-when-input {
  position: absolute; right: 0; top: calc(100% + 5px); z-index: 6; padding: 6px 8px;
  border: 1px solid var(--line); border-radius: 8px; font-family: inherit; font-size: 13px;
  background: var(--card); box-shadow: 0 6px 16px rgba(0, 0, 0, .14);
}

/* Stream — month labels group the season; entries are records, not grid rows. */
.log-stream .mlabel {
  font-size: 11px; font-weight: 700; letter-spacing: .5px; color: var(--muted);
  text-transform: uppercase; margin: 16px 0 2px;
}
.log-empty { padding: 8px 0; }
.erow { display: flex; align-items: baseline; gap: 8px; padding: 8px 0; border-top: 1px solid var(--line); }
.erow-ic { flex: none; font-size: 14px; }
.erow-main { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 8px; }
.erow-edit { min-width: 0; max-width: 100%; }
.erow-edit .inline-display { font-weight: 400; color: var(--text); }
.erow-edit.is-editing { flex: 1 1 100%; }
.erow-sum-input { width: 100%; min-width: 240px; }
.erow-when { flex: none; font-size: 12px; color: var(--muted); white-space: nowrap; }
.erow-del { flex: none; opacity: 0; transition: opacity .12s; color: var(--red); text-decoration: none; }
.erow-del-form { display: inline; margin: 0; }
.erow:hover .erow-del, .erow:focus-within .erow-del { opacity: 1; }
@media (hover: none) { .erow-del { opacity: 1; } }
.erow.is-fresh { animation: erow-fresh 2.4s ease-out forwards; border-radius: 6px; }
@keyframes erow-fresh { 0%, 20% { background: #fbf1d8; } 100% { background: transparent; } }
@media (prefers-reduced-motion: reduce) { .erow.is-fresh { animation: none; } }
