/* qr-punch — Home Sweet Homes punch sheets. Mobile first.

   Palette is the HSH *document* palette, not a new one: the same navy, link
   blue, tint and rule colour the 1011 punch sheets and the other HSH documents
   use (docs/BRAND.md records where they come from). A sheet on a phone and the
   same sheet on paper should read as one family.

   This gets used on a phone, outdoors, by someone wearing work gloves: big tap
   targets, high contrast, no hover-only affordances, no horizontal scrolling.
   Desktop just gets a wider column. */

:root {
  --navy: #1B3A5C;       /* HSH navy — brand bar, progress, table heads */
  --blue-dk: #204a8c;    /* HSH link / control blue */
  --blue-bg: #e3ecfb;    /* HSH tint */
  --ink: #1a1a1a;
  --muted: #555;
  --line: #9aa7b8;       /* HSH rule — card and table borders */
  --hair: #dde3ea;       /* lighter rule, inside a card */
  --bg: #f4f6f9;
  --card: #ffffff;
  --accent: var(--blue-dk);
  --done: #15803d;
  --done-bg: #ecfdf3;
  --warn: #b45309;
  --tap: 48px;           /* minimum tap target */
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font: 17px/1.45 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  color: var(--ink);
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
}

/* ---------- brand bar ----------
   Same shape as the .hd block on the printed HSH sheets: navy bar, mark and
   wordmark on the left, the sheet's own name under it. */

header.brand {
  position: sticky; top: 0; z-index: 10;
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 8px 12px;
  color: #fff;
  background: var(--navy);
  border-bottom: 3px solid var(--blue-dk);
}
header.brand .mark-link {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--tap);
  color: #fff; text-decoration: none;
}
/* Two logos ship in the markup and CSS picks one (app/server.py page()):
   the reverse lock-up on screen, the one-colour mark + live text in print. */
header.brand .logo-screen {
  display: block; height: 30px; width: auto; max-width: 46vw;
}
header.brand .logo-print { display: none; }
.brand-id { flex: 1; min-width: 0; display: flex; flex-direction: column; }
/* The wordmark is inside the reverse logo on screen, so the text copy is kept
   for print and for anyone whose browser blocks images — not repeated twice. */
.brand-id .wordmark { display: none; }
.brand-id .brand-sub {
  font-size: 13px; font-weight: 500; color: #c6d7ea;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
header.brand .lang {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--tap); min-height: var(--tap);
  padding: 0 10px;
  color: #fff; text-decoration: none; font-weight: 600; font-size: 15px;
  border: 1px solid rgba(255, 255, 255, .55); border-radius: 8px;
}
/* The Admin tab, rendered only for an admin session (app/server.py
   nav_tabs()). Filled rather than outlined, so it does not read as a second
   language toggle; same tap target as everything else in the bar. */
header.brand .brandnav { display: flex; align-items: center; }
header.brand .admintab {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--tap); padding: 0 12px;
  color: var(--navy); background: #fff;
  text-decoration: none; font-weight: 700; font-size: 14px;
  border-radius: 8px; white-space: nowrap;
}

footer.brand-ft {
  display: flex; flex-wrap: wrap; gap: 4px 10px;
  padding: 12px 14px 20px;
  font-size: 12px; color: var(--muted);
  border-top: 1px solid var(--hair);
}
footer.brand-ft .ent { font-weight: 600; color: var(--navy); }
/* Middot-separated identity line, the way the printed HSH sheets read. */
footer.brand-ft span + span::before { content: "·"; margin-right: 6px; }

main { max-width: 720px; margin: 0 auto; padding: 14px 14px 24px; }

section {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 14px;
  margin-bottom: 14px;
}
section h2 { margin: 0 0 8px; font-size: 19px; color: var(--navy); }

.crumb, .byline, .hint, .muted { color: var(--muted); font-size: 14px; }
.crumb { margin: 0 0 4px; }
.code { font-size: 13px; color: var(--muted); font-weight: 400; }
.empty { color: var(--muted); font-style: italic; }

/* ---------- closed project (read-only) ----------
   A QR code stays taped to the wall after the job ends. The sheet still opens;
   it just explains itself, in both languages at once. */

section.closed-banner {
  border-color: var(--warn);
  border-left-width: 6px;
  background: #fffbeb;
}
section.closed-banner h2 { color: var(--warn); }
section.closed-banner p { margin: 0 0 6px; font-size: 15px; }
section.closed-banner p:last-child { margin-bottom: 0; }
.tag.closed-tag { border-color: var(--warn); color: var(--warn);
                  background: #fffbeb; }

/* The read-only checklist keeps the row shape so a finished sheet still reads
   like a punch list, but there is nothing to tap that would silently do
   nothing. */
.check .box {
  flex: 0 0 auto; width: 28px; height: 28px; margin: 2px 0 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 2px solid var(--line); border-radius: 6px;
  color: var(--done); font-weight: 700;
}
.check .box-done { border-color: var(--done); }

/* ---------- progress ---------- */

.bar {
  height: 14px; border-radius: 7px; background: var(--blue-bg);
  border: 1px solid var(--line);
  overflow: hidden; margin: 10px 0 6px;
}
.bar > span { display: block; height: 100%; background: var(--navy); }
.pct { margin: 0; font-size: 15px; }

/* The ring from the approved mockup (WP10 A1). It sits beside the bar rather
   than instead of it: the ring is the number you read from arm's length, the
   bar is what prints. The print block hides the ring and keeps the bar. */
.progress { display: flex; align-items: center; gap: 14px; }
.progress-txt { flex: 1 1 auto; min-width: 0; }
.progress .bar { margin-top: 0; }
svg.ring { flex: 0 0 auto; display: block; }
svg.ring .ring-track { stroke: var(--blue-bg); }
svg.ring .ring-on { stroke: var(--navy); }
svg.ring .ring-pct {
  font: 700 20px/1 system-ui, -apple-system, sans-serif; fill: var(--navy);
}
svg.ring .ring-sub {
  font: 400 11px/1 system-ui, -apple-system, sans-serif; fill: var(--muted);
}

/* ---------- both languages on one line ----------
   ES first, EN under it (WP10 A1). The second line is smaller and quieter but
   NOT grey-on-grey: it is read in daylight, outdoors, by whoever did not tap
   the language toggle. */
.bi { display: block; }
.bi .bi-1 { display: block; }
.bi .bi-2 {
  display: block; font-size: 14px; color: var(--muted); margin-top: 1px;
}
h2.bi .bi-1 { display: block; }
h2.bi .bi-2 { font-size: 15px; font-weight: 400; }

/* ---------- the crew on this job ----------
   Names, never numbers (DARRELL 2026-10-30 12:36 AM PT). A marked chip is
   whoever has claimed the room; unmarked chips are people who could. */
.crewline { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
            margin: 10px 0 0; }
.crewline-lbl { font-size: 13px; color: var(--muted); text-transform: uppercase;
                letter-spacing: .05em; }
ul.chips { display: flex; gap: 6px; flex-wrap: wrap;
           list-style: none; margin: 0; padding: 0; }
ul.chips > li.chip {
  padding: 5px 11px; border-radius: 999px; font-size: 14px;
  background: var(--card); border: 1px solid var(--line); color: var(--ink);
}
ul.chips > li.chip.is-claim {
  background: var(--navy); border-color: var(--navy); color: #fff;
  font-weight: 600;
}

/* ---------- the admin / Mission Control dashboard (WP10 A2) ----------
   The approved mockup's office view: navy project header, a stats row, a grid
   of room cards and a detail pane for the selected room. It lives at the top
   of /admin, which is where Mission Control's iframe lands.

   Mobile first like everything else: the pane sits under the grid on a phone
   and beside it from 900px up, and the whole section is allowed to be wider
   than `main`'s 720px reading column because a card grid is not prose. */

section.dash { padding: 0; overflow: hidden; }

.projtabs { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 12px 0; }
.projtabs .projtab {
  display: inline-flex; align-items: center; min-height: 36px; padding: 0 12px;
  border: 1px solid var(--line); border-radius: 999px;
  font-size: 14px; font-weight: 600; text-decoration: none; color: var(--navy);
  background: var(--card);
}
.projtabs .projtab.on {
  background: var(--navy); border-color: var(--navy); color: #fff;
}

.ad-head { background: var(--navy); color: #fff; padding: 18px 16px 16px; }
.ad-head-top {
  display: flex; flex-wrap: wrap; gap: 12px;
  justify-content: space-between; align-items: flex-end;
}
.ad-eyebrow {
  font-size: 12px; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 600; color: #c6d7ea;
}
.dash h2.ad-title {
  margin: 3px 0 0; font-size: 26px; line-height: 1.1; color: #fff;
}
.ad-sub { font-size: 14px; color: #c6d7ea; margin-top: 4px; }
.ad-big { text-align: left; }
.ad-big b { display: block; font-size: 44px; line-height: 1; font-weight: 700; }
.ad-big span { display: block; font-size: 13px; color: #c6d7ea; margin-top: 3px; }
/* The header's own bar is on navy, so it borrows the tint for the track and
   white for the fill — the page-level .bar is navy-on-tint. */
.ad-head .bar { background: rgba(255, 255, 255, .18); border-color: transparent; }
.ad-head .bar > span { background: #fff; }
.ad-stats { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.ad-stat {
  flex: 1 1 130px; padding: 10px 12px; border-radius: 10px;
  background: rgba(255, 255, 255, .10);
  border: 1px solid rgba(255, 255, 255, .16);
}
.ad-stat b { display: block; font-size: 22px; font-weight: 700; }
.ad-stat .bi-1, .ad-stat .bi-2 { font-size: 12px; color: #c6d7ea; }

.ad-main { display: flex; flex-wrap: wrap; gap: 16px; padding: 14px; }
.ad-left { flex: 1 1 320px; min-width: 0; }
.ad-lh {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 10px; margin-bottom: 8px;
}
.ad-lh h3 {
  margin: 0; font-size: 13px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--muted); font-weight: 700;
}
.ad-lh span { font-size: 13px; color: var(--muted); }

ul.ad-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 10px;
}
/* The whole card is the link, so the tap target is the card and not a word
   inside it — gloves, outdoors, same rule as the crew sheet. */
a.rc {
  display: flex; flex-direction: column; gap: 6px;
  padding: 12px; min-height: var(--tap);
  background: var(--card); border: 2px solid var(--hair); border-radius: 12px;
  text-decoration: none; color: inherit;
}
a.rc.sel { border-color: var(--navy); }
.rc-top { display: flex; justify-content: space-between; gap: 8px;
          align-items: flex-start; }
.rc-n { font-size: 16px; font-weight: 700; color: var(--navy); }
.rc-n .bi-2 { font-weight: 400; }
a.rc .bar { margin: 2px 0; }
.rc-cnt { display: flex; justify-content: space-between; gap: 8px;
          font-size: 13px; color: var(--muted); }
.rc-cnt b { color: var(--ink); }
.rc-ph { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
.rc-ph .frame {
  display: block; aspect-ratio: 4 / 3; border-radius: 6px; overflow: hidden;
  border: 1px dashed var(--line); background: var(--bg);
}
.rc-ph .frame img { width: 100%; height: 100%; object-fit: cover;
                    display: block; border: 0; }
.rc-phc { font-size: 12px; color: var(--muted); }

.pill {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 700; white-space: nowrap;
}
.pill.ok { background: var(--done-bg); color: var(--done); }
.pill.go { background: var(--blue-bg); color: var(--blue-dk); }
.pill.idle { background: #ececea; color: var(--muted); }

.cr { margin-top: 18px; }
ul.cr-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 10px;
}
.cr-card {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
}
.cr-av {
  flex: 0 0 40px; width: 40px; height: 40px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--navy); color: #fff; font-weight: 700; font-size: 17px;
}
.cr-n { display: block; font-size: 16px; font-weight: 700; color: var(--navy); }
.cr-r { display: block; font-size: 12px; color: var(--muted); }

.ad-detail {
  flex: 1 1 300px; min-width: 0; align-self: flex-start;
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  overflow: hidden;
}
.dp-h { padding: 14px; border-bottom: 1px solid var(--hair); }
.dp-top { display: flex; justify-content: space-between; gap: 10px;
          align-items: flex-start; }
.dp-eyebrow {
  font-size: 12px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--muted); font-weight: 700;
}
.dp-title { font-size: 21px; font-weight: 700; color: var(--navy);
            margin-top: 2px; }
.dp-title .bi-2 { font-size: 14px; font-weight: 400; }
.dp-where { font-size: 13px; color: var(--muted); text-align: right; }
/* The one tap target in the detail pane's header: the link to the room's own
   sheet. Found by looking (tools/visual_admin.py, WP10 A4) at 390px, where it
   measured 34px tall — it only ever cleared 44px when the project code and
   the slug happened to wrap onto two lines, which is luck and not a layout.
   Embedded there is no link here at all (a crew sheet is not a page the
   Mission Control frame may open), so this styles the anchor, not the box. */
.dp-where a {
  display: inline-flex; align-items: center; justify-content: flex-end;
  min-height: 44px; color: var(--blue-dk);
}
.dp-prog { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.dp-prog .bar { flex: 1 1 auto; margin: 0; }
.dp-prog b { font-size: 19px; color: var(--navy); }
.dp-cnt, .dp-claim { margin: 8px 0 0; font-size: 13px; color: var(--muted); }
.dp-b, .dp-ph { padding: 4px 14px 14px; }
h3.dp-sec {
  margin: 12px 0 4px; font-size: 12px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted); font-weight: 700;
}
ul.dp-items { list-style: none; margin: 0; padding: 0; }
ul.dp-items > li.it {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 9px 0; border-bottom: 1px solid var(--hair);
}
ul.dp-items > li.it:last-child { border-bottom: 0; }
.it-c {
  flex: 0 0 22px; width: 22px; height: 22px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  border: 2px solid var(--line); background: var(--card);
  font-size: 13px; font-weight: 700;
}
li.it.done .it-c { background: var(--done-bg); border-color: var(--done);
                   color: var(--done); }
.it-t { flex: 1 1 auto; min-width: 0; font-size: 15px; }
.it-t .bi-2 { font-size: 13px; }
ul.dp-phg {
  list-style: none; margin: 8px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
}
ul.dp-phg > li { position: relative; }
ul.dp-phg img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block;
  border-radius: 8px; border: 1px solid var(--hair);
}
ul.dp-phg > li.empty { grid-column: 1 / -1; }

@media (min-width: 900px) {
  .ad-detail { flex: 0 1 340px; }
  /* A card grid is not prose, so the dashboard gets more than the 720px
     reading column — progressively. A browser without :has() keeps 720px,
     where the grid is narrower and the detail pane stacks under it, and
     nothing is lost but width. */
  main:has(> section.dash) { max-width: 1160px; }
}

/* ---------- checklist ---------- */

ul.items { list-style: none; margin: 0; padding: 0; }
.item {
  border-top: 1px solid var(--hair);
  padding: 12px 0;
}
.item:first-child { border-top: 0; }
.item.is-done { background: var(--done-bg); border-radius: 8px; padding: 12px 10px; }
.item.is-done .text { color: var(--done); }

.check {
  display: flex; align-items: flex-start; gap: 12px;
  min-height: var(--tap); cursor: pointer;
}
.check input[type=checkbox] {
  flex: 0 0 auto; width: 28px; height: 28px; margin: 2px 0 0;
  accent-color: var(--done);
}
.check .text { flex: 1; }

/* One line per item: the tick box and the text take the width they need, the
   percent sits at the right-hand end of the same row. Keeping the percent
   under the text cost ~60px per item, and a nine-item room is a long scroll
   on a phone held in one hand (ADDENDUM finding F3). */
.item-top { display: flex; align-items: flex-start; gap: 10px; }
.item-top .check { flex: 1 1 auto; min-width: 0; }
.item-top select {
  flex: 0 0 auto; width: auto; min-width: 92px; margin-top: 0;
}
.item-tags { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
             margin-top: 6px; }
.item-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
             margin-top: 8px; }
.item-actions { margin-top: 6px; }
/* The read-only percent, and the print copy of the dropdown's value. */
.pct-tag { flex: 0 0 auto; margin-top: 10px; }

.tag {
  display: inline-block; padding: 3px 9px; border-radius: 999px;
  background: var(--bg); border: 1px solid var(--line);
  font-size: 13px; color: var(--muted);
}
.tag.claimed { background: var(--blue-bg); border-color: #b9cdea;
               color: var(--navy); }

/* ---------- forms ---------- */

label { display: block; margin: 10px 0 0; font-size: 15px; }
/* Every text-ish input, not just type=text: the link box is type=url so the
   phone keyboard carries a slash, and it was rendering at 21px tall before it
   was named here. */
input[type=text], input[type=url], input[type=search], input[type=tel],
input[type=number], textarea, select {
  width: 100%; margin-top: 4px; padding: 11px 12px;
  min-height: var(--tap);
  font: inherit; color: inherit;
  background: var(--card);
  border: 1px solid var(--line); border-radius: 8px;
}
textarea { min-height: 84px; resize: vertical; }
.item-meta select { width: auto; min-width: 96px; min-height: 44px; padding: 8px 8px; }

button {
  margin-top: 12px; padding: 12px 18px; min-height: var(--tap);
  font: inherit; font-weight: 600;
  color: #fff; background: var(--navy);
  border: 0; border-radius: 8px; cursor: pointer;
}
button.link {
  margin: 0; padding: 6px 0; min-height: 32px;
  color: var(--blue-dk); background: none; font-weight: 500;
  text-decoration: underline;
}

/* Checkboxes and percent dropdowns submit themselves when JS is available, so
   the per-item Save button is only there for the no-JS case. app.js hides it. */
.nojs-save { margin-top: 0; padding: 8px 14px; min-height: 36px; font-size: 14px; }

.hidden { display: none; }

/* Sign in. One column, big targets, nothing else on the page: it is the
   first thing a worker sees after scanning a code with one hand. */
.signin { max-width: 26rem; }
.signin label { display: block; margin-top: 14px; font-weight: 600; }
.signin input {
  display: block; width: 100%; margin-top: 6px;
  min-height: var(--tap); padding: 10px 12px; font-size: 17px;
  border: 1px solid var(--rule); border-radius: 8px;
}
.signin small { display: block; margin-top: 4px;
                font-weight: 400; color: #5a6672; font-size: 13px; }
.signin .wide { width: 100%; margin-top: 18px; }
.flash-bad {
  background: #fdeaea; border-left: 4px solid #a3281f; color: #6d1b15;
  padding: 10px 12px; border-radius: 6px;
}

/* Who you are, in the brand bar, next to the language toggle.

   It takes a whole row of the bar rather than a share of one. An ADMIN on a
   390px phone has five things up there — logo, Oficina, English, their name
   and Salir · Sign out — and in a single non-wrapping row flexbox solved it
   by squeezing the name to "A…" and breaking the sign-out button across three
   lines, which is how it looked until this was found by looking
   (tools/visual_admin.py, WP10 A4). Nothing MEASURED it, because nothing
   overflowed: the row shrank instead. `nowrap` on the button is what makes
   the bar wrap at all — a button whose own text can break has a min width of
   one word, so the row never runs out of space to give. */
.whoami { display: flex; align-items: center; gap: 8px; margin: 0 0 0 auto; }
.whoami .signout { white-space: nowrap; }
.whoami-name {
  color: #c6d7ea; font-size: 13px; font-weight: 600;
  max-width: 24vw; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.whoami .signout {
  min-height: var(--tap); padding: 0 10px; margin: 0;
  background: transparent; color: #fff; font-size: 13px; font-weight: 600;
  border: 1px solid rgba(255, 255, 255, .55); border-radius: 8px;
}

/* The enrolment controls on /admin/crew: two small boxes and a button per
   row, stacked so the table column stays narrow on a phone. */
.enrol { display: flex; flex-direction: column; gap: 4px; }
.enrol input { min-height: 36px; padding: 4px 8px; font-size: 14px; }

details { margin-top: 10px; }
summary {
  min-height: var(--tap); display: flex; align-items: center;
  color: var(--blue-dk); font-weight: 600; cursor: pointer;
}

/* ---------- disclosures that look like buttons ----------
   "Agregar una nota", "Pedir material" and the per-item "Opciones" used to
   render as plain bold text, which nobody reads as something to tap. They are
   now full-width 48px buttons with a chevron that flips when open. */

details.disclosure { margin-top: 12px; }
details.disclosure > summary {
  justify-content: space-between; gap: 10px;
  min-height: var(--tap); padding: 12px 14px;
  background: var(--blue-bg); color: var(--navy);
  border: 1px solid #b9cdea; border-radius: 8px;
  font-weight: 700; font-size: 16px;
  list-style: none;                 /* Firefox's default triangle */
}
details.disclosure > summary::-webkit-details-marker { display: none; }
details.disclosure > summary::after {
  content: "\25BE"; font-size: 15px; line-height: 1;
}
details.disclosure[open] > summary::after { content: "\25B4"; }
details.disclosure[open] > summary { border-radius: 8px 8px 0 0; }
details.disclosure > :not(summary) {
  border: 1px solid #b9cdea; border-top: 0; border-radius: 0 0 8px 8px;
  padding: 2px 12px 14px;
}
/* The per-item disclosure sits inside a list row, one per line, so it is
   deliberately quieter than a section-level one: an outline chip rather than a
   filled bar. Four filled blue bars down a punch list turn the items
   themselves into the background. */
.item details.disclosure { margin-top: 8px; }
.item details.disclosure > summary {
  display: inline-flex; width: auto; gap: 8px;
  font-size: 14px; font-weight: 600; padding: 0 14px; min-height: 44px;
  background: var(--card); color: var(--blue-dk);
  border: 1px solid var(--line); border-radius: 999px;
}
.item details.disclosure[open] > summary { border-radius: 999px; }
.item details.disclosure > :not(summary) {
  border: 0; border-left: 3px solid #b9cdea; border-radius: 0;
  margin-top: 8px; padding: 2px 0 6px 11px;
}

/* ---------- photo upload ----------
   The file input is the whole button: it fills the label, so a tap anywhere on
   the navy block opens the camera (or the gallery, for the second one). The
   input itself is transparent rather than display:none so it stays keyboard
   reachable, and :focus-within draws the ring. */

.filebtn {
  position: relative; display: flex; align-items: center; justify-content: center;
  gap: 8px; margin-top: 10px; padding: 12px 14px; min-height: 52px;
  text-align: center; font-weight: 700; font-size: 16px;
  color: #fff; background: var(--navy);
  border-radius: 8px; cursor: pointer;
}
.filebtn input[type=file] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer;
}
.filebtn:focus-within { outline: 3px solid var(--blue-dk); outline-offset: 2px; }
.cap { font-size: 14px; }

/* Upload state: "Enviando… · Uploading…" then "Listo · Done". */
.upstate { margin: 10px 0 0; font-weight: 600; font-size: 15px; min-height: 0; }
.upstate.busy { color: var(--warn); }
.upstate.ok { color: var(--done); }
.upstate.bad { color: #b91c1c; }

ul.photos, ul.upshots, ul.links { list-style: none; margin: 10px 0 0; padding: 0; }
ul.photos, ul.upshots { display: flex; flex-wrap: wrap; gap: 10px; }
ul.photos > li, ul.upshots > li {
  flex: 0 0 auto; width: 104px;
  display: flex; flex-direction: column; gap: 2px;
}
ul.photos img, ul.upshots img {
  display: block; width: 104px; height: 104px; object-fit: cover;
  border: 1px solid var(--line); border-radius: 8px; background: var(--card);
}
ul.photos.compact > li, ul.upshots > li { width: 76px; }
ul.photos.compact img, ul.upshots img { width: 76px; height: 76px; }
.thumb-cap { font-size: 13px; line-height: 1.25; }
ul.photos .byline { font-size: 12px; }

/* ---------- material links ---------- */

ul.links > li {
  border-top: 1px solid var(--hair); padding: 10px 0;
  display: flex; flex-direction: column; gap: 2px;
}
ul.links > li:first-child { border-top: 0; }
ul.links a {
  display: flex; align-items: center; min-height: 44px;
  color: var(--blue-dk); font-weight: 600; word-break: break-word;
}
.dom { color: var(--muted); font-size: 13px; }
/* Paper-only copies of what a control shows on screen (ADDENDUM finding F5):
   the link's URL, and the item's percentage once the dropdown is gone. */
.print-url, .print-pct { display: none; }
ul.links.compact a { min-height: 44px; font-size: 15px; }

.claimbox { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--hair); }
.claimbox p { margin: 0; }

/* ---------- feeds, rooms, admin ---------- */

ul.feed, ul.rooms { list-style: none; margin: 0; padding: 0; }
ul.feed > li { border-top: 1px solid var(--hair); padding: 10px 0; }
ul.feed > li:first-child { border-top: 0; }
ul.feed p { margin: 0 0 2px; }
.qty { color: var(--muted); }

ul.rooms > li {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; border-top: 1px solid var(--hair);
}
ul.rooms > li:first-child { border-top: 0; }
ul.rooms a {
  flex: 1; display: flex; align-items: center;
  min-height: var(--tap); color: var(--blue-dk);
  text-decoration: none; font-weight: 600;
}

.status {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 13px; background: var(--bg); border: 1px solid var(--line);
}
.s-requested { background: var(--blue-bg); border-color: #b9cdea;
               color: var(--navy); }
.s-approved, .s-delivered { background: var(--done-bg); border-color: #bbf7d0;
                            color: var(--done); }
.s-ordered { background: #fef3c7; border-color: #fde68a; color: var(--warn); }
.s-rejected, .s-cancelled { background: #fef2f2; border-color: #fecaca; color: #b91c1c; }
.ok { color: var(--done); font-weight: 600; }

/* ---------- wide tables on a narrow phone ----------
   ADDENDUM finding F1: the admin tables are wider than a 390px viewport, so
   the whole page scrolled sideways and the navy table head bled out past the
   card. The scroller is the table's own, the card stays put, and the right
   edge is a visible overflow instead of a cut-off column. */

.tablewrap {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  max-width: 100%; border-radius: 8px;
}
.tablewrap table.admin { min-width: 100%; width: max-content; }

table.admin { width: 100%; border-collapse: collapse; font-size: 14px;
              border: 1px solid var(--navy); }
table.admin th, table.admin td {
  text-align: left; vertical-align: top;
  padding: 8px 6px; border-bottom: 1px solid var(--hair);
}
table.admin thead th {
  background: var(--navy); color: #fff; font-weight: 600;
  font-size: 12px; letter-spacing: .05em; text-transform: uppercase;
}
table.admin button { margin-top: 6px; padding: 10px 12px; min-height: 44px; }
table.admin select { min-height: 44px; padding: 8px 8px; }
table.admin td.whatcell { max-width: 260px; }
table.admin td.whatcell > * { display: block; }
/* A link in this cell is a row of its own, so it gets a real tap target. */
table.admin td.whatcell a {
  display: flex; align-items: center; min-height: var(--tap);
  word-break: break-word;
}
img.adminthumb {
  width: 84px; height: 84px; object-fit: cover; margin-bottom: 4px;
  border: 1px solid var(--line); border-radius: 6px;
}

ul.tabs { list-style: none; display: flex; flex-wrap: wrap; gap: 8px;
          margin: 0 0 12px; padding: 0; }
ul.tabs a {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px;
  border: 1px solid var(--line); border-radius: 999px;
  color: var(--blue-dk); text-decoration: none; font-weight: 600;
  font-size: 14px;
}
ul.tabs a.on { background: var(--navy); border-color: var(--navy); color: #fff; }

.flash {
  margin: 0 0 14px; padding: 12px 14px; border-radius: 8px;
  background: #fffbeb; border: 1px solid #fde68a; color: #78350f;
}
.adminlink { text-align: center; }
.adminlink a {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--tap); padding: 0 16px; color: var(--muted);
}

figure { margin: 10px 0; }
figure img { width: 100%; height: auto; border: 1px solid var(--line);
             border-radius: 8px; background: #fff; }
figcaption { color: var(--muted); font-size: 14px; margin-top: 4px; }

@media (prefers-color-scheme: dark) {
  /* Night work happens. The brand bar is already navy, so it stays put; the
     rest darkens around it. */
  :root {
    --ink: #e8eaed; --muted: #a3adbb; --line: #3a4455; --hair: #2b3340;
    --bg: #11161d; --card: #1a212b;
    --blue-dk: #8fb4ec; --blue-bg: #1d2c42;
    --done: #4ade80; --done-bg: #14301f;
  }
  section h2, footer.brand-ft .ent { color: #cddcf0; }
  button { background: var(--navy); }
  table.admin { border-color: #3a4455; }
}

/* ---------- the property, at the top of every room sheet ----------
   DARRELL 2026-10-28 2:49 PM PT, requirements 1 and 2. This used to be one
   grey crumb line. A sheet taped to a wall in one of several houses has to
   answer "which house is this" before it answers anything else. */

.roomhead .property {
  margin: 0;
  font-size: 19px; font-weight: 700; line-height: 1.2;
  color: var(--navy);
}
.roomhead .addr {
  margin: 1px 0 8px;
  font-size: 15px; color: var(--muted);
}

/* ---------- where am I ----------
   The house plan with this room outlined (requirement 3), plus the symbol key
   (requirement 4). Short on purpose: it answers a half-second question, and
   an answer that needs scrolling is not one. */

.wherebox { margin: 10px 0 12px; }

figure.keyplan, figure.plan { margin: 0 0 12px; }
figure.keyplan img, figure.plan img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--line); border-radius: 4px;
  background: #fff;
}
figure.keyplan figcaption, figure.plan figcaption {
  margin-top: 4px; font-size: 14px; color: var(--muted);
}

/* The <img> and the highlight have to scale together, so the overlay is
   absolutely positioned over the image inside a wrapper that is exactly the
   image's size. */
.kp-wrap { position: relative; display: block; }
.kp-hl {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
}
.kp-hl rect {
  fill: rgba(230, 60, 40, .22);
  stroke: #c81e14; stroke-width: 3px;
}
a.plan-link {
  display: block; text-decoration: none;
  border-radius: 4px;
}
a.plan-link:focus-visible { outline: 3px solid var(--blue-dk); }

/* ---------- symbol key ----------
   The same key as the printed plan sheets (app/plans.py holds the geometry).
   Two columns on a phone, so four symbols are two short rows rather than four
   long ones. */

.legend { margin: 8px 0 0; }
.legend h3 {
  margin: 0 0 4px;
  font-size: 13px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--blue-dk);
}
.legend ul {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: 1fr 1fr; gap: 2px 10px;
}
.legend li {
  display: flex; align-items: center; gap: 7px;
  font-size: 14px; min-height: 26px;
}
.legend svg { flex: none; }
/* Plan-sheet colours, one per symbol, carried by currentColor. */
.legend .sym-light, .legend .sym-fan { color: #1b5aa8; }
.legend .sym-switch { color: #6b3fa0; }
.legend .sym-outlet { color: #0e7a5a; }
.legend li span { color: var(--ink); }

/* ---------- plans, at the bottom of the sheet ----------
   Requirements 5-8, in Darrell's order. */

section.plans h3 {
  margin: 14px 0 4px;
  font-size: 15px; color: var(--navy);
}
.btn-wide {
  display: flex; align-items: center; justify-content: center;
  min-height: var(--tap); margin: 0 0 14px;
  padding: 10px 14px;
  border: 1px solid var(--blue-dk); border-radius: 8px;
  background: var(--blue-bg); color: var(--blue-dk);
  font-size: 16px; font-weight: 600; text-decoration: none;
}
/* "Enlarged" has to actually be bigger, or the page is a round trip for
   nothing. On a phone the plan on the sheet is already the width of the card,
   so the win here is breaking out of the card's and the page's padding
   (14px each side of each, so 56px of a 390px screen) and giving the image
   the height of the screen instead of a slot between two other sections.
   Pinch-zoom then does the rest, and the hint says so. */
.planpage figure.plan.big {
  margin: 0 -14px;
}
.planpage figure.plan.big img {
  border-radius: 0; border-left: 0; border-right: 0;
  max-height: 78vh; width: 100%; object-fit: contain;
  background: #fff;
}
.planpage figure.plan.big figcaption { padding: 0 14px; }
.planpage .keyplan .kp-wrap { margin: 0 -14px; }
.planpage .keyplan img { border-radius: 0; border-left: 0; border-right: 0; }
.planpage h2 { margin-top: 2px; }
.planpage .zoomhint { margin: 6px 0 12px; }

/* ---------- the plan set ----------
   WP8/A2, DARRELL 2026-10-29 10:38 PM PT. The page the ONE code on the cover
   sheet opens: the plan of the house, then every room's plans. Each room gets
   a rule above its name so a thumb scrolling a ten-room set can tell where
   one room's plans end and the next begins — that is the only thing this page
   needs that a room sheet does not already have. */
.planset .set-room {
  border-top: 1px solid var(--line); margin: 18px 0 0; padding: 12px 0 0;
}
.planset .set-room-name { margin: 0 0 2px; }
.planset .set-rooms-head { margin-top: 20px; }
.planset .set-room .adminlink { margin: 0 0 8px; }

/* ---------- what a photo shows ----------
   DARRELL 2026-10-29 5:18 AM PT. A segmented control above the camera
   buttons; every option is a full tap target. */

fieldset.tagset {
  margin: 0 0 8px; padding: 0; border: 0;
}
fieldset.tagset legend {
  padding: 0; margin-bottom: 4px;
  font-size: 14px; color: var(--muted);
}
.tagopt {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: var(--tap); margin: 0 6px 4px 0; padding: 4px 12px 4px 8px;
  border: 1px solid var(--line); border-radius: 999px;
  background: #fff; font-size: 15px;
}
.tagopt input { width: 20px; height: 20px; accent-color: var(--blue-dk); }
.tagopt:has(input:checked) {
  border-color: var(--blue-dk); background: var(--blue-bg);
  font-weight: 600;
}
.tagopt.tag-done:has(input:checked) {
  border-color: var(--done); background: var(--done-bg); color: var(--done);
}
.tagopt.tag-problem:has(input:checked) {
  border-color: #b3261e; background: #fdecea; color: #7a1510;
}

.tagbadge {
  display: block; margin-top: 3px; padding: 2px 8px;
  border-radius: 8px; font-size: 12px; line-height: 1.2;
  text-align: center;
}
.tagbadge .es { display: block; font-weight: 700; }
.tagbadge .en { display: block; font-weight: 400; opacity: .8; }
.tagbadge.tag-done { background: var(--done-bg); color: var(--done); }
.tagbadge.tag-problem { background: #fdecea; color: #7a1510; }

/* A tick box with a sentence next to it — the "request this material" box on
   the link form. */
.checkrow {
  display: flex; align-items: flex-start; gap: 10px;
  min-height: var(--tap); padding: 6px 0;
}
.checkrow input { width: 24px; height: 24px; margin-top: 2px; flex: none;
                  accent-color: var(--blue-dk); }
.checkrow small { display: block; color: var(--muted); font-size: 13px; }

/* "From a link: example.com" on a material request. It is a real link to a
   real product page, so it is a real tap target — 27px of chip inside a
   paragraph was something to aim at with a gloved finger and miss. */
.tag.fromlink {
  display: inline-flex; align-items: center;
  min-height: var(--tap); margin-top: 4px; padding: 4px 12px;
  text-decoration: none; white-space: nowrap;
}

@media (min-width: 620px) {
  .legend ul { grid-template-columns: repeat(4, 1fr); }
}

@media print {
  /* A printed sheet is the paper punch list, and it should look like the other
     HSH documents: the brand bar and the entity footer stay, every control
     goes, and there is deliberately no pay, rate or dollar wording anywhere
     (HANDOFF section 5).

     Printing goes through a black-and-white office printer and gets
     photocopied on site, so the bar switches to the ONE-COLOUR mark plus the
     wordmark as live text on white (ADDENDUM 10:31 PT answer 9). A navy fill
     either eats toner or arrives as a grey smear; a single-ink rule reads at
     any quality.

     ADDENDUM finding F5: an explicit page margin, every interactive control
     gone, attached links printed as text, photos not printed at all. */

  @page { margin: 0.5in; }

  header.brand, footer.brand-ft { position: static; }
  header.brand {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
    background: #fff; color: #000;
    border-bottom: 2px solid #000; border-radius: 0;
    padding: 0 0 6px; margin-bottom: 10px;
  }
  header.brand .logo-screen { display: none !important; }
  header.brand .logo-print { display: block; width: 34px; height: 34px; }
  .brand-id .wordmark {
    display: block; color: #000; font-size: 15px; font-weight: 700;
    letter-spacing: .055em; text-transform: uppercase;
  }
  .brand-id .brand-sub { color: #333; }
  footer.brand-ft .ent { color: #000; }
  /* Anything a finger or a mouse would have used is not on the paper. The one
     exception is the item checkbox: on paper that IS the punch sheet's tick
     box, and a crew lead with a pen is the oldest interface we support. */
  button, details, .claimbox, .whoami, .signin, .enrol,
  .item-actions, .adminlink, ul.tabs, nav.brandnav, .filebtn, .cap, .upstate,
  select, textarea, input[type=text], input[type=url], input[type=file],
  input[type=tel], input[type=password],
  header.brand .lang { display: none !important; }
  /* The forms go whole, not control by control: hiding only the inputs left
     their labels ("Pegar un enlace · Paste a link") printed on the paper with
     nothing underneath them. */
  .photo-form, .link-form, .claimform { display: none !important; }

  /* Photos are not printed (half-tones on an office printer are a toner bill,
     not information) but the links ARE, as plain text under their label, so a
     printed sheet still records where the materials came from. The photo
     section goes with them: a heading over an empty space is worse than no
     heading. */
  ul.photos, ul.upshots, img.adminthumb,
  section.photos-section { display: none !important; }

  /* The ring is a screen thing: on an office laser it is a grey doughnut and
     the number inside it comes out small. The bar beside it prints, and the
     "60% completado — 3/5 hecho" line under it says the same thing in words.
     The crew chips DO print: a sheet on a wall should say whose job it is. */
  svg.ring { display: none !important; }
  .progress { display: block; }

  /* The dashboard is an office screen, but /admin does get printed (somebody
     takes the room list into a walk-through), so it prints in one ink rather
     than not at all: the navy header goes white with a rule under it, the
     project switcher and the photo frames go — a frame is either a half-tone
     or an empty dashed box — and the card grid stays a grid. */
  .ad-head {
    background: #fff !important; color: #000 !important;
    border-bottom: 2px solid #000; padding: 0 0 8px;
  }
  .dash h2.ad-title, .ad-eyebrow, .ad-sub, .ad-big b, .ad-big span,
  .ad-stat b, .ad-stat .bi-1, .ad-stat .bi-2 { color: #000 !important; }
  .ad-stat { border: 1px solid #000 !important; }
  .projtabs, .rc-ph, .rc-phc, ul.dp-phg, .cr-av { display: none !important; }
  a.rc, .cr-card, .ad-detail { border: 1px solid #000 !important; }
  .ad-main { padding: 10px 0 0; }
  a.rc { break-inside: avoid; }
  ul.chips > li.chip.is-claim {
    background: #fff !important; color: #000 !important;
    border: 2px solid #000 !important;
  }

  /* ONE INK. Everything navy, blue or green on screen prints black on white:
     a punch sheet goes through a B&W office printer and then a site
     photocopier, where a tint is a grey smear and a green "done" row is just
     a darker grey than the rest. */
  body, main, section, h1, h2, h3, p, li, span, strong, em, a, td, th,
  section h2, .pct, .crumb, .hint, .byline, .tag, .status, .qty, .empty,
  .code, .thumb-cap, .item.is-done .text, .item.is-done,
  footer.brand-ft .ent, .ok, .muted {
    color: #000 !important;
    background: transparent !important;
  }
  .tag, .status { border: 1px solid #000 !important; }
  .check .box, .check input[type=checkbox] {
    border-color: #000; accent-color: #000;
  }
  /* The progress bar is the one deliberate fill, and it is black. */
  .bar { border: 1px solid #000; background: #fff; }
  /* !important, because the one-ink rule above blanks every span background
     and the bar's fill is the one that has to survive it. */
  .bar > span {
    background: #000 !important;
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  ul.links { margin: 6px 0 0; }
  ul.links > li { border-top: 1px solid #000; padding: 6px 0; }
  ul.links a { min-height: 0; color: #000; font-weight: 700;
               text-decoration: none; }
  .print-url { display: block; font-size: 11px; word-break: break-all; }
  .print-pct { display: inline-block; }
  .dom { display: none; }
  body { background: #fff; }
  main { padding: 0; max-width: none; }
  section { border: 0; padding: 0; margin-bottom: 18px; }
  section.closed-banner { border: 2px solid #000; padding: 8px 10px; }
  section.closed-banner h2 { color: #000; }
  .item, ul.feed > li { break-inside: avoid; }

  /* ---------- plans on paper ----------
     Plans DO print, unlike photos. They are line drawings: they survive a
     B&W printer and a site photocopier, and the whole point of the bottom
     section (DARRELL 2026-10-28 2:49 PM PT) is that a crew member standing in
     the room can see the room plan, the electrical and where the room is in
     the house. A printed punch sheet without them is the thing this change
     set out to fix.

     The property name and address are the first two lines on the page, for
     the same reason they are the first two on the screen. */
  .roomhead .property { font-size: 16px; font-weight: 700; }
  .roomhead .addr { font-size: 13px; }

  figure.keyplan img, figure.plan img {
    border: 1px solid #000;
    /* Hold the plan to something that leaves room for the list, and never let
       one split across two sheets of paper. */
    max-height: 3.4in; width: auto; max-width: 100%;
  }
  figure.keyplan, figure.plan { break-inside: avoid; }
  .planpage figure.plan.big img { max-height: 8in; }

  /* The highlight is the one coloured thing worth keeping on paper, and red
     does not survive a B&W printer: a heavy black outline and no fill, so the
     room underneath it stays readable. */
  .kp-hl rect {
    fill: none !important;
    stroke: #000 !important; stroke-width: 4px !important;
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }

  /* The symbol key prints in one ink, like the plans it explains. */
  .legend .sym-light, .legend .sym-fan,
  .legend .sym-switch, .legend .sym-outlet { color: #000 !important; }
  .legend h3 { color: #000 !important; }

  /* Anchors that are really buttons are controls, and controls are not on the
     paper — "See the plan enlarged" is not an instruction you can follow with
     a printout in your hand. */
  .btn-wide, fieldset.tagset, .checkrow,
  .tap-hint, .zoomhint { display: none !important; }
  .tagbadge { border: 1px solid #000 !important; }
}
