/* Walkthrough web console, redesign 2026-08-11: the APP's design
   system (apps/mobile/src/lib/theme.ts), not the legal-page palette.
   Blueprint cobalt on indigo-cast slate neutrals; Geist for UI,
   Geist Mono for counts and dates, Instrument Serif for display.
   The variable NAMES are historical (slate/walnut) so every older
   rule re-skins itself; the VALUES are the app tokens. Field-first:
   big tap targets, high contrast, nothing precious. */

@font-face {
  font-family: "Geist";
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url("Geist_400Regular.ttf") format("truetype");
}
@font-face {
  font-family: "Geist";
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  src: url("Geist_500Medium.ttf") format("truetype");
}
@font-face {
  font-family: "Geist";
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  src: url("Geist_600SemiBold.ttf") format("truetype");
}
@font-face {
  font-family: "Geist Mono";
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url("GeistMono_400Regular.ttf") format("truetype");
}
@font-face {
  font-family: "Instrument Serif";
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url("InstrumentSerif_400Regular.ttf") format("truetype");
}

:root {
  /* App tokens under the historical names. */
  --slate: #155da3; /* brand cobalt, darker step (blueprint-700) */
  --slate-dark: #114a82; /* blueprint-800, for hover and press */
  --walnut: #272260; /* heading ink (slate-700, the logo ink) */
  --ink: #120f30; /* slate-900 */
  --muted: #565380; /* slate-500 */
  --bg: #f3f3f8; /* slate-50 shell */
  --card: #fff;
  --rule: #d2d0e1; /* slate-200 */
  --danger: #b23b2e;
  --done: #1f7a55;
  --focus: #1c71c1;
  /* New in the redesign. */
  --rule-subtle: #e7e6f0; /* slate-100 */
  --line-strong: #aca9c6; /* slate-300, the app's lineStrong: form controls */
  --faint: #7e7ba2; /* slate-400 */
  --brand-subtle: #eaf3fb; /* blueprint-50 */
  --brand-border: #a8ccee; /* blueprint-200 */
  --shadow-sm: 0 1px 2px rgba(18, 15, 48, 0.06);
  --shadow-md: 0 4px 12px rgba(18, 15, 48, 0.1);
  --serif: "Instrument Serif", Georgia, serif;
  --mono: "Geist Mono", ui-monospace, monospace;

  /* Type scale. Every font-size in this file resolves to one of
     these; half-pixel steps (13 vs 13.5 vs 14 vs 14.5) made the page
     look assembled rather than designed (design review 2026-08-11).
     Steps track the app's own scale in apps/mobile/src/lib/theme.ts
     where the desk UI can afford the same size. */
  --fs-2xs: 11px;   /* eyebrows, micro labels */
  --fs-xs: 12px;    /* meta lines, counts, chips */
  --fs-sm: 13px;    /* dense controls: doors, quiet buttons */
  --fs-base: 14px;  /* default UI text */
  --fs-md: 15px;    /* body copy, item rows, primary buttons */
  --fs-body: 16px;  /* the scale's base step */
  /* Text fields alias the base step. It must never resolve below
     16px: mobile browsers zoom the page on focus if it does. */
  --fs-input: var(--fs-body);
  --fs-lg: 18px;    /* section titles */
  --fs-xl: 24px;    /* card headings, sign-in code */
  --fs-2xl: 30px;   /* reserved */
  --fs-display: 38px; /* page titles */
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
}

body {
  font-family: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--ink);
  background: var(--bg);
  line-height: 1.5;
  min-height: 100vh;
}

button { font: inherit; }
input { font: inherit; }

/* Layout ------------------------------------------------------------- */

.topbar {
  background: var(--card);
  border-bottom: 2px solid var(--slate);
  padding: 0 24px;
  display: flex;
  align-items: center;
  gap: 28px;
  height: 60px;
}

.topbar .brand {
  font-size: var(--fs-xs);
  letter-spacing: 0.15em;
  color: var(--slate);
  font-weight: 600;
  text-transform: uppercase;
}

.container {
  max-width: 860px;
  margin: 0 auto;
  padding: 32px 24px 80px;
}

.subtitle {
  color: var(--muted);
  font-size: var(--fs-base);
  margin: 0 0 24px;
}

/* Buttons ------------------------------------------------------------ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: var(--slate);
  color: #fff;
  border: 1px solid var(--slate);
  border-radius: 8px;
  padding: 10px 18px;
  font-size: var(--fs-md);
  font-weight: 600;
  cursor: pointer;
}

.btn:hover { background: var(--slate-dark); }

.btn:disabled {
  opacity: 0.55;
  cursor: default;
}

.btn-secondary {
  background: var(--card);
  color: var(--slate-dark);
  border: 1px solid var(--rule);
}

.btn-secondary:hover { background: #f0f0ee; }

.btn-link {
  background: none;
  border: none;
  color: var(--slate-dark);
  cursor: pointer;
  padding: 4px;
  font-size: var(--fs-sm);
  text-decoration: underline;
}

.btn-quiet {
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  padding: 8px 10px;
  font-size: var(--fs-base);
  border-radius: 6px;
}

.btn-quiet:hover { background: #f0f0ee; color: var(--ink); }

.btn-quiet.danger:hover { color: var(--danger); }

/* Forms -------------------------------------------------------------- */

.field input {
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: var(--card);
  font-size: var(--fs-input);
}

.field input:focus {
  outline: 2px solid var(--focus);
  outline-offset: -1px;
  border-color: var(--focus);
}

.field label {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--walnut);
  margin-bottom: 6px;
}

/* Sign-in ------------------------------------------------------------ */

.signin-wrap {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.signin-card {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 12px;
  padding: 36px 32px;
  width: 100%;
  max-width: 400px;
}

.signin-card .brand {
  font-size: var(--fs-xs);
  letter-spacing: 0.15em;
  color: var(--slate);
  font-weight: 600;
  text-transform: uppercase;
  margin-bottom: 8px;
}

.signin-card h1 {
  font-size: var(--fs-xl);
  margin-bottom: 6px;
}

.signin-card p {
  color: var(--muted);
  font-size: var(--fs-base);
  margin: 0 0 20px;
}

.signin-card form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.signin-card .code-input {
  letter-spacing: 0.35em;
  text-align: center;
  font-size: var(--fs-xl);
  font-variant-numeric: tabular-nums;
}

.signin-footer {
  margin-top: 16px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

/* Messages ----------------------------------------------------------- */

.error-banner {
  background: #fdecea;
  border: 1px solid #f5c6c1;
  color: var(--danger);
  border-radius: 8px;
  padding: 10px 14px;
  font-size: var(--fs-base);
}

.info-banner {
  background: #eef4f8;
  border: 1px solid #cfe0ea;
  color: var(--slate-dark);
  border-radius: 8px;
  padding: 10px 14px;
  font-size: var(--fs-base);
}

.loading {
  color: var(--muted);
  font-size: var(--fs-base);
  padding: 32px 0;
  text-align: center;
}

.empty-state {
  background: var(--card);
  border: 1px dashed var(--rule);
  border-radius: 12px;
  padding: 40px 24px;
  text-align: center;
  color: var(--muted);
  font-size: var(--fs-md);
}

.toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--walnut);
  color: #fff;
  border-radius: 8px;
  padding: 10px 18px;
  font-size: var(--fs-base);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
  z-index: 100;
  max-width: 90vw;
}

/* The paywall toast's way out. The toast is walnut with white text, so
   the link has to carry its own contrast rather than inheriting the
   page's link color, which is a mid tone chosen against paper. */
.toast-link {
  color: #fff;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}

/* Project list ------------------------------------------------------- */

.project-grid {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.project-card {
  display: block;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 12px;
  padding: 18px 20px;
  text-decoration: none;
  color: inherit;
}

a.project-card:hover {
  border-color: var(--slate);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}

.project-card .name {
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--walnut);
}

.project-card .address {
  font-size: var(--fs-base);
  color: var(--muted);
  margin-top: 2px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.project-card .meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  margin-top: 10px;
  font-size: var(--fs-sm);
  color: var(--muted);
}

.project-card .meta .open-count {
  color: var(--slate-dark);
  font-weight: 600;
}

.project-card .card-actions {
  display: flex;
  gap: 8px;
  margin-top: 14px;
  align-items: center;
  flex-wrap: wrap;
}

.project-card .card-actions .btn {
  text-decoration: none;
}

/* One small-button size everywhere (polish item 21). */
.btn-sm {
  padding: 8px 14px;
  font-size: var(--fs-base);
}

/* Punch manager ------------------------------------------------------ */

.back-link {
  display: inline-block;
  color: var(--slate-dark);
  font-size: var(--fs-sm);
  text-decoration: none;
  margin-bottom: 12px;
}

.back-link:hover { text-decoration: underline; }

.punch-section {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 12px;
  padding: 18px 20px;
  margin-bottom: 20px;
}

.punch-section-head {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 12px;
}

.punch-section-title {
  font-size: var(--fs-body);
  font-weight: 700;
  color: var(--walnut);
}

.punch-section-sub {
  font-size: var(--fs-sm);
  color: var(--muted);
}

.section-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.section-actions .btn {
  padding: 8px 14px;
  font-size: var(--fs-base);
}

.punch-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.punch-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 4px;
  border-top: 1px solid var(--rule);
}

.punch-item.drag-source { opacity: 0.4; }

.punch-item.drag-over {
  box-shadow: inset 0 3px 0 var(--slate);
}

.punch-item .drag-handle {
  cursor: grab;
  color: var(--muted);
  user-select: none;
  padding: 6px 8px;
  font-size: var(--fs-body);
  letter-spacing: -2px;
}

.punch-check {
  display: flex;
  align-items: center;
  padding: 4px 6px;
  cursor: pointer;
}

.punch-check input {
  width: 20px;
  height: 20px;
  accent-color: var(--slate);
  cursor: pointer;
}

.punch-check input:disabled { cursor: default; }

.punch-body {
  flex: 1;
  min-width: 0;
}

.punch-text {
  font-size: var(--fs-md);
  overflow-wrap: break-word;
}

.punch-item.done .punch-text {
  text-decoration: line-through;
  color: var(--muted);
}

.punch-location {
  font-size: var(--fs-sm);
  color: var(--slate-dark);
  background: #eef4f8;
  border-radius: 999px;
  padding: 1px 10px;
  display: inline-block;
  margin-top: 3px;
}

.punch-actions {
  display: flex;
  gap: 2px;
  opacity: 0.6;
}

.punch-item:hover .punch-actions,
.punch-item:focus-within .punch-actions {
  opacity: 1;
}

.add-item-form {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  border-top: 1px solid var(--rule);
  padding-top: 12px;
  margin-top: 2px;
}

.add-item-form input {
  flex: 2 1 220px;
  padding: 10px 12px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  font-size: var(--fs-base);
}

.add-item-form input.location-input {
  flex: 1 1 140px;
}

.add-item-form input:focus {
  outline: 2px solid var(--focus);
  outline-offset: -1px;
}

.edit-item-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
}

.edit-item-form input {
  padding: 10px 12px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  font-size: var(--fs-base);
}

.edit-item-actions {
  display: flex;
  gap: 8px;
}

@media (max-width: 560px) {
  .punch-section-head { flex-direction: column; }
  .punch-actions { opacity: 1; }
}

/* Team page ---------------------------------------------------------- */

.signin-card.team-card { max-width: 460px; }

.toggle {
  display: flex;
  border: 1px solid var(--rule);
  border-radius: 8px;
  overflow: hidden;
  width: fit-content;
}

.toggle-option {
  background: var(--card);
  border: none;
  padding: 8px 18px;
  font-size: var(--fs-base);
  cursor: pointer;
  color: var(--muted);
}

.toggle-option.active {
  background: var(--slate);
  color: #fff;
  font-weight: 600;
}

.tier-row {
  display: flex;
  align-items: center;
  gap: 14px;
  border: 1px solid var(--rule);
  border-radius: 12px;
  padding: 14px 16px;
  background: var(--card);
}

.tier-info { flex: 1; min-width: 0; }

.tier-name {
  font-weight: 700;
  color: var(--walnut);
  font-size: var(--fs-md);
}

.tier-desc {
  font-size: var(--fs-sm);
  color: var(--muted);
  margin: 2px 0 4px;
}

.tier-price {
  font-size: var(--fs-sm);
  color: var(--slate-dark);
  font-weight: 600;
}

.qty-input {
  width: 72px;
  padding: 10px 10px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  font-size: var(--fs-input);
  text-align: center;
}

.picker-total {
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--walnut);
  padding: 4px 0;
}

.picker-note {
  color: var(--muted);
  font-size: var(--fs-sm);
  margin: 0;
}

.muted-note {
  color: var(--muted);
  font-size: var(--fs-sm);
}

.team-container { max-width: 640px; }

.section-title {
  font-size: var(--fs-lg);
  color: var(--walnut);
  margin: 28px 0 10px;
}

.member-list {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--rule);
  border-radius: 12px;
  background: var(--card);
  overflow: hidden;
}

.member-list .empty-state { border: none; }

.member-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--rule);
}

.member-row:last-child { border-bottom: none; }

/* Handoff panel: what happens to a departing member's jobs. Sits
   directly under their row, inset and tinted so it reads as attached
   to that person rather than as a new section. */
.handoff {
  padding: 14px 16px 16px;
  border-bottom: 1px solid var(--rule);
  background: var(--shell);
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: flex-start;
}

.handoff:last-child { border-bottom: none; }

.handoff-lead {
  margin: 0;
  font-size: var(--fs-sm);
  line-height: 1.5;
}

/* One row per job, because the choice is per job. */
.handoff-jobs {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
  max-height: 260px;
  overflow-y: auto;
}

.handoff-job {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 8px;
  border-radius: var(--radius-sm, 6px);
  cursor: pointer;
}

.handoff-job:hover { background: var(--bg); }

.handoff-job-name {
  font-size: var(--fs-sm);
  font-weight: 600;
}

.handoff-job-note {
  font-size: var(--fs-xs);
  color: var(--muted);
}

.handoff-dest {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-sm);
}

.handoff-count {
  margin: 0;
  font-size: var(--fs-sm);
  font-weight: 600;
}

.handoff-note {
  margin: 0;
  font-size: var(--fs-xs);
  color: var(--muted);
}

.handoff-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.member-info { flex: 1; min-width: 0; }

.member-email {
  font-size: var(--fs-base);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
}

.member-notes {
  font-size: var(--fs-sm);
  color: var(--muted);
}

.tier-chip {
  font-size: var(--fs-xs);
  font-weight: 600;
  border-radius: 999px;
  padding: 2px 10px;
  background: #eef4f8;
  color: var(--slate-dark);
  white-space: nowrap;
}

.tier-chip.pro {
  background: var(--walnut);
  color: #fff;
}

.add-member-form {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 10px;
}

.add-member-form input {
  flex: 2 1 200px;
  padding: 10px 12px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  font-size: var(--fs-base);
}

.tier-select {
  padding: 10px 12px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  font-size: var(--fs-base);
  background: var(--card);
}

.seats-form {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.seats-form label {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--walnut);
}

.billing-row {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.picker-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* Width and the toolbar's chrome now come from the redesign layer at
   the bottom of this file; keeping the old sticky/border rules here
   made the toolbar a card AND a stuck bar at once. */
.editor-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}

.toolbar-spacer { flex: 1; }

.editor-label {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--walnut);
  margin: 14px 0 6px;
}

.editor-container textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  font: inherit;
  font-size: var(--fs-base);
  background: var(--card);
  resize: vertical;
}

.editor-container textarea:focus,
.segment-title:focus {
  outline: 2px solid var(--focus);
  outline-offset: -1px;
}

.segment-card {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 12px;
  padding: 16px 18px;
  margin-top: 18px;
}

.segment-head {
  display: flex;
  gap: 10px;
  align-items: center;
}

.segment-title {
  flex: 1;
  font-size: var(--fs-input);
  font-weight: 700;
  color: var(--walnut);
  padding: 8px 10px;
  border: 1px solid var(--rule);
  border-radius: 8px;
}

.action-rows {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.action-row {
  display: flex;
  gap: 8px;
  align-items: center;
}

.action-row input {
  flex: 2;
  padding: 8px 10px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  font-size: var(--fs-base);
}

.action-row input.owner-input { flex: 1; }

/* Spreadsheet import -------------------------------------------------- */

.import-card {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 12px;
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.import-card textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  font: inherit;
  font-size: var(--fs-sm);
}

.import-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

.table-scroll {
  overflow-x: auto;
  border: 1px solid var(--rule);
  border-radius: 8px;
}

.import-table {
  border-collapse: collapse;
  font-size: var(--fs-base);
  width: 100%;
}

.import-table tbody tr:nth-child(even) td { background: #fafaf9; }

.import-table th,
.import-table td {
  border-bottom: 1px solid var(--rule);
  padding: 6px 10px;
  text-align: left;
  white-space: nowrap;
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.import-table th {
  background: #f9f9f9;
  font-weight: 600;
  color: var(--walnut);
}

.review-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.review-row {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  border-top: 1px solid var(--rule);
  padding-top: 8px;
}

.review-fields {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.review-fields input {
  padding: 8px 10px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  font-size: var(--fs-base);
}

.review-fields input.review-location { max-width: 260px; }

.review-note {
  font-size: var(--fs-xs);
  color: var(--slate-dark);
}

.review-rownum {
  font-size: var(--fs-sm);
  color: var(--muted);
  white-space: nowrap;
  padding-top: 10px;
}

.import-cta {
  margin-bottom: 16px;
}

/* Replace row + transcript drawer (meeting editor) ------------------- */

.replace-row {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  padding: 10px 0;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 12px;
}

.replace-row input {
  padding: 8px 10px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  font-size: var(--fs-base);
  width: 200px;
}

.transcript-host {
  border: 1px solid var(--rule);
  border-radius: 12px;
  background: var(--card);
  padding: 14px 16px;
  margin-bottom: 14px;
}

.transcript-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-bottom: 8px;
}

.transcript-block {
  white-space: pre-wrap;
  font: inherit;
  font-size: var(--fs-sm);
  color: var(--ink);
  background: var(--bg);
  border-radius: 8px;
  padding: 10px 12px;
  margin: 0 0 8px;
  max-height: 320px;
  overflow-y: auto;
}

/* Reports list ------------------------------------------------------- */

.report-list {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--rule);
  border-radius: 12px;
  background: var(--card);
  overflow: hidden;
}

.report-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--rule);
}

.report-row:last-child { border-bottom: none; }

.report-info { flex: 1; min-width: 0; }

.report-title {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--walnut);
}

.report-sub {
  font-size: var(--fs-sm);
  color: var(--muted);
}

.report-row.clickable { cursor: pointer; }

.action-row .trade-select {
  padding: 8px 10px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  font-size: var(--fs-base);
  background: var(--card);
  max-width: 150px;
}

.report-row.clickable:hover {
  background: #f4f6f8;
}

/* Polish batch (design review 2026-08-06) ---------------------------- */

/* Item 11: visible keyboard focus on every control. */
.btn:focus-visible,
.btn-quiet:focus-visible,
.btn-link:focus-visible,
.toggle-option:focus-visible,
.back-link:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* Item 6: Back controls are buttons now. */
button.back-link {
  background: none;
  border: none;
  font: inherit;
  font-size: var(--fs-sm);
  cursor: pointer;
  padding: 0;
}

/* Item 14: the wordmark goes home. */
.topbar .brand { text-decoration: none; }
/* Item 13: the Lock Ten footer, outside the SPA mount. */
.app-footer {
  max-width: 860px;
  margin: 0 auto;
  padding: 24px;
  color: var(--muted);
  font-size: var(--fs-sm);
  display: flex;
  gap: 16px;
}
.app-footer a { color: var(--muted); }

/* Item 22: editor toolbar density. */
.editor-toolbar .btn {
  padding: 8px 14px;
  font-size: var(--fs-base);
}

/* Team page ---------------------------------------------------------- */

.member-list {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--rule);
  border-radius: 10px;
  background: var(--card);
  overflow: hidden;
}

.member-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 18px;
}

.member-row + .member-row {
  border-top: 1px solid var(--rule);
}

.member-name {
  font-weight: 600;
  color: var(--walnut);
  font-size: var(--fs-md);
}

.member-you {
  color: var(--muted);
  font-weight: 400;
}

.member-email {
  color: var(--muted);
  font-size: var(--fs-sm);
  margin-top: 2px;
}

.member-badges {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: flex-end;
}

.badge {
  font-size: var(--fs-xs);
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--rule);
  color: var(--muted);
  white-space: nowrap;
}

.badge-owner {
  border-color: var(--slate);
  color: var(--slate);
  font-weight: 600;
}

.badge-muted {
  opacity: 0.75;
}

/* ====================================================================
   Redesign layer (2026-08-11, from the approved mockups). Appended
   last so equal-specificity rules here win over the historical ones.
   ==================================================================== */

/* App shell: sidebar + full-width content --------------------------- */

.app {
  display: grid;
  grid-template-columns: 236px minmax(0, 1fr);
  min-height: 100vh;
}

.sidebar {
  border-right: 1px solid var(--rule-subtle);
  background: var(--bg);
  padding: 24px 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 28px;
  position: sticky;
  top: 0;
  height: 100vh;
}

.wordmark {
  font-family: var(--serif);
  font-size: var(--fs-xl);
  color: var(--ink);
  text-decoration: none;
  padding: 0 10px;
  line-height: 1.1;
}

.wordmark small {
  display: block;
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-top: 3px;
}

.workspace-block { padding: 0 10px; }

.workspace-block b {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--walnut);
}

.eyebrow {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 4px;
}

.side-nav { display: flex; flex-direction: column; gap: 2px; }

.side-link {
  font-size: var(--fs-base);
  font-weight: 500;
  color: var(--muted);
  text-decoration: none;
  padding: 9px 10px;
  border-radius: 8px;
}

.side-link:hover { background: var(--rule-subtle); color: var(--ink); }

.side-link.on {
  background: var(--brand-subtle);
  color: var(--slate-dark);
  font-weight: 600;
}

.side-foot {
  margin-top: auto;
  padding: 14px 10px 0;
  border-top: 1px solid var(--rule-subtle);
  font-size: var(--fs-xs);
  color: var(--faint);
  min-width: 0;
}

.side-email {
  display: block;
  color: var(--muted);
  font-weight: 500;
  margin-bottom: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.content {
  padding: 36px 40px 64px;
  max-width: 1160px;
  min-width: 0;
  width: 100%;
}

@media (max-width: 880px) {
  .app { grid-template-columns: 1fr; }
  .sidebar {
    position: static;
    height: auto;
    flex-direction: row;
    align-items: center;
    gap: 16px;
    border-right: none;
    border-bottom: 1px solid var(--rule-subtle);
    padding: 12px 16px;
  }
  .wordmark small { display: none; }
  .workspace-block { display: none; }
  .side-nav { flex-direction: row; }
  .side-foot {
    margin-top: 0;
    margin-left: auto;
    border-top: none;
    padding: 0;
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .side-email { max-width: 160px; }
  .content { padding: 24px 16px 48px; }
}

/* Page headers ------------------------------------------------------- */

.page-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 22px;
}

h1, .page-title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: var(--fs-display);
  line-height: 1.05;
  color: var(--ink);
  margin: 0;
  letter-spacing: 0;
}

.page-sub {
  color: var(--muted);
  font-size: var(--fs-base);
  margin: 6px 0 0;
}

.back-link {
  display: inline-block;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--muted);
  text-decoration: none;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  margin-bottom: 18px;
}

.back-link:hover { color: var(--slate-dark); }

/* Filters ------------------------------------------------------------ */

.filters { display: flex; gap: 6px; flex-wrap: wrap; }

.filter-pill {
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--muted);
  background: none;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 6px 13px;
  cursor: pointer;
}

.filter-pill:hover { color: var(--ink); }

.filter-pill.on {
  background: var(--card);
  border-color: var(--rule);
  color: var(--ink);
  font-weight: 600;
}

/* Project cards (redesigned) ----------------------------------------- */

.project-card {
  box-shadow: var(--shadow-sm);
  border-color: var(--rule-subtle);
}

.project-card .card-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
}

.project-card .name {
  font-family: var(--serif);
  font-weight: 400;
  font-size: var(--fs-xl);
  color: var(--ink);
}

.project-card .name .address {
  font-family: "Geist", sans-serif;
  font-size: var(--fs-sm);
  color: var(--faint);
  margin-left: 10px;
  display: inline;
}

.card-meta {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.meta-mono {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--faint);
}

.chip {
  font-size: var(--fs-xs);
  font-weight: 500;
  border-radius: 999px;
  padding: 3px 9px;
  white-space: nowrap;
}

.chip-who { background: var(--brand-subtle); color: var(--slate-dark); }


.door {
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--muted);
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 7px 13px;
  text-decoration: none;
  white-space: nowrap;
}

.door:hover { border-color: var(--brand-border); color: var(--slate-dark); }

.door b { font-weight: 600; color: var(--slate-dark); }

.door.hot {
  background: var(--brand-subtle);
  border-color: var(--brand-border);
  color: var(--slate-dark);
  font-weight: 600;
}

.project-card .card-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 13px;
}

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

.progress {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 4px 0 18px;
}

.progress code {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--muted);
  white-space: nowrap;
}

.progress-bar {
  flex: 1;
  height: 5px;
  border-radius: 999px;
  background: var(--rule-subtle);
  overflow: hidden;
}

.progress-bar i {
  display: block;
  height: 100%;
  width: 0;
  background: var(--slate);
  border-radius: 999px;
}

/* Editor toolbar (grouped) + save bar --------------------------------- */

.editor-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--card);
  border: 1px solid var(--rule-subtle);
  border-radius: 12px;
  padding: 8px 10px;
  box-shadow: var(--shadow-sm);
  flex-wrap: wrap;
}

.tool-group { display: flex; gap: 4px; }

.tool-sep { width: 1px; height: 20px; background: var(--rule-subtle); }

.editor-toolbar .btn-secondary {
  border-color: transparent;
  background: none;
  color: var(--muted);
  font-weight: 500;
  padding: 8px 12px;
  font-size: var(--fs-sm);
}

.editor-toolbar .btn-secondary:hover {
  background: var(--bg);
  color: var(--walnut);
}

.editor-toolbar .btn-secondary.on {
  background: var(--brand-subtle);
  color: var(--slate-dark);
  font-weight: 600;
}

.replace-row {
  background: var(--brand-subtle);
  border: 1px solid var(--brand-border);
  border-radius: 12px;
  padding: 10px 12px;
  margin-top: 8px;
}

.save-bar {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 16px;
  background: var(--card);
  border: 1px solid var(--rule-subtle);
  border-radius: 12px 12px 0 0;
  padding: 12px 18px;
  margin-top: 24px;
  box-shadow: 0 -4px 12px rgba(18, 15, 48, 0.05);
}

.save-state { font-size: var(--fs-sm); color: var(--muted); }

/* Cards and rows pick up the indigo shadow --------------------------- */

.report-row, .member-list, .punch-section, .segment-card, .import-card {
  box-shadow: var(--shadow-sm);
  border-color: var(--rule-subtle);
}

/* The hidden attribute must always win: the base rule's display:flex
   was silently defeating it, which is why the find/replace row never
   closed (the very thing Josh flagged in round 2). */
.replace-row[hidden] { display: none; }

/* Same guard as .replace-row above: any element that pairs a display
   rule with the hidden attribute needs this, or the attribute is
   silently defeated (reviewer must-hotfix on the redesign commit). */
.progress[hidden] { display: none; }

/* Small segmented toggle for the card visibility control. */
.toggle-sm .toggle-option {
  padding: 5px 12px;
  font-size: var(--fs-xs);
}

.toggle-sm .toggle-option:disabled {
  opacity: 0.55;
  cursor: default;
}

/* ====================================================================
   Editor refinement (2026-08-11, Josh: "does not look like a
   professional interface... more contrast... items are hard to read
   and not expanding to use the space"). Four fixes: the editor uses
   the page width, item rows become a real grid so the task field
   takes every spare pixel, form controls get the app's stronger line
   token and larger ink-colored text, and disabled buttons read as
   inert rather than broken.
   ==================================================================== */

.editor-container {
  max-width: 1040px;
}

/* Form controls ------------------------------------------------------ */

.editor-container textarea,
.action-row input,
.action-row .trade-select,
.segment-title {
  border: 1px solid var(--line-strong);
  color: var(--ink);
  font-size: var(--fs-md);
  background: var(--card);
}

.editor-container textarea {
  padding: 12px 14px;
  line-height: 1.6;
  min-height: 88px;
}

.editor-container textarea::placeholder,
.action-row input::placeholder {
  color: var(--faint);
}

.editor-container textarea:focus,
.action-row input:focus,
.action-row .trade-select:focus,
.segment-title:focus {
  outline: 2px solid var(--focus);
  outline-offset: -1px;
  border-color: var(--focus);
}

/* Section labels: a header with a hairline, not a floating small word */

.editor-label {
  display: block;
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--walnut);
  margin: 26px 0 10px;
  padding-bottom: 7px;
  border-bottom: 1px solid var(--rule-subtle);
}

.segment-card .editor-label {
  margin-top: 20px;
}

/* Item rows: a grid, so the task text gets all the spare width ------- */

.action-rows {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* The last column is a fixed width, not auto: the header row's own
   last cell is empty, and an auto column would collapse there and
   knock every header label out of line with its field. */
.action-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 230px 78px;
  gap: 10px;
  align-items: center;
}

.action-row-trade {
  grid-template-columns: minmax(0, 1fr) 165px 230px 78px;
}

.action-row input,
.action-row .trade-select {
  width: 100%;
  min-width: 0;
  padding: 10px 12px;
  border-radius: 8px;
}

/* The grid owns the column widths now; the historical 150px cap left
   the select floating short of its cell. */
.action-row .trade-select {
  max-width: none;
}

.action-row .btn-quiet {
  padding: 8px 10px;
  font-size: var(--fs-sm);
  color: var(--faint);
}

.action-row .btn-quiet:hover {
  color: var(--danger);
  background: var(--rule-subtle);
}

/* Column headers so the fields are labeled once, not per row. */

.action-head {
  padding: 0 0 2px;
}

.action-head span {
  font-family: var(--mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}

@media (max-width: 860px) {
  .action-row,
  .action-row-trade {
    grid-template-columns: minmax(0, 1fr) auto;
  }
  .action-head {
    display: none;
  }
}

/* Section cards ------------------------------------------------------- */

.segment-card {
  padding: 20px 22px 22px;
  margin-top: 20px;
  border-color: var(--rule-subtle);
}

.segment-title {
  font-size: var(--fs-lg);
  font-weight: 600;
  padding: 9px 12px;
  border-radius: 8px;
}

/* Disabled buttons read as not-yet, not as broken -------------------- */

.btn:disabled {
  opacity: 1;
  background: var(--rule-subtle);
  border-color: var(--rule);
  color: var(--faint);
}

.save-bar .btn:not(:disabled) {
  box-shadow: 0 1px 2px rgba(18, 15, 48, 0.12);
}

/* ====================================================================
   Contrast pass (2026-08-11 design review). The console had been
   assigning CONTENT to --faint, which is the mobile theme's
   placeholder/disabled color (#7E7BA2, 4.0:1 on white, under the AA
   floor). Anything a builder reads or clicks moves to --muted
   (#565380, 7.1:1); --faint is now decoration only.
   ==================================================================== */

/* The dashboard lines a builder actually scans. */
.meta-mono,
.project-card .name .address {
  color: var(--muted);
}

/* Editor column headers exist to be read, so they cannot be the
   faintest thing on the page. */
.action-head span {
  color: var(--muted);
  font-size: var(--fs-2xs);
}

/* Interactive text is never decoration. */
.action-row .btn-quiet {
  color: var(--muted);
}

/* Edit and Delete on every punch row were --muted THEN dimmed to 0.6
   opacity, landing at 2.8:1: the least legible text in the console,
   on its most repeated action. De-emphasis comes from weight and
   size, not from making the words hard to see. */
.punch-actions {
  opacity: 1;
}

.punch-actions .btn-quiet {
  color: var(--muted);
  font-size: var(--fs-sm);
}

/* Disabled buttons: keep the inert look, but readable (was 3.2:1). */
.btn:disabled {
  color: var(--muted);
}

/* Save state earns its colors: the palette already carried a green
   that nothing used, and a failed save used to look like a success. */
.save-state.is-saved {
  color: var(--done);
  font-weight: 500;
}

.save-state.is-error {
  color: var(--danger);
  font-weight: 600;
}

/* The hairline on section labels belongs to the editors; the import
   card reuses the class for two small field labels, where an
   underline reads as a stray rule. */
.import-card .editor-label {
  border-bottom: none;
  margin: 14px 0 6px;
  padding-bottom: 0;
}

/* Roster emails were rendering semibold: the older .member-email rule
   still won on font-weight, and its ellipsis never worked because
   the later block dropped white-space. */
.member-email {
  font-weight: 400;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ====================================================================
   Dashboard and shell (2026-08-11 design review, findings 5/7/14/15).
   ==================================================================== */

/* The sidebar was the same value as the page, separated by one
   hairline, which is most of why the console read flat. White rail,
   recessed work area. */
.sidebar {
  background: var(--card);
  border-right: 1px solid var(--rule);
}

/* On a wide monitor the whole app used to hug the left edge with
   hundreds of pixels of dead background on the right. */
.content {
  margin: 0 auto;
}

/* Doors on a fixed grid: the same kind sits in the same column on
   every card, so counts line up down the page. Six columns matches
   the six capture kinds; below 1100px it relaxes to three, then two,
   which keeps columns aligned across cards at every width. */
.doors {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 7px;
  width: 100%;
}

/* Six across needs roughly 1400px of window once the icons and the
   longest kind name are in; below that, three across keeps the
   columns aligned across cards without ellipsis, which is the point
   of the grid. */

.door {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  white-space: normal;
  min-width: 0;
}

.door-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Counts in tabular figures so the column reads as a column. */
.door-count {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--slate-dark);
}

/* A door with nothing behind it stays in place (Josh: match the app,
   habit beats hierarchy) but stops competing for the eye. */
.door-empty {
  border-color: var(--rule-subtle);
  color: var(--faint);
}

.door-empty:hover {
  color: var(--muted);
  border-color: var(--rule);
}

.door.hot .door-count { color: var(--slate-dark); }

/* What is waiting on this job, in the palette's warning tone: it is
   not an error, it is work. */
.chip-attention {
  background: #f4ecdb;
  color: #7a4e00;
  font-weight: 600;
}

.project-card .card-actions {
  align-items: flex-start;
  flex-wrap: nowrap;
  gap: 16px;
}


/* Doors now own the full card width (the visibility toggle moved up
   to the meta row), and the label rides a hair smaller so the longest
   kind name fits its column without ellipsis. */
.door {
  font-size: var(--fs-sm);
  padding: 7px 11px;
}

.project-card .card-actions {
  gap: 0;
}

/* ====================================================================
   Final review pass: prose measure, destructive affordances, focus.
   ==================================================================== */

/* 1040px is right for the item grid and wrong for prose: it put the
   narrative on a ~140 character line, well past what anyone tracks.
   Text fields keep a readable measure; the grids keep the width. */
.editor-container textarea {
  max-width: 760px;
}

.editor-container .segment-card textarea {
  max-width: 100%;
}

/* A destructive action should look destructive before the cursor
   arrives, not only on hover. */
.btn-quiet.danger {
  color: #8f3629;
}

.btn-quiet.danger:hover {
  color: var(--danger);
  background: #f6e1dd;
}

/* The dashboard's own controls had no visible keyboard focus. */
.door:focus-visible,
.filter-pill:focus-visible,
.side-link:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* Off-palette warm grays inside an indigo-cast system read dirty.
   These are the app's own tints. */
.btn-secondary:hover { background: var(--bg); }
.btn-quiet:hover { background: var(--bg); }
.info-banner {
  background: var(--brand-subtle);
  border-color: var(--brand-border);
}
.error-banner {
  background: #f6e1dd;
  border-color: #e3bdb4;
}
.report-row.clickable:hover { background: var(--bg); }

/* Capture-kind icons on the doors: the app's own glyphs, so the two
   surfaces read as one product. Sized in ems to ride the label. */
.kind-icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  opacity: 0.85;
}

.door {
  gap: 7px;
}

.door .door-label {
  flex: 1;
}

.door-empty .kind-icon { opacity: 0.55; }

.door.hot .kind-icon { opacity: 1; }

/* Project management from the console (scope item 1) ---------------- */

.head-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.new-project-form,
.edit-project-form {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 12px;
  padding: 12px 14px;
  margin-bottom: 14px;
}

.edit-project-form {
  margin-bottom: 0;
  border: none;
  padding: 0;
}

.new-project-form input,
.edit-project-form input {
  flex: 1;
  min-width: 180px;
  padding: 9px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  font-size: var(--fs-md);
  color: var(--ink);
  background: var(--card);
}

.new-project-form input:focus,
.edit-project-form input:focus {
  outline: 2px solid var(--focus);
  outline-offset: -1px;
  border-color: var(--focus);
}

.new-project-form[hidden] { display: none; }

.card-manage-group {
  display: inline-flex;
  gap: 2px;
}

.card-manage {
  font-size: var(--fs-sm);
  color: var(--faint);
  padding: 5px 8px;
}

.card-manage:hover { color: var(--ink); }

.card-manage.danger { color: var(--faint); }

.card-manage.danger:hover {
  color: var(--danger);
  background: #f6e1dd;
}

/* ====================================================================
   Josh 2026-08-16: cards too wide and short, no logo, blue too light,
   editor toolbar not reading as controls, item text clipping.
   ==================================================================== */

/* Cards two across on a wide monitor, so a job reads as a card rather
   than a full-width band. Each column is equal width, so the door
   columns still line up across cards, which is the point of the grid. */
.project-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 460px), 1fr));
  gap: 14px;
}

/* Doors now size to the CARD, not the window: a card in a two-across
   layout is about half the width, so a viewport breakpoint would put
   six columns in 460px and clip every label. */
.project-card {
  container-type: inline-size;
}

.doors {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@container (min-width: 430px) {
  .doors { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@container (min-width: 900px) {
  .doors { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}


/* The house mark, so the rail is a brand and not just a word. */
.wordmark {
  display: flex;
  align-items: center;
  gap: 9px;
}

.brand-mark {
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 6px;
  /* The asset is 601x535, so a forced square squashed it. */
  object-fit: contain;
}

/* Help lives next to sign-out, where people look for it. */
.side-foot-links {
  display: flex;
  align-items: center;
  gap: 4px;
}

.side-foot-link {
  font-size: var(--fs-sm);
  color: var(--muted);
  text-decoration: none;
  padding: 8px 10px;
  border-radius: 6px;
}

.side-foot-link:hover {
  background: var(--bg);
  color: var(--ink);
}

/* Editor toolbar: these were words on a white strip, so nothing said
   they were controls. Give them the same outline the rest of the app
   uses for a secondary button. */
.editor-toolbar {
  background: var(--bg);
  border-color: var(--rule);
  gap: 6px;
  padding: 10px 12px;
}

.editor-toolbar .btn-secondary {
  background: var(--card);
  border: 1px solid var(--rule);
  color: var(--walnut);
  font-weight: 500;
  padding: 8px 14px;
  box-shadow: var(--shadow-sm);
}

.editor-toolbar .btn-secondary:hover {
  border-color: var(--brand-border);
  color: var(--slate-dark);
  background: var(--card);
}

.editor-toolbar .btn-secondary.on {
  background: var(--brand-subtle);
  border-color: var(--brand-border);
  color: var(--slate-dark);
  font-weight: 600;
}

.tool-sep {
  background: var(--rule);
  height: 24px;
}

/* Item fields wrap and grow instead of scrolling their text out of
   sight. They are textareas now, so they need the input treatment. */
.action-row .grow-field {
  width: 100%;
  min-width: 0;
  /* The editor's textarea rule sets min-height 88px for the narrative
     box. Inheriting it made every one-line punch item a 88px slab,
     which is why the rows still read as heavy after they started
     wrapping. autoGrow owns the height here. */
  min-height: 0;
  padding: 10px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  font-family: inherit;
  font-size: var(--fs-md);
  color: var(--ink);
  background: var(--card);
  line-height: 1.45;
  resize: none;
  overflow-y: hidden;
}

.action-row .grow-field:focus {
  outline: 2px solid var(--focus);
  outline-offset: -1px;
  border-color: var(--focus);
}

/* Rows can be different heights now, so align to the top and let the
   trade select and Remove sit against the first line. */
.action-row {
  align-items: start;
}

.action-row .trade-select,
.action-row .btn-quiet {
  margin-top: 1px;
}

/* Branding settings ------------------------------------------------- */

.settings-form {
  max-width: 720px;
  display: flex;
  flex-direction: column;
}

.setting-field + .setting-field { margin-top: 22px; }

.setting-hint {
  color: var(--muted);
  font-size: var(--fs-sm);
  margin: -4px 0 10px;
}

.settings-form input[type="text"],
.settings-form textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  font-family: inherit;
  font-size: var(--fs-md);
  color: var(--ink);
  background: var(--card);
  line-height: 1.5;
  resize: vertical;
}

.settings-form input[type="text"]:focus,
.settings-form textarea:focus {
  outline: 2px solid var(--focus);
  outline-offset: -1px;
  border-color: var(--focus);
}

.logo-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.logo-box {
  width: 132px;
  height: 72px;
  border: 1px dashed var(--line-strong);
  border-radius: 10px;
  background: var(--card);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  flex: none;
}

.logo-preview-img {
  max-width: 116px;
  max-height: 60px;
  object-fit: contain;
}

.logo-empty {
  font-size: var(--fs-sm);
  color: var(--faint);
}

.color-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.color-input {
  width: 52px;
  height: 40px;
  padding: 3px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--card);
  cursor: pointer;
}

.settings-form .color-hex {
  width: 130px;
  font-family: var(--mono);
  text-transform: uppercase;
}

.settings-form .save-bar {
  justify-content: flex-start;
  border-radius: 12px;
  margin-top: 28px;
}

/* Sidebar foot: same shapes as the top of the rail ------------------ */

.side-foot {
  margin-top: auto;
  padding: 16px 10px 0;
  border-top: 1px solid var(--rule-subtle);
  color: var(--faint);
  min-width: 0;
}

.account-row {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 10px;
  min-width: 0;
}

/* Reads as a sibling of the house mark at the top: same box, same
   radius, brand tint instead of an image. */
.account-mark {
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 6px;
  background: var(--brand-subtle);
  color: var(--slate-dark);
  font-size: var(--fs-sm);
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}

.side-foot .side-email {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--walnut);
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

/* Help and Sign out are the same control as Projects and Team, so the
   rail reads as one list of places rather than nav on top and
   leftovers underneath. */
.side-foot-nav {
  margin: 0 -10px;
}

.side-link-button {
  font-family: inherit;
  background: none;
  border: none;
  text-align: left;
  width: 100%;
  cursor: pointer;
}

/* The console's footer lives in the content column and inherits its
   padding, so it starts where the page starts. The standalone
   .app-footer above still serves team.html, which is a centered
   column with no sidebar. */
.content-footer {
  display: flex;
  gap: 16px;
  margin-top: 40px;
  padding-top: 18px;
  border-top: 1px solid var(--rule-subtle);
  color: var(--faint);
  font-size: var(--fs-xs);
}

.content-footer a {
  color: var(--faint);
  text-decoration: none;
}

.content-footer a:hover {
  color: var(--muted);
  text-decoration: underline;
}

/* ====================================================================
   The Photos door (stage 1, ratified 2026-09-05).

   Nothing above this point is touched. In particular the kind-door
   grid keeps its own container steps (2 / 3 at 430 / 6 at 900) exactly
   as they are, because the Photos door does not join that grid: it
   spans the full card width on a row of its own underneath. Two
   reasons, one of them a bug avoided. Photos are not a capture KIND,
   and a seventh cell would have shifted every existing door into a
   different column on every card, which is the muscle memory the fixed
   grid exists to protect.

   These rules are LAST in the file on purpose. A later block of equal
   specificity wins over a container query, which is how the door grid
   broke the last time it moved, so nothing may be appended after this.
   ==================================================================== */

/* The Photos door ---------------------------------------------------- */

.door-photos {
  grid-column: 1 / -1;
  align-items: center;
}

/* The only door carrying a second line, so the label and its sub-label
   stack while the icon and the count stay on the centre line. */
.door-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
  line-height: 1.25;
}

/* .door .door-label carries flex: 1 for the single-line doors. Inside
   the column stack that would stretch the label and push the sub-label
   to the bottom of the box, so it is neutralised here. */
.door-photos .door-label {
  flex: none;
}

.door-sub {
  font-size: var(--fs-2xs);
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.door-empty .door-sub {
  color: var(--faint);
}

/* Interface glyphs ride their label the same way the kind glyphs do. */
.ui-icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  opacity: 0.85;
}

/* The view ------------------------------------------------------------ */

/* Named container, so the grid below sizes to the CONTENT column and
   can never accidentally resolve against .project-card, which is also
   a container. */
.photos-view {
  container-type: inline-size;
  container-name: photos;
  position: relative;
}

.photos-crumb {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  min-width: 0;
}

/* Shared links ------------------------------------------------------- */

/* Every public link standing open on this job, for its owner. Above
   the grid because it is about the job rather than about a selection,
   and quiet enough that it reads as a status strip rather than as the
   page's subject. */
.photo-sharelinks {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-bottom: 18px;
  padding: 12px 14px;
  border: 1px solid var(--rule);
  border-radius: 12px;
  background: var(--card);
}

/* The guard, for the same reason .photo-actionbar needs one: the rule
   above names display, which beats the user agent's
   [hidden] { display: none }. Without this the strip would render as an
   empty box on every job that has never been shared. */
.photo-sharelinks[hidden] { display: none; }

.sharelink-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  margin-bottom: 6px;
}

.sharelink-head h2 {
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--walnut);
  margin: 0;
}

.sharelink-note {
  font-size: var(--fs-sm);
  color: var(--muted);
}

.sharelink-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px 12px;
  padding: 7px 0;
  border-top: 1px solid var(--rule-subtle);
}

.sharelink-what {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  min-width: 0;
}

.sharelink-what strong {
  font-size: var(--fs-base);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.sharelink-when {
  font-size: var(--fs-sm);
  color: var(--muted);
}

.crumb-sep,
.crumb-here {
  font-size: var(--fs-lg);
  color: var(--faint);
}

.crumb-here {
  color: var(--muted);
}

.photo-group {
  margin-bottom: 34px;
}

.group-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.photo-group h2 {
  font-family: inherit;
  font-size: var(--fs-lg);
  font-weight: 600;
  color: var(--walnut);
  margin: 0;
}

.group-sub {
  font-size: var(--fs-sm);
  color: var(--muted);
  margin-top: 2px;
}

.group-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}

.group-actions a.btn-quiet {
  text-decoration: none;
}

/* The grid ------------------------------------------------------------ */

.photo-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

.photo-tile {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: 8px;
  overflow: hidden;
  background: var(--rule-subtle);
  cursor: pointer;
  /* Shift-click is a range here, and without this the browser also
     drags a text selection across the tiles' alt text as it goes. */
  user-select: none;
  /* The ring lives here so a selected tile can grow one without
     changing size and re-flowing the row. */
  box-shadow: 0 0 0 0 transparent;
  transition: box-shadow 120ms ease;
}

.tile-img {
  display: block;
  width: 100%;
  height: 100%;
  /* Square centre crop, the same crop the gallery thumb already is. */
  object-fit: cover;
  user-select: none;
}

/* Selected: a cobalt ring, and nothing else changes. Dimming the
   UNSELECTED tiles is the obvious move and the wrong one, because on
   this screen the photos are the content, not the chrome. */
.photo-tile.on {
  box-shadow: 0 0 0 3px var(--slate);
}

.photo-tile:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.tile-room {
  position: absolute;
  left: 6px;
  bottom: 6px;
  max-width: calc(100% - 12px);
  padding: 2px 7px;
  border-radius: 5px;
  /* A scrim, not a solid chip: the room label has to stay readable over
     a white ceiling and a dark crawlspace alike. */
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: var(--fs-2xs);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tile-mark {
  position: absolute;
  right: 6px;
  top: 6px;
  width: 20px;
  height: 20px;
  border-radius: 5px;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-2xs);
}

.tile-mark .ui-icon {
  opacity: 1;
  width: 12px;
  height: 12px;
}

/* Out of the way until it is wanted: on hover, on keyboard focus, and
   at rest for every tile once anything on the page is selected. */
.tile-check {
  position: absolute;
  left: 6px;
  top: 6px;
  width: 20px;
  height: 20px;
  border-radius: 5px;
  border: 1.5px solid #fff;
  background: rgba(0, 0, 0, 0.35);
  color: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 120ms ease;
}

.tile-check .ui-icon {
  width: 13px;
  height: 13px;
  opacity: 1;
}

.photo-tile:hover .tile-check,
.photo-tile:focus-visible .tile-check,
.photo-grid.has-selection .tile-check {
  opacity: 1;
}

.photo-tile.on .tile-check {
  opacity: 1;
  background: var(--slate);
  border-color: var(--slate);
  color: #fff;
}

/* The action bar ------------------------------------------------------ */

/* Floating and centred rather than pinned edge to edge: the console has
   a fixed sidebar, and a full-width bar runs under it and lines up with
   nothing (the same mistake the page footer made, 2026-08-16). */
.photo-actionbar {
  position: fixed;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 10px 14px;
  border-radius: 10px;
  border: 1px solid var(--rule);
  background: var(--card);
  box-shadow: var(--shadow-md);
  animation: photo-bar-in 140ms ease-out;
  /* Stage 2 put five actions in here where stage 1 had one, so the bar
     is now wide enough to run off a laptop screen if it is allowed to.
     It wraps instead, and the width ceiling is the VIEWPORT rather than
     the content column, because the bar is position:fixed and centred
     on the window: a container-relative max-width would let it hang
     over the left edge on a narrow window. */
  flex-wrap: wrap;
  justify-content: center;
  max-width: min(760px, calc(100vw - 32px));
}

/* The third time this stylesheet has hit display-defeats-hidden
   (.replace-row, then .progress, now this). An author display rule
   beats the user agent's [hidden] { display: none }, so the bar that is
   only supposed to exist while something is selected was rendering at
   66px saying "0 selected", live, over the bottom of the grid.

   THE RULE, for anything added later: if an element is toggled with the
   hidden attribute AND carries a display declaration of its own, it
   needs its own [hidden] guard. There is no way to get one for free. */
.photo-actionbar[hidden] { display: none; }

.photo-actionbar .bar-count {
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--walnut);
  font-variant-numeric: tabular-nums;
}

.photo-actionbar .bar-actions {
  display: flex;
  align-items: center;
  /* Wraps for the same reason the bar does. The count keeps its own
     line above the buttons once they need two rows, which reads better
     than a count stranded at the end of a wrapped row. */
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}

@keyframes photo-bar-in {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}

/* The single-photo panel ---------------------------------------------- */

/* Narrow container: a full-screen overlay, because a 400px drawer on a
   500px screen is not a drawer. */
.photo-panel {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 18px;
  overflow-y: auto;
  background: var(--card);
  border-left: 1px solid var(--rule);
  animation: photo-panel-in 140ms ease-out;
}

.panel-photo-link {
  display: block;
  border-radius: 8px;
  overflow: hidden;
  background: var(--bg);
}

.panel-photo {
  display: block;
  width: 100%;
  max-height: 46vh;
  object-fit: contain;
}

.panel-rows {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.panel-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.panel-label {
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
}

.panel-value {
  font-size: var(--fs-base);
  color: var(--ink);
}

.panel-value a {
  color: var(--slate);
}

.panel-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px solid var(--rule-subtle);
}

@keyframes photo-panel-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

.empty-title {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--walnut);
  margin: 0 0 4px;
}

/* Modals (Add to report, Email) ---------------------------------------

   Small, centred, and above the single-photo panel, because a modal is
   always the thing most recently asked for. The scrim is fixed to the
   window, which is what puts it over the panel and the sidebar both.

   A CORRECTION: this used to say the scrim had to sit outside
   .photos-view because container-type: inline-size makes an element a
   containing block for position:fixed descendants. Measured in
   Chromium on 2026-09-06, it does not: a fixed child of a
   container-type element still resolves against the viewport. The rule
   that DOES trap fixed descendants is contain: layout (and transform,
   filter, perspective, will-change and backdrop-filter), none of which
   is on .photos-view. The modal host stays mounted outside it anyway,
   for a different and still-true reason written at the mount site in
   view-photos.ts. */

.photo-modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(18, 15, 48, 0.34);
  animation: photo-panel-in 140ms ease-out;
}

.photo-modal {
  width: min(440px, 100%);
  /* Never taller than the window, and the body is what scrolls, so the
     footer buttons stay reachable on a short laptop screen. */
  max-height: min(560px, calc(100vh - 40px));
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 18px;
  border-radius: 10px;
  border: 1px solid var(--rule);
  background: var(--card);
  box-shadow: var(--shadow-md);
}

.modal-title {
  margin: 0;
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--walnut);
}

.modal-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow-y: auto;
}

.modal-note {
  margin: 0;
  font-size: var(--fs-sm);
  color: var(--muted);
  line-height: 1.5;
}

.modal-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* One report to pick. Full width and left aligned: this is a list of
   things, not a row of controls. */
.modal-choice {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
  padding: 9px 11px;
  border-radius: 8px;
  border: 1px solid var(--rule);
  background: var(--bg);
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: var(--ink);
}

.modal-choice:hover {
  border-color: var(--slate);
}

.modal-choice:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.choice-title {
  font-size: var(--fs-md);
  font-weight: 500;
}

/* The share link, on screen and copyable. Readonly rather than
   disabled so the text can still be selected by hand when the clipboard
   API is not available. */
.modal-linkrow {
  display: flex;
  align-items: center;
  gap: 6px;
}

.modal-link {
  flex: 1 1 auto;
  /* min-width:0 or a long URL refuses to shrink and pushes Copy off the
     edge of the dialog. */
  min-width: 0;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--rule);
  background: var(--bg);
  font-size: var(--fs-sm);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: var(--ink);
}

.modal-link:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

.modal-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--rule-subtle);
}

/* Close is first in the DOM so it is the first tab stop and the default
   focus target, and it stays on the left. Everything after it is pushed
   right, which is where the action a person came for belongs. */
.modal-foot > *:nth-child(2) {
  margin-left: auto;
}

/* Container steps ------------------------------------------------------
   Tiles per row against the CONTENT column's width, not the window's:
   3 below 480, 4 to 767, 5 to 999, 6 from 1000. The panel becomes a
   drawer at the same 768 the grid takes its fourth step past, which is
   the width where 400px of it still leaves a usable grid behind. */

@container photos (min-width: 480px) {
  .photo-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@container photos (min-width: 768px) {
  .photo-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .photo-panel {
    inset: 0 0 0 auto;
    width: 400px;
    box-shadow: var(--shadow-md);
  }
}

@container photos (min-width: 1000px) {
  .photo-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* Motion is decoration here; the states it announces are all visible
   without it. */
@media (prefers-reduced-motion: reduce) {
  .photo-actionbar,
  .photo-panel,
  .photo-modal-scrim {
    animation: none;
  }
  .photo-tile,
  .tile-check {
    transition: none;
  }
}

/* The Recent feed ------------------------------------------------------
   TWO containers, and the difference between them is what makes the
   filter bar and the grid both size to the CONTENT column.

   .feed-col is the column: the filter bar AND the view are inside it.
   A container query only sees descendants, and the wide-screen rule at
   the bottom of this file targets the Filters button, which lives in
   the bar. With the bar a SIBLING of the grid's container that rule
   matched nothing and the button never left a big monitor. The column
   is the container rather than the bar being moved inside .feed-view,
   so that element stays exactly what the Photos door has: same
   classes, same children, same rules.

   Adding a second container-type ancestor does NOT trap the
   single-photo panel, which is position: fixed. Measured in Chromium
   on 2026-09-14 at seven column widths: the panel's right edge is the
   VIEWPORT's at every one of them, never the view's. Same finding as
   the correction written above .photo-modal-scrim, now true of two
   nested containers rather than one.

   .feed-view carries BOTH names, and that is the second half of it. It
   is the same element as .photos-view, which already declares
   `container-type: inline-size; container-name: photos`, so a bare
   `container-name: feed` here REPLACED that name rather than adding to
   it: every `@container photos` rule (the grid's 480 / 768 / 1000 steps
   and the panel's drawer at 768) stopped matching inside Recent, and
   the feed sat at three tiles across on every screen. A name LIST is
   the fix. container-type stays where it was, on .photos-view. */

.feed-col {
  container-type: inline-size;
  container-name: feed;
}

.feed-view {
  container-name: photos feed;
}

/* PHOTOS START HIGHER ON A LAPTOP (Josh, 2026-09-13). The default
   .page-head is a flex row with 22px under it and a display-size title;
   on a 768px-tall screen that plus a two-row filter bar pushed the
   first tile below the fold. So the head stacks tight, the title steps
   down, and the three lines under it are one line each. */
.feed-head {
  display: block;
  margin-bottom: 12px;
}

.feed-head .page-title {
  font-size: var(--fs-xl);
  line-height: 1.1;
}

.feed-sub,
.feed-status,
.feed-attention {
  margin: 2px 0 0;
  font-size: var(--fs-sm);
  color: var(--muted);
}

.feed-status {
  font-variant-numeric: tabular-nums;
}

.feed-attention a {
  color: var(--slate-dark);
}

/* The filter bar ------------------------------------------------------
   One row always: search, the job select, Filters, Clear. The chips and
   the labels live in a drawer under it.

   JOSH'S CALL, 2026-09-13: below a wide container the labels row and
   the chips took too much of the screen, and the photos are the page.
   So the drawer is CLOSED by default and the Filters button opens it,
   PUSHING the content down rather than floating over it: a popover on a
   page whose job is scanning photos hides the photos. Past 1000px of
   content the button is gone and the drawer is simply open, which is
   the bar as it was drawn in the mockup. */
.feed-filters {
  margin-bottom: 16px;
}

.feed-filter-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.feed-search {
  flex: 1 1 220px;
  min-width: 0;
  padding: 7px 10px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: var(--card);
  color: var(--ink);
  /* 16px, not the console's 14: anything smaller makes iOS Safari zoom
     the page the moment the field takes focus, and a console somebody
     opens on a phone in a truck should not jump. */
  font-size: 16px;
}

.feed-search:focus-visible {
  outline: 2px solid var(--slate);
  outline-offset: 1px;
}

.feed-job {
  flex: 0 1 200px;
  min-width: 0;
  padding: 7px 8px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: var(--card);
  color: var(--ink);
  font-size: var(--fs-base);
}

.feed-filter-drawer {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}

/* Display-defeats-hidden, the fourth time (see .photo-actionbar). */
.feed-filter-drawer[hidden] { display: none; }

.feed-chiprow {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
}

.feed-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.feed-chip {
  padding: 4px 10px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--card);
  color: var(--muted);
  font-size: var(--fs-sm);
  font-family: inherit;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
}

.feed-chip:hover { border-color: var(--brand-border); color: var(--slate-dark); }

.feed-chip.on {
  background: var(--brand-subtle);
  border-color: var(--brand-border);
  color: var(--slate-dark);
  font-weight: 600;
}

.feed-chip:focus-visible {
  outline: 2px solid var(--slate);
  outline-offset: 1px;
}

/* The builder's own Settings words that have no photos yet. Labelled,
   rather than mixed in, so a chip reading zero is not read as a bug. */
.feed-morelabels {
  align-self: center;
  font-size: var(--fs-sm);
  color: var(--faint);
  margin-left: 4px;
}

/* Blocks and the end of the feed -------------------------------------- */

.feed-block .group-head h2 a {
  color: inherit;
  text-decoration: none;
}

.feed-block .group-head h2 a:hover {
  text-decoration: underline;
}

/* The "+37 more" tile. It is a link to the job's own Photos door, which
   is the surface that holds all of them, so it is an anchor rather than
   a button and it is deliberately not selectable. */
.tile-more {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--rule-subtle);
  color: var(--slate-dark);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-decoration: none;
  text-align: center;
  padding: 8px;
}

.tile-more:hover { background: var(--brand-subtle); }

.feed-more {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 8px 0 40px;
}

/* A Load more that did not work. Under the button rather than in place
   of the feed: the pages already drawn are still good, and the failure
   is about the NEXT page. */
.feed-more-error {
  margin: 0;
  font-size: var(--fs-sm);
  color: var(--danger);
}

/* The live region now exists whether or not there is a problem to
   announce (see view-feed.ts drawMore), so an empty one collapses
   instead of leaving a blank line under the button. */
.feed-more-error:empty {
  display: none;
}

.feed-end {
  margin: 0;
  color: var(--faint);
  font-size: var(--fs-sm);
}

.feed-stale {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 16px;
}

.feed-stale[hidden] { display: none; }

/* The action bar, across jobs ------------------------------------------
   Two rows of its own: the trial warning above everything, then the
   count and the buttons, then the one-zip-per-job note. The warning is
   ABOVE the buttons on purpose, and visible the whole time an uncharged
   owned capture is selected rather than appearing at the click: the
   decision it affects is which photos to pick. */
.feed-actionbar {
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
}

.feed-actionbar .bar-main {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: center;
  gap: 18px;
}

.feed-trial-warning {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  padding: 7px 10px;
  border-radius: 8px;
  background: #fdf3e3;
  border: 1px solid #edd9b4;
  color: #6b4a10;
  font-size: var(--fs-sm);
  text-align: center;
  justify-content: center;
}

.feed-trial-warning[hidden] { display: none; }

.bar-note {
  display: block;
  text-align: center;
  font-size: var(--fs-sm);
  color: var(--muted);
}

.bar-note[hidden] { display: none; }

/* Email step one ------------------------------------------------------- */

.modal-check {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  font-size: var(--fs-base);
  color: var(--ink);
  cursor: pointer;
}

.modal-help {
  margin: 4px 0 0 26px;
  font-size: var(--fs-sm);
  color: var(--muted);
}

.modal-warning {
  margin: 14px 0 0;
  padding: 7px 10px;
  border-radius: 8px;
  background: #fdf3e3;
  border: 1px solid #edd9b4;
  color: #6b4a10;
  font-size: var(--fs-sm);
}

.choice-sub {
  display: block;
  font-size: var(--fs-sm);
  color: var(--muted);
}

/* Past a wide container the bar is what the mockup drew: the Filters
   button is gone and the drawer is simply open, labels row and all. The
   [hidden] override is the whole point, so the toggle's state cannot
   leave a wide screen with its chips switched off.

   LOOK DECISION TAKEN (Josh). .content is max-width 1160 with 40px of
   side padding, so the content column tops out at 1080px however wide
   the monitor is: verified in Chromium 2026-09-14, natural column width
   1080 at a 1700px viewport. A 1200px step, here and on the grid's own
   six-across step, therefore never fired in the shipped shell. Lowered
   to 1000px so both fire within the 1080px column instead of only on a
   column forced past 1200. */
@container feed (min-width: 1000px) {
  .feed-filter-toggle { display: none; }
  .feed-filter-drawer,
  .feed-filter-drawer[hidden] { display: flex; }
}

/* ======================================================================
   Console Stage B: the project header, the door strip, the project home
   (scope 2026-09-15).

   APPENDED AT THE END, which is the rule this file already states above
   .door-photos: a later block of equal specificity wins over a
   container query, so nothing here targets a bare `.door` or `.doors`.
   Every rule below is on a class the strip alone wears (.strip-door) or
   on a class that did not exist before this block, so the card grid's
   container steps are untouched.
   ====================================================================== */

.project-header {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 18px;
}

/* The header owns the page title, so the door's own heading under it is
   a section title rather than a second h1. */
.project-head {
  margin-bottom: 0;
}

.project-identity {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.project-title {
  margin: 0;
}

.project-address {
  font-size: var(--fs-sm);
  color: var(--muted);
}

.project-address[hidden] { display: none; }

.project-lines {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
}

/* The attention chip is created hidden and shown when there is
   something waiting, so it needs the guard every toggled element in
   this console needs (test/hidden-attribute.test.ts). */
.chip-attention[hidden] { display: none; }

/* The door you are standing in, under the job's name. */
.door-title {
  font-family: var(--serif);
  font-size: var(--fs-lg);
  font-weight: 400;
  color: var(--walnut);
  margin: 0;
}

/* The door strip ------------------------------------------------------
   ONE ROW, always, scrolling sideways when the column is too narrow to
   hold eight doors (scope: narrow screens scroll it horizontally). No
   container query: a row that wraps on a wide screen and scrolls on a
   narrow one is two different components to learn, and the strip's job
   is to be in the same place on every page of the job. Doors never
   shrink, so a label is never clipped mid-word. */
.door-strip {
  display: flex;
  align-items: stretch;
  gap: 7px;
  overflow-x: auto;
  /* Room for the focus ring on the first and last door, which an
     overflow container would otherwise clip. */
  padding: 3px 3px 6px;
  margin: 0 -3px;
  scrollbar-width: thin;
}

/* THE CUE THAT THERE IS MORE. A row that scrolls with no edge treatment
   reads as a row that ENDS: the door past the right edge is not hidden,
   it is invisible, which is worse. The last 28px fade, so the row
   visibly continues off the edge.

   The class is toggled in project-header.ts, on mount and on resize,
   and only when scrollWidth really exceeds clientWidth, so a strip that
   fits wears no decoration pretending otherwise.

   mask-image rather than an overlaid gradient because this element is
   the scroller: a background gradient would scroll away with the
   content, and an absolutely positioned fade would need a wrapper and
   would sit over whatever passed under it. -webkit- as well, for Safari
   and for iPadOS, where a builder at a desk is as likely to be. */
.door-strip.overflowing {
  -webkit-mask-image: linear-gradient(
    to right,
    #000 0,
    #000 calc(100% - 28px),
    transparent 100%
  );
  mask-image: linear-gradient(
    to right,
    #000 0,
    #000 calc(100% - 28px),
    transparent 100%
  );
}

.strip-door {
  flex: none;
  gap: 7px;
  /* Room under the label for the current-page bar below, kept on EVERY
     door so lighting one does not move the other seven. */
  position: relative;
  padding-bottom: 9px;
}

.strip-door .door-label {
  flex: none;
}

/* The page you are on: lit, and not a link you would click twice. */
.strip-door.on {
  background: var(--brand-subtle);
  border-color: var(--slate);
  color: var(--slate-dark);
  font-weight: 600;
}

/* WHERE AM I, as a different signal from HAS OPEN WORK.
   .strip-door.on and .door.hot were the same tinted pill in the same
   palette, one step apart in border colour, so on a job with open punch
   items two doors looked alike and neither read as "this page". The bar
   underneath answers only the first question: it is a position, not a
   tint, so nothing about a count can imitate it. Cobalt, and 2px, the
   same weight the sidebar's current link carries. */
.strip-door.on::after {
  content: "";
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: 3px;
  height: 2px;
  border-radius: 1px;
  background: var(--slate);
}

.strip-door.on .kind-icon,
.strip-door.on .ui-icon {
  opacity: 1;
}

/* The job name inside a door's back link. */
.back-name {
  font-weight: 500;
}

/* The card's job name opens the job's home. */
.name-link {
  color: inherit;
  text-decoration: none;
}

.name-link:hover {
  color: var(--slate-dark);
  text-decoration: underline;
}

/* The project home ---------------------------------------------------- */

.project-home {
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.home-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.home-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.home-section-title {
  font-size: var(--fs-lg);
  font-weight: 600;
  color: var(--ink);
  margin: 0;
}

.home-section-head a.btn-quiet {
  text-decoration: none;
}

/* The home's photo strip: the Photos door's tiles at a size that fits
   twelve without becoming a second gallery. Selection and the actions
   live on the door, so these are plain links. */
.home-photostrip {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 8px;
}

.strip-tile {
  display: block;
  cursor: pointer;
}

/* Latest captures ------------------------------------------------------ */

.home-captures {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.home-capture {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: var(--card);
  color: var(--ink);
  text-decoration: none;
  min-width: 0;
}

.home-capture:hover {
  border-color: var(--brand-border);
  background: var(--brand-subtle);
}

.home-capture:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.home-capture-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
  line-height: 1.3;
}

.home-capture-title {
  font-size: var(--fs-md);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.home-capture-sub {
  font-size: var(--fs-xs);
  color: var(--muted);
}

.home-capture-kind {
  flex: none;
  font-size: var(--fs-2xs);
  color: var(--faint);
  white-space: nowrap;
}

/* The job's own footer row: who can see it, then Edit and Delete. Wraps
   rather than scrolls, because at a narrow column the two halves are
   independent controls and a builder can lose neither. */
.home-visibility {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}

/* Edit and Delete sit to the RIGHT, away from the visibility toggle:
   the same row, but not the same gesture. */
.home-visibility .card-manage-group {
  margin-left: auto;
}

/* The card's photo strip (Stage B) -------------------------------------
   The Photos row keeps its place, its link and its count, and grows
   pictures. Tinted like the hot punch tile when there is something
   behind it, quiet when there is not.

   .card-photostrip only, never a bare .door: the card's door grid sizes
   itself with container queries further up this file, and a later rule
   of equal specificity would win over them. */
.card-photostrip {
  align-items: center;
  gap: 8px;
  /* The thumbs set the height; the text row's padding would leave the
     pictures floating in it. */
  padding: 5px 9px;
  transition:
    background-color 100ms ease,
    box-shadow 100ms ease;
}

/* THE ROW IS A LINK, and at rest it does not look like one: it wears
   the same tint .door.hot wears, so the pictures read as decoration on
   a card rather than as a door you can open. The pointer gets a lift
   the tint cannot be confused with, since the tint is already spent
   saying "there is something behind this". Deeper than .door:hover's
   border change on purpose: there are five pictures in the way of
   noticing a 1px border. */
.card-photostrip:hover {
  background: var(--card);
  border-color: var(--slate);
  box-shadow: 0 1px 6px rgba(17, 74, 130, 0.16);
}

/* AS MANY AS FIT, never fewer than three. The thumbs do not shrink, so
   a narrow card clips the tail of the row rather than squeezing five
   pictures into postage stamps; at 44px a card has to be under about
   190px wide before the third one goes, which the grid's own
   minmax(min(100%, 460px)) never allows. */
.strip-thumbs {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 1;
  min-width: 0;
  overflow: hidden;
}

.strip-thumb {
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 6px;
  /* The same square centre crop the gallery thumb already is, so a
     strip tile and a grid tile show the same picture. */
  object-fit: cover;
  background: var(--rule-subtle);
  user-select: none;
}
