.btn {
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
padding: 8px 16px; border-radius: 6px; font-size: 13px; font-weight: 500;
border: 1px solid transparent; height: 34px;
}
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-primary { background: var(--accent); color: white; }
.btn-primary:hover:not(:disabled) { background: var(--accent-hover); }
.btn-secondary { background: var(--bg-surface); color: var(--text); border-color: var(--border); }
.btn-secondary:hover:not(:disabled) { background: var(--bg-hover); }
.btn-ghost { color: var(--text-muted); }
.btn-ghost:hover:not(:disabled) { background: var(--bg-hover); color: var(--text); }
.btn-danger { background: var(--danger); color: white; }
.btn-danger:hover:not(:disabled) { filter: brightness(0.92); }
.btn-icon { width: 34px; padding: 0; }
.btn-sm { height: 28px; padding: 4px 12px; font-size: 12px; }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ============================================================
   ICON BUTTON + ROW ACTIONS
   ------------------------------------------------------------
   One implementation for every trailing icon action (chat history, task rows,
   admin rows, share rows, gallery tiles). Rules this exists to enforce:

     * The icon is an 18px SVG with a fixed box. Text glyphs (a pencil, a bin, an
       x) have a platform-dependent ink box that can paint outside its own button
       and land on the control next to it - which is how the chat-history rename
       and delete icons came to overlap.
     * The hit area comes from --hit (32px on a mouse, 44px on a touch device),
       not from the glyph, so the target is the same on every platform.
     * The actions never shrink, and the row clips: a long title truncates
       instead of pushing the actions out of the container.
   Every icon button MUST also carry an aria-label; `title` alone is not an
   accessible name and never appears on touch.
   ============================================================ */
.icon-btn,
.ai-conv-icon {
  display: inline-grid; place-items: center;
  width: var(--hit); height: var(--hit); min-width: var(--hit);
  padding: 0; border: none; border-radius: 8px; background: none;
  color: var(--text-muted); cursor: pointer; flex: 0 0 auto;
}
.icon-btn svg,
.ai-conv-icon svg { width: 18px; height: 18px; display: block; }
.icon-btn:hover,
.ai-conv-icon:hover { background: var(--bg-hover); color: var(--text); }
.icon-btn:focus-visible,
.ai-conv-icon:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.icon-btn-danger:hover,
.ai-conv-icon-danger:hover { color: var(--danger); background: var(--danger-subtle); }
.row-actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; flex: 0 0 auto; }
/* Inside a conversation row the two actions must never wrap onto a second line
   (the row clips, so a wrapped button would be invisible). */
.ai-conv-item .row-actions { flex-wrap: nowrap; }
.field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 16px; }
.field-label { font-size: 12px; font-weight: 500; color: var(--text-muted); }
/* Body-copy-sized help text, so it must meet AA: --text-muted (5.2:1) rather
   than --text-subtle (3.4:1, decorative only). */
.field-help { font-size: 11.5px; color: var(--text-muted); }

/* WHICH AI MODEL DID THIS. One muted line under an AI result, so the model that actually
   answered is visible without competing with the result itself. A router answers with a
   model the app never named, and a model that keeps returning nothing is worth spotting. */
.ai-model-note {
  font-size: 11.5px; color: var(--text-muted); margin-top: 4px;
  overflow-wrap: anywhere;
}
.chat-progress-models { margin-top: 4px; }
.chat-progress-models .chat-progress-step-label { font-family: var(--font-mono, monospace); font-size: 11px; }
/* Field-level error text. Without this rule the class rendered as plain body
   text, so a failed save was indistinguishable from a hint. */
.field-error { display: block; font-size: 12px; color: var(--danger); margin-top: 4px; }
/* The .input class is the explicit styling hook. It exists because a type-keyed
   selector cannot match <input> with no type attribute - which is how two admin
   fields and one sharing field ended up unstyled. type="search" is deliberately
   NOT added to the list below: it would out-specify the class-based
   .task-filter-search rule and silently restyle the Tasks filters. */
.input,
input[type="text"], input[type="number"], input[type="date"],
input[type="url"], input[type="email"], input[type="password"],
textarea, select {
width: 100%; padding: 8px 12px; border: 1px solid var(--border);
border-radius: 6px; background: var(--bg-surface); font-size: 13px;
}
.input:focus,
input:focus, textarea:focus, select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-subtle); }
textarea { resize: vertical; min-height: 70px; }
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; }
.form-section { margin-bottom: 24px; padding-bottom: 24px; border-bottom: 1px solid var(--border); }
.form-section:last-child { border-bottom: none; }
.form-section-title { font-size: 13px; font-weight: 600; margin-bottom: 16px; }
.card { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; }
.card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.card-title { font-size: 14px; font-weight: 600; }
.item-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
.item-card {
background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius);
overflow: hidden; cursor: pointer; display: flex; flex-direction: column;
}
.item-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow); }
.item-card-image {
aspect-ratio: 1; background: var(--bg-muted); display: grid; place-items: center;
color: var(--text-subtle); overflow: hidden;
}
.item-card-image img { width: 100%; height: 100%; object-fit: cover; }
/* Display image (item details page + item form) */
.item-detail-image img {
  width: 100%; max-height: 380px; object-fit: contain; display: block;
  background: var(--bg-muted); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.display-image-row { display: flex; gap: 14px; align-items: flex-start; flex-wrap: wrap; }
.display-image-preview {
  width: 140px; height: 140px; object-fit: cover; display: block;
  background: var(--bg-muted); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.display-image-preview.empty {
  display: grid; place-items: center; color: var(--text-subtle); font-size: 12px;
}
.display-image-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.item-card-body { padding: 12px; flex: 1; display: flex; flex-direction: column; gap: 4px; }
.item-card-title { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.item-card-meta { font-size: 11px; color: var(--text-muted); display: flex; gap: 8px; flex-wrap: wrap; }
.tag { display: inline-flex; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 500; background: var(--bg-muted); color: var(--text-muted); }
.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-surface); }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th { text-align: left; padding: 12px 16px; font-weight: 600; font-size: 12px; color: var(--text-muted); background: var(--bg-elevated); border-bottom: 1px solid var(--border); }
td { padding: 12px 16px; border-bottom: 1px solid var(--border); }
tr:last-child td { border-bottom: none; }
tr:hover td { background: var(--bg-hover); }
.modal-overlay {
position: fixed; inset: 0; background: rgba(0,0,0,0.5); backdrop-filter: blur(4px);
display: grid; place-items: center; z-index: 100; padding: 16px;
}
.modal {
background: var(--bg-surface); border-radius: var(--radius-lg); box-shadow: var(--shadow);
max-width: min(640px, 95vw); max-height: 90vh; width: 100%; display: flex; flex-direction: column;
}
.modal.lg { max-width: min(900px, 95vw); }
.modal-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--border); }
.modal-title { font-size: 15px; font-weight: 600; }
.modal-body { padding: 20px; overflow-y: auto; flex: 1; }
.modal-footer { display: flex; justify-content: flex-end; gap: 8px; padding: 16px 20px; border-top: 1px solid var(--border); background: var(--bg-elevated); }
/* Share dialog: the QR image and the URL had no styling at all, so they rendered
   at their intrinsic size and the URL was unreadable when it wrapped. */
.qr-share-qr {
  display: block; width: 180px; height: 180px; margin: 0 auto 12px;
  background: var(--bg-muted); border: 1px solid var(--border); border-radius: var(--radius-sm);
  image-rendering: pixelated;
}
.qr-share-qr-failed { display: grid; place-items: center; font-size: 12px; color: var(--text-subtle); }
.qr-share-label { font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-subtle); margin: 12px 0 4px; }
.qr-share-link {
  font-family: var(--font-mono); font-size: 12px; color: var(--text-muted);
  background: var(--bg-muted); border-radius: var(--radius-sm); padding: 8px 10px;
  overflow-wrap: anywhere;
}
.qr-share-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.toasts { position: fixed; top: 20px; right: 20px; z-index: 200; display: flex; flex-direction: column; gap: 8px; max-width: 380px; }
.toast { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 16px; box-shadow: var(--shadow); }
.toast.success { border-left: 3px solid var(--success); }
.toast.error { border-left: 3px solid var(--danger); }
.toast.warning { border-left: 3px solid var(--warning); }
.toast.info { border-left: 3px solid var(--info); }
.empty { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 40px; color: var(--text-muted); }
.empty-title { font-size: 15px; font-weight: 600; color: var(--text); margin-bottom: 8px; }
.empty-desc { font-size: 13px; max-width: 360px; }
.page-header { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 24px; flex-wrap: wrap; }
.page-title { font-size: 1.5rem; font-weight: 600; }
.page-subtitle { color: var(--text-muted); font-size: 13px; margin-top: 4px; }
.page-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.toolbar { display: flex; align-items: center; gap: 12px; padding: 12px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 16px; flex-wrap: wrap; }
/* ============================================================
   SEARCH FIELD (one component for every search input with an icon)
   ------------------------------------------------------------
   The icon is positioned over the field, so the input MUST reserve room for it.
   The rule below is written as .search-field > input[type="text"] (0,2,1) on
   purpose: a bare `.search-field input` (0,1,1) ties with the generic input rule
   and would then depend on which stylesheet happens to load last - which is
   exactly how the header search icon came to sit on top of its own placeholder.
   The icon is aria-hidden + pointer-events:none so it is neither announced nor
   clickable, and the padding leaves a 10px gap after the 16px icon.
   ============================================================ */
.search-field { position: relative; flex: 1; min-width: 180px; }
.search-field > svg {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--text-subtle);
  pointer-events: none;
}
.search-field > input[type="text"],
.search-field > input[type="search"] {
  padding-left: 38px; background: var(--bg-elevated);
}
.toolbar-search { flex: 1; min-width: 200px; }
.tree { list-style: none; }
.tree ul { list-style: none; padding-left: 20px; margin-top: 4px; }
.tree-node { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-radius: 6px; cursor: pointer; font-size: 13px; }
.tree-node:hover { background: var(--bg-hover); }
.tree-toggle {
  display: inline-grid; place-items: center; min-width: 24px; min-height: 24px;
  background: none; border: none; cursor: pointer; font: inherit;
  color: var(--text-subtle); border-radius: var(--radius-xs);
}
.tree-toggle:hover { background: var(--bg-hover); color: var(--text); }
.tree-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tree-count { font-size: 11px; color: var(--text-subtle); background: var(--bg-muted); padding: 1px 6px; border-radius: 10px; }
.activity-item { display: flex; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.activity-icon { width: 32px; height: 32px; border-radius: 50%; background: var(--bg-muted); display: grid; place-items: center; flex-shrink: 0; }
.activity-time { font-size: 11px; color: var(--text-subtle); margin-top: 2px; }
.gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 8px; }
.gallery-item { aspect-ratio: 1; background: var(--bg-muted); border: 1px solid var(--border); border-radius: 6px; position: relative; overflow: hidden; cursor: pointer; }
.gallery-item img { width: 100%; height: 100%; object-fit: cover; }
.gallery-item-remove { position: absolute; top: 4px; right: 4px; background: rgba(0,0,0,0.6); color: white; border-radius: 50%; width: 28px; height: 28px; display: grid; place-items: center; font-size: 13px; line-height: 1; }
.gallery-item-remove:hover { background: rgba(0,0,0,0.8); }
.gallery-item-remove:focus-visible { outline: 2px solid var(--accent-contrast); outline-offset: 1px; }

/* Stage 5 / S5 — container types, physical properties, cover photos and
   placement guidance. */
.gallery-item-badge { position: absolute; top: 4px; left: 4px; background: var(--accent); color: white; font-size: 10px; padding: 2px 6px; border-radius: 10px; text-transform: uppercase; letter-spacing: 0.04em; }
.gallery-item-action { position: absolute; bottom: 4px; left: 4px; background: rgba(0,0,0,0.65); color: white; font-size: 11px; padding: 3px 8px; border-radius: 10px; }
.gallery-item-action:hover { background: rgba(0,0,0,0.85); }
.state-badge { display: inline-block; margin-left: 6px; font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; padding: 1px 6px; border-radius: 10px; border: 1px solid var(--border); }
.state-ok { color: var(--accent); border-color: var(--accent); }
.state-warn { color: var(--warning-strong); border-color: var(--warning-strong); }
.state-danger { color: var(--danger); border-color: var(--danger); }
.state-muted { color: var(--text-subtle); }
.container-properties { margin-top: 12px; }
.dimension-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.dimension-row input[type="number"] { width: 96px; }
.dimension-row select { min-width: 120px; }
.dimension-x { color: var(--text-subtle); }
.prop-value { font-weight: 500; }
.prop-unknown { color: var(--text-subtle); font-style: italic; }
.guidance-body { display: flex; flex-direction: column; gap: 10px; }
.guidance-row { display: flex; gap: 12px; align-items: flex-start; font-size: 13px; }
.guidance-label { min-width: 110px; color: var(--text-subtle); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }
.guidance-reason { flex: 1; }
.guidance-unknowns { margin: 0; padding-left: 18px; }
.guidance-unknowns li { margin-bottom: 2px; }
.guidance-candidate { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px; cursor: pointer; }
.guidance-candidate:hover { background: var(--bg-hover); }

.spinner { width: 18px; height: 18px; border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.qr-code { display: inline-block; padding: 8px; background: white; }

/* AI Chat Styles */
.ai-chat-page { display: flex; flex-direction: column; gap: 16px; }
.chat-messages { 
  flex: 1; 
  max-height: 60vh; 
  overflow-y: auto; 
  padding: 8px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.message { 
  margin-bottom: 12px; 
  max-width: 80%;
}
.message.user { margin-left: auto; }
.message.ai { margin-right: auto; }
.message-bubble {
  padding: 12px 16px;
  border-radius: 16px;
  background: var(--accent-subtle);
  color: var(--text);
  font-size: 13px;
  line-height: 1.4;
}
.message.user .message-bubble {
  background: var(--accent);
  color: white;
}
.message-time {
  font-size: 11px;
  color: var(--text-subtle);
  margin-top: 4px;
}
.chat-input-area {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 0;
}
.chat-input-area textarea {
  width: 100%;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  resize: vertical;
  min-height: 60px;
  font-size: 13px;
}
.chat-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
/* The chat dock: everything under the transcript (status, quick actions, composer).
   On a wide screen it is simply the last block of the column; on a phone it becomes
   sticky so nothing can be covered by the chat box (see the mobile block below). */
.chat-dock { display: flex; flex-direction: column; gap: 12px; }
.chat-loading {
  display: flex;
  justify-content: center;
  padding: 16px;
}
.chat-quick-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

/* AI Card Styles */
.ai-suggestion-card {
  background: var(--bg-surface);
  border-left: 3px solid var(--accent);
  padding: 12px;
  margin-bottom: 8px;
  border-radius: 0 6px 6px 0;
}

/* ============================================================
   AI: markdown, chat polish, catalog review cards, badges
   ============================================================ */

/* Markdown-rendered AI output.
   Every AI prose surface renders through App.Markdown, which produces this
   markup. Sizes stay close to the surrounding UI (13.5px base) so formatted
   answers do not dominate the page, and every block keeps its own vertical
   rhythm. Long URLs/code/words wrap instead of widening the layout. */
.markdown { font-size: 13.5px; line-height: 1.55; overflow-wrap: anywhere; word-break: break-word; min-width: 0; }
.markdown > *:first-child { margin-top: 0; }
.markdown > *:last-child { margin-bottom: 0; }
.markdown p { margin: 0 0 8px; }
.markdown p:last-child { margin-bottom: 0; }
.markdown h1, .markdown h2, .markdown h3, .markdown h4, .markdown h5, .markdown h6 {
  margin: 12px 0 6px; font-weight: 600; line-height: 1.3; overflow-wrap: anywhere;
}
.markdown h1 { font-size: 17px; } .markdown h2 { font-size: 16px; } .markdown h3 { font-size: 15px; }
.markdown h4, .markdown h5, .markdown h6 { font-size: 14px; }
.markdown h5, .markdown h6 { color: var(--text-muted); }
.markdown ul, .markdown ol { margin: 4px 0 10px; padding-left: 22px; }
.markdown li { margin: 3px 0; }
.markdown li > ul, .markdown li > ol { margin: 3px 0 0; }
.markdown li > p { margin: 0 0 4px; }
.markdown li > p:last-child { margin-bottom: 0; }
.markdown code {
  font-family: var(--font-mono); font-size: 12px;
  background: var(--bg-muted); padding: 1px 5px; border-radius: 4px;
  overflow-wrap: anywhere;
}
/* Fenced code blocks: readable monospaced block, whitespace preserved, scrolls
   on its own so a long line can never stretch the page. */
.markdown pre {
  background: var(--bg-muted); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 10px 12px; margin: 6px 0 10px;
  overflow-x: auto; max-width: 100%;
}
.markdown pre code {
  display: block; background: none; padding: 0; border-radius: 0;
  font-size: 12px; line-height: 1.45; white-space: pre; overflow-wrap: normal;
}
/* Tables: horizontal scroll inside their own box (never widens the page),
   focusable for keyboard scrolling, announced to screen readers. */
.md-table-wrap {
  overflow-x: auto; max-width: 100%; margin: 8px 0 12px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  -webkit-overflow-scrolling: touch;
}
.md-table-wrap:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.markdown table { border-collapse: collapse; width: auto; min-width: 100%; font-size: 12.5px; }
.markdown th, .markdown td { border: 1px solid var(--border); padding: 6px 10px; text-align: left; vertical-align: top; }
.markdown th { background: var(--bg-muted); font-weight: 600; white-space: nowrap; }
.markdown td { overflow-wrap: anywhere; }
.markdown blockquote {
  margin: 6px 0; padding: 4px 12px; border-left: 3px solid var(--border-strong);
  color: var(--text-muted);
}
.markdown blockquote > *:last-child { margin-bottom: 0; }
.markdown hr { border: none; border-top: 1px solid var(--border); margin: 12px 0; }
.markdown a { color: var(--accent); text-decoration: underline; overflow-wrap: anywhere; }
.markdown strong { font-weight: 600; }
.markdown del { color: var(--text-subtle); }
.markdown sup, .markdown sub { font-size: 10.5px; }

/* Plain-text blocks (stored descriptions/notes, provider error text): escaped
   text with the author's line breaks, never interpreted as markup. */
.prose, .storage-notes-body, .scan-error-text { overflow-wrap: anywhere; word-break: break-word; }
.prose { font-size: 13px; line-height: 1.5; }

/* Narrow screens: keep formatted AI content compact and scrollable. */
@media (max-width: 700px) {
  .markdown h1 { font-size: 16px; } .markdown h2 { font-size: 15px; } .markdown h3 { font-size: 14px; }
  .markdown pre { padding: 8px 10px; }
  .markdown pre code { font-size: 11.5px; }
  .markdown table { font-size: 12px; }
  .markdown th, .markdown td { padding: 5px 8px; }
}

/* Collapsible reasoning */
.ai-reasoning { margin-top: 8px; font-size: 12px; color: var(--text-muted); }
.ai-reasoning > summary { cursor: pointer; color: var(--text-subtle); user-select: none; padding: 2px 0; }
.ai-reasoning > summary:hover { color: var(--text-muted); }
.ai-reasoning .markdown { font-size: 12px; margin-top: 6px; padding-left: 10px; border-left: 2px solid var(--border); }

/* Insights + uncertainty + saved */
.ai-insights { padding: 4px 2px; }
.ai-uncertain {
  font-size: 12px; color: var(--warning-strong); background: var(--warning-subtle);
  padding: 6px 10px; border-radius: 6px; margin-bottom: 12px;
}
.saved-pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 500; color: var(--success-strong);
  background: var(--success-subtle); padding: 6px 12px; border-radius: 6px;
}

/* Chat: status bar + loading */
.ai-status-bar { display: flex; gap: 8px; flex-wrap: wrap; }
.ai-chip { font-size: 11px; padding: 3px 10px; border-radius: 20px; background: var(--bg-muted); color: var(--text-muted); }
.ai-chip.ok { background: var(--success-subtle); color: var(--success-strong); }
.ai-chip.off { background: var(--bg-muted); color: var(--text-subtle); }
.chat-loading { display: flex; align-items: center; gap: 10px; justify-content: center; padding: 10px; color: var(--text-muted); }
.chat-loading-text { font-size: 12px; }

/* Chat: messages with avatars */
.chat-messages { display: flex; flex-direction: column; gap: 14px; }
.message { display: flex; gap: 10px; max-width: 100%; align-items: flex-start; }
.message.user { flex-direction: row-reverse; }
.message-avatar {
  flex-shrink: 0; width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center; font-size: 11px; font-weight: 600;
  background: var(--bg-muted); color: var(--text-muted);
}
.message.ai .message-avatar { background: var(--accent-subtle); color: var(--accent); }
.message-stack { display: flex; flex-direction: column; max-width: 92%; }
.message.user .message-stack { align-items: flex-end; }
.message-bubble {
  padding: 10px 14px; border-radius: 14px; font-size: 13.5px; line-height: 1.5;
  background: var(--bg-elevated); border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}
.message.user .message-bubble { background: var(--accent); color: #fff; border-color: transparent; }
.message.user .user-content { white-space: pre-wrap; }
.message .ai-error { color: var(--danger); }
.message-time { font-size: 10.5px; color: var(--text-subtle); margin-top: 4px; padding: 0 4px; }
.chat-empty { text-align: center; padding: 40px 20px; color: var(--text-muted); }
.chat-empty-icon { font-size: 28px; color: var(--accent); margin-bottom: 8px; }
.chat-empty-title { font-size: 15px; font-weight: 600; color: var(--text); margin-bottom: 4px; }
.chat-empty-sub { font-size: 12.5px; }
.chat-hint { font-size: 11px; color: var(--text-subtle); align-self: center; }

/* Inline draft cards inside chat */
.ai-draft-cards { margin-top: 10px; display: flex; flex-direction: column; gap: 12px; }

/* Records the assistant actually looked up (Stage 5 / S2): real, clickable
   cards with a thumbnail and the canonical app route. */
.ai-ref-label {
  margin-top: 10px; font-size: 10.5px; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text-subtle);
}
.ai-ref-note {
  margin-top: 8px; font-size: 12px; color: var(--text-muted);
  border-left: 3px solid var(--warning, var(--accent)); padding: 4px 10px;
  background: var(--bg-muted); border-radius: var(--radius-sm, 6px);
}
.ai-ref-cards {
  margin-top: 8px; max-width: 100%;
  /* Two tiles per row at every width: the tiles are small enough for a phone and
     the block stays a predictable height. */
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px;
}
/* A long list shows two records until the user opens it. The rest stay in the
   document (so an export, a screen reader and a test see everything) and are only
   hidden from the eye. */
.ai-ref-block { margin-top: 8px; }
.ai-ref-block:not(.expanded) .ai-ref-cards > *:nth-child(n+3) { display: none; }
/* Opened, the list is a FIXED area of about eight tiles and scrolls, so a dozen
   records can never push the answer off the screen. */
.ai-ref-block.expanded .ai-ref-cards {
  max-height: calc(4 * 48px + 3 * 8px);
  overflow-y: auto; overscroll-behavior: contain; padding-right: 2px;
}
.ai-ref-block.expanded .ai-ref-cards:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ai-ref-more { margin-top: 6px; font-size: 12px; padding: 4px 10px; }
.ai-ref-link {
  text-decoration: none; color: inherit; display: block;
  border-radius: var(--radius); outline-offset: 2px;
}
.ai-ref-link:focus-visible { outline: 2px solid var(--accent); }
/* An entry is the link plus, for an item, its "Details" button: two separate
   targets, so a click can never both open the record and open the overlay. The
   button is laid OVER the tile's bottom-right corner (it is a sibling, never
   nested), which keeps every tile exactly the same height. */
.ai-ref-entry { position: relative; display: block; min-width: 0; }
.ai-ref-entry .ai-ref-tile-body { padding-right: 44px; }
.ai-ref-details {
  position: absolute; right: 3px; bottom: 3px; z-index: 1;
  font-size: 10px; line-height: 1.2; padding: 2px 7px;
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-pill);
}

/* THE compact record tile. One look for a record everywhere it is mentioned: the
   chat, the photo-catalogue screen and the bulk review. The image slot is a FIXED
   square that is reserved whether or not a photo exists, so a record without one -
   or with a photo that fails to load - never changes the shape of the row.

   The tile is deliberately SMALL and FIXED in both directions: two fit side by side
   in an answer without pushing the text off the screen, and a dozen of them are a
   small, scrollable block instead of a wall of cards. */
.ai-ref-tile {
  --tile-size: 28px;
  --tile-height: 48px;
  position: relative;
  display: flex; align-items: center; gap: 6px;
  min-width: 0; width: 100%; height: var(--tile-height);
  padding: 4px 6px;
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-sm);
  transition: border-color var(--duration-fast) var(--ease-standard);
}
.ai-ref-link:hover .ai-ref-tile, .ai-ref-link:focus-visible .ai-ref-tile { border-color: var(--accent); }
.ai-ref-tile-media {
  flex: 0 0 auto; width: var(--tile-size); height: var(--tile-size);
  display: grid; place-items: center; overflow: hidden;
  background: var(--bg-muted); border-radius: var(--radius-sm);
  border: 1px solid var(--border);
}
/* `.empty` is the shared empty-STATE block (`padding: 40px`, a column of text), and this
   slot is a fixed square. Without this override the padding grew the box to 82x82 - 34px
   taller than the tile it lives in - so the "No photo" placeholder spilled over the rows
   above and below it. Every other fixed slot in this file (.display-image-preview,
   .item-panel-photos, .value-top-img, .rescan-thumb) already carries the same override for
   the same reason; this one was missing it. The square and its flex-shrink: 0 come from the
   rule above and are deliberately NOT restated here. */
.ai-ref-tile-media.empty { display: grid; place-items: center; padding: 0; }
.ai-ref-tile-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ai-ref-tile-noimg {
  font-size: 9px; line-height: 1.15; text-align: center;
  color: var(--text-subtle); padding: 0 1px;
  /* At most TWO lines, inside the square: "No photo" fits on two, and a longer label
     ("Photo unavailable", after a failed load) is clamped with an ellipsis instead of
     spilling out of the box - the full message belongs in the Details overlay, where
     there is room for it. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; max-width: 100%;
}
/* No manual retry button inside the compact square: a button cannot fit in 28px, and the
   tile already re-attempts on its own and again when the app is resumed. The full-size
   photo - and its retry - live in the Details overlay, where there is room for both. */
.ai-ref-tile-media .img-retry { display: none; }
.ai-ref-tile-body { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.ai-ref-tile-name {
  font-size: 12px; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ai-ref-tile-meta {
  font-size: 10px; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* A badge would take the room the name needs, so in the compact tile it is a small
   corner dot that explains itself on hover - and keeps its text for a screen
   reader. */
.ai-ref-tile-badge {
  position: absolute; top: 3px; right: 3px;
  width: 6px; height: 6px; padding: 0; overflow: hidden;
  font-size: 0; border: 0; border-radius: 50%;
  background: var(--warning-strong, var(--accent));
}

/* Persistent conversations (Stage 5 / S3): a list beside the transcript. */
.ai-chat-layout { display: grid; grid-template-columns: minmax(220px, 280px) minmax(0, 1fr); gap: 16px; align-items: start; }
.ai-chat-main { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.ai-conv-panel {
  display: flex; flex-direction: column; gap: 10px; padding: 12px;
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-sm);
}
.ai-conv-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ai-conv-title { font-size: 12px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-subtle); }
.ai-conv-retention { display: flex; flex-direction: column; gap: 4px; }
.ai-conv-retention-label { font-size: 11.5px; color: var(--text-muted); }
.ai-conv-retention-select {
  width: 100%; padding: 6px 8px; font-size: 12.5px; border-radius: var(--radius-sm, 6px);
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
}
.ai-conv-hint { font-size: 10.5px; line-height: 1.45; color: var(--text-subtle); }
/* The list scrolls once there are more conversations than fit. `max-height` +
   `overflow-y: auto` alone were NOT enough: `.ai-conv-item` carries
   `overflow: hidden`, and an overflow-hidden flex item's automatic minimum size is
   0 - so instead of overflowing, every row was SHRUNK to fit the box and the list
   never scrolled at all (14 conversations were crushed into ~24px rows on a desktop
   and ~6px slivers in the phone drawer, with the text clipped). `flex: 0 0 auto`
   pins each row to its own height, so the overflow is real and the list scrolls.

   The two action chips float in each tile's top-right corner, so the width they
   occupy is computed ONCE here and used by the tile, the title and the chips - they
   cannot drift apart. `--conv-gap` is what keeps two TOUCH targets from overlapping:
   the chips are 28px but the target is --hit (32px on a mouse, 44px on a touch
   device), so the gap between them is the difference - 4px on a mouse (as designed)
   and 16px on a phone, never a negative space where a tap could hit the wrong one. */
.ai-conv-list {
  display: flex; flex-direction: column; gap: 0;
  max-height: 340px; overflow-y: auto; overscroll-behavior: contain;
  --conv-chip: 28px;
  --conv-gap: calc(var(--hit) - var(--conv-chip));
  --conv-actions: calc(2 * var(--hit) + var(--conv-gap));
}
/* One conversation, as a tile: its own padding, surface and radius. The row reserves
   the top-right corner for the chips, so a long title is truncated with an ellipsis
   rather than running underneath them. */
.ai-conv-item {
  position: relative;
  display: flex; align-items: center; gap: var(--space-1);
  /* Never squashed: the list scrolls instead. */
  flex: 0 0 auto;
  /* Tall enough for the chips even when the text is one short line. */
  min-height: calc(var(--hit) + 12px);
  padding: 8px 10px; margin-bottom: 8px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--bg-surface);
  overflow: hidden;
}
.ai-conv-item:last-child { margin-bottom: 0; }
.ai-conv-item:hover { background: var(--bg-hover); }
.ai-conv-item.active { background: var(--accent-subtle); border-color: var(--accent); }
.ai-conv-open {
  flex: 1 1 auto; min-width: 0; text-align: left; display: flex; flex-direction: column; gap: 2px;
  /* The top-right corner belongs to the action chips. */
  padding: 0 calc(var(--conv-actions) + 10px) 0 0;
  background: none; border: none; cursor: pointer; color: inherit;
  font: inherit; border-radius: var(--radius-sm);
}
.ai-conv-item-title {
  font-size: 12.5px; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Also one line: the row keeps a predictable height, so the list scrolls evenly
   instead of one long "12 Jan · 42 messages" wrapping and pushing the rows apart. */
.ai-conv-item-meta {
  font-size: 10.5px; color: var(--text-subtle);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The rename/delete controls are CHIPS: a solid, rounded button-style surface in the
   tile's top-right corner, so they read as controls sitting on top of the content
   instead of blending into the text.

   The geometry is deliberate. The button BOX stays --hit (32px with a mouse, 44px on
   a touch device), because that is this application's documented touch target - but
   `background-clip: content-box` paints only the inner 28px, so what the user SEES is
   a compact chip while what they can TAP is the full target. The radius is offset by
   the same padding, so the painted chip keeps the 6px corner on both platforms. */
.ai-conv-item .row-actions {
  position: absolute; top: 6px; right: 6px; z-index: 2;
  display: flex; flex-wrap: nowrap; gap: var(--conv-gap);
  width: var(--conv-actions); justify-content: flex-end;
}
.ai-conv-item .row-actions .ai-conv-icon {
  width: var(--hit); height: var(--hit); min-width: var(--hit);
  padding: calc((var(--hit) - var(--conv-chip)) / 2);
  border-radius: calc(6px + (var(--hit) - var(--conv-chip)) / 2);
  background-color: var(--bg-elevated);
  background-clip: content-box;
  color: var(--text-muted);
}
.ai-conv-item .row-actions .ai-conv-icon:hover { background-color: var(--bg-hover); color: var(--text); }
.ai-conv-item .row-actions .ai-conv-icon-danger:hover { background-color: var(--danger-subtle); color: var(--danger); }
/* The rename/delete button geometry comes from the shared .icon-btn block above:
   a fixed --hit box around an 18px SVG, so the two controls can never overlap
   each other or the title, and neither is a bare glyph. */
.ai-conv-loading, .ai-conv-empty { font-size: 12px; color: var(--text-muted); display: flex; gap: 8px; align-items: center; }
.ai-conv-error { display: flex; flex-direction: column; gap: 6px; font-size: 12px; color: var(--danger); }
.ai-conv-error-detail { font-size: 11px; color: var(--text-muted); word-break: break-word; }
.ai-conv-footer { display: flex; justify-content: flex-end; }

/* A message whose save failed stays visible, with an honest retry. */
.ai-save-error {
  margin-top: 6px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 11.5px; color: var(--danger);
}
.ai-save-pending { margin-top: 4px; font-size: 10.5px; color: var(--text-subtle); }

@media (max-width: 860px) {
  .ai-chat-layout { grid-template-columns: minmax(0, 1fr); }
  .ai-conv-list { max-height: 220px; }
}

/* AI suggestion cards (Stage 5 / S4). A suggestion is NOT a task: it is an
   observation with its own evidence and its own explicit actions. */
.ai-sug-label {
  margin-top: 12px; font-size: 10.5px; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text-subtle);
}
.ai-sug-cards, .ai-sug-list { display: flex; flex-direction: column; gap: 10px; margin-top: 8px; }
.ai-sug-list { margin-top: 0; }
.ai-sug-card {
  border: 1px solid var(--border); border-left: 3px solid var(--accent);
  border-radius: var(--radius); background: var(--bg-elevated);
  box-shadow: var(--shadow-sm); padding: 10px 12px; max-width: 620px;
}
.ai-sug-card.ai-sug-dismissed { border-left-color: var(--text-subtle); opacity: 0.85; }
.ai-sug-card.ai-sug-accepted { border-left-color: var(--success, #10b981); }
.ai-sug-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.ai-sug-badge {
  font-size: 10px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 20px; background: var(--accent-subtle); color: var(--accent);
}
.ai-sug-kind { font-size: 11.5px; color: var(--text-muted); }
.ai-sug-status {
  margin-left: auto; font-size: 10.5px; padding: 2px 8px; border-radius: 20px;
  background: var(--bg-muted); color: var(--text-muted);
}
.ai-sug-status-new { background: var(--accent-subtle); color: var(--accent); }
.ai-sug-status-accepted { background: var(--success-subtle); color: var(--success-strong); }
.ai-sug-title { font-size: 13.5px; font-weight: 600; color: var(--text); }
.ai-sug-why { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
.ai-sug-evidence {
  margin-top: 6px; font-size: 12px; color: var(--text-muted);
  background: var(--bg-muted); border-radius: var(--radius-sm, 6px); padding: 6px 8px;
  display: flex; gap: 8px; flex-wrap: wrap;
}
.ai-sug-evidence-label {
  font-size: 10px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-subtle);
}
.ai-sug-link {
  display: inline-block; margin-top: 8px; font-size: 12px; color: var(--accent);
  text-decoration: none; border-bottom: 1px dotted currentColor;
}
.ai-sug-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 10px; }
.ai-sug-defer { display: none; gap: 4px; flex-wrap: wrap; margin-top: 6px; }
.ai-sug-done { font-size: 12px; color: var(--success-strong); font-weight: 600; }
.ai-sug-warn { font-size: 11.5px; color: var(--warning-strong); }
.ai-sug-deferred-note { margin-top: 6px; font-size: 11px; color: var(--text-subtle); }


/* Tasks (Stage 5 / S4). */
.tasks-page { display: flex; flex-direction: column; gap: 12px; }
.task-tabs, .task-filters, .task-bulk { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.task-summary {
  display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; color: var(--text-muted);
  padding: 8px 12px; background: var(--bg-muted); border-radius: var(--radius);
}
.task-summary-overdue { color: var(--danger); font-weight: 600; }
.task-bulk {
  padding: 8px 10px; background: var(--accent-subtle); border-radius: var(--radius);
  border: 1px solid var(--accent);
}
.task-bulk-count { font-size: 12px; font-weight: 600; color: var(--accent); margin-right: 4px; }
.task-filter-select, .task-filter-search {
  padding: 5px 8px; font-size: 12.5px; border-radius: var(--radius-sm, 6px);
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
}
.task-filter-search { min-width: 180px; }
.task-list { display: flex; flex-direction: column; gap: 6px; }
.task-row {
  display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px;
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius);
}
.task-row.completed { opacity: 0.7; }
.task-row.completed .task-title { text-decoration: line-through; }
.task-row.dismissed { opacity: 0.55; }
.task-row-lead { display: flex; align-items: center; gap: 8px; padding-top: 2px; }
.task-row-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.task-title { font-size: 13.5px; font-weight: 600; color: var(--text); cursor: pointer; }
.task-title:hover { color: var(--accent); }
.task-desc {
  font-size: 12px; color: var(--text-muted); white-space: pre-wrap;
  overflow: hidden; text-overflow: ellipsis; display: -webkit-box;
  -webkit-line-clamp: 3; -webkit-box-orient: vertical;
}
.task-meta { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-top: 2px; }
.task-badge {
  font-size: 10.5px; padding: 2px 7px; border-radius: 20px;
  background: var(--bg-muted); color: var(--text-muted); text-transform: capitalize;
}
.task-badge.task-priority-urgent { background: var(--danger-subtle, #fee2e2); color: var(--danger); }
.task-badge.task-priority-high { background: var(--warning-subtle); color: var(--warning-strong); }
.task-badge.task-from-ai { background: var(--accent-subtle); color: var(--accent); }
.task-due { font-size: 11px; color: var(--text-muted); }
.task-due.overdue { color: var(--danger); font-weight: 600; }
.task-due.today { color: var(--warning-strong); font-weight: 600; }
.task-due.soon { color: var(--accent); }
.task-link { font-size: 11.5px; color: var(--accent); text-decoration: none; border-bottom: 1px dotted currentColor; }
.task-link.archived { color: var(--text-subtle); }
.task-link.deleted { color: var(--text-subtle); text-decoration: line-through; }
.task-row-actions { display: flex; gap: 4px; flex-wrap: wrap; justify-content: flex-end; }
.task-more { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.task-error { color: var(--danger); display: flex; flex-direction: column; gap: 6px; }
.task-loading { display: flex; align-items: center; gap: 8px; color: var(--text-muted); font-size: 12.5px; }
.task-form-error { color: var(--danger); font-size: 12px; min-height: 16px; margin-top: 6px; }
.task-form-title { width: 100%; }

@media (max-width: 720px) {
  .task-row { flex-wrap: wrap; }
  .task-row-actions { width: 100%; justify-content: flex-start; }
  .task-filter-search { min-width: 100%; }
}

/* Reviewable AI item card (shared by chat + catalog) */
.ai-review-card {
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 14px; box-shadow: var(--shadow-sm);
}
.ai-review-card.saved { opacity: 0.85; border-color: var(--success); }
.ai-review-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.ai-review-title { font-size: 13px; font-weight: 600; }
.ai-review-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
.confidence-badge { font-size: 11px; font-weight: 600; padding: 3px 10px; border-radius: 20px; }
.confidence-badge.good { background: var(--success-subtle); color: var(--success-strong); }
.confidence-badge.mid { background: var(--warning-subtle); color: var(--warning-strong); }
.confidence-badge.low { background: var(--danger-subtle); color: var(--danger); }

/* AI Catalog layout */
.ai-catalog-grid { display: flex; gap: 14px; align-items: flex-start; margin-bottom: 12px; flex-wrap: wrap; }
.ai-catalog-thumb {
  width: 96px; height: 96px; border-radius: var(--radius-sm); overflow: hidden;
  border: 1px solid var(--border); background: var(--bg-muted); flex-shrink: 0;
}
.ai-catalog-thumb img { width: 100%; height: 100%; object-fit: cover; }
.ai-catalog-grid .form-grid { flex: 1; min-width: 260px; }

/* Tag chips editor */
.tag-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.tag {
  display: inline-flex; align-items: center; gap: 6px; padding: 3px 8px;
  border-radius: 12px; font-size: 11.5px; font-weight: 500;
  background: var(--accent-subtle); color: var(--accent);
}
.tag-remove {
  border: none; background: none; color: inherit; cursor: pointer; line-height: 1;
  display: inline-grid; place-items: center; min-width: 18px; min-height: 18px;
  padding: 1px 3px; border-radius: var(--radius-xs); opacity: 0.75;
}
.tag-remove:hover { opacity: 1; background: var(--bg-hover); }
.tag-remove:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tag-add { background: var(--bg-muted); color: var(--text-muted); cursor: pointer; border: 1px dashed var(--border-strong); }
.tag-input { max-width: 180px; }


/* Reminders */
.reminder-filters { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.reminder-item { display: flex; align-items: flex-start; gap: 12px; padding: 12px 16px; border-bottom: 1px solid var(--border); }
.reminder-item:last-child { border-bottom: none; }
.reminder-title { font-weight: 600; font-size: 13px; }
.reminder-item.done .reminder-title { text-decoration: line-through; color: var(--text-subtle); }
.reminder-meta { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; font-size: 12px; color: var(--text-muted); margin-top: 4px; }
.reminder-item-link { color: var(--accent); cursor: pointer; }
.reminder-notes { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
.reminder-side { margin-left: auto; display: flex; flex-direction: column; align-items: flex-end; gap: 8px; flex-shrink: 0; }
.reminder-actions { display: flex; gap: 6px; }
.reminder-due { font-size: 11px; font-weight: 600; padding: 2px 10px; border-radius: 10px; background: var(--bg-muted); color: var(--text-muted); white-space: nowrap; }
.reminder-due.overdue { background: var(--danger-subtle); color: var(--danger); }
.reminder-due.today, .reminder-due.soon { background: var(--warning-subtle); color: var(--warning-strong); }
.reminder-due.done, .reminder-due.no-date { background: var(--bg-muted); color: var(--text-subtle); }

/* QR Labels */
.label-controls { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 16px; }
.label-size-select { font-size: 12px; color: var(--text-muted); display: flex; align-items: center; gap: 6px; }
.label-count { font-size: 12px; color: var(--text-subtle); }
.label-pick-list { padding: 8px 16px; }
.label-pick-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); }
.label-pick-row:last-child { border-bottom: none; }
.label-pick-name { font-size: 13px; font-weight: 500; }
.label-pick-sub { font-size: 11px; color: var(--text-subtle); margin-left: auto; }
.label-toolbar { display: flex; align-items: center; gap: 12px; margin: 16px 0 12px; }
.label-hint { font-size: 12px; color: var(--text-muted); }
.label-sheet { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; }
.label { border: 1px dashed var(--border-strong); border-radius: var(--radius-sm); padding: 12px; text-align: center; background: var(--bg-surface); }
.label-name { font-weight: 700; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.label-sub { font-size: 11px; color: var(--text-muted); min-height: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.label img { width: 120px; height: 120px; margin-top: 8px; image-rendering: pixelated; }
.label.size-sm img { width: 80px; height: 80px; }
.label.size-lg img { width: 160px; height: 160px; }
.label-qr-fail { font-size: 11px; color: var(--text-subtle); padding: 16px 0; }

/* Inventory Report */
.report-controls { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; margin-bottom: 16px; }
.report-photo-toggle { font-size: 13px; display: flex; align-items: center; gap: 6px; cursor: pointer; }
.report-hint { font-size: 12px; color: var(--text-muted); }
.report-head { margin-bottom: 16px; }
.report-title { font-size: 22px; font-weight: 700; }
.report-meta { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
.report-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin-bottom: 20px; }
.report-summary-item { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 14px; }
.report-summary-value { font-size: 18px; font-weight: 700; }
.report-summary-label { font-size: 11px; color: var(--text-muted); }
.report-group { margin-bottom: 24px; }
.report-group-title { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; font-size: 15px; font-weight: 700; margin-bottom: 8px; }
.report-group-total { font-size: 12px; font-weight: 500; color: var(--text-muted); }
.report-sub { font-size: 11px; color: var(--text-subtle); }
.report-thumb { width: 44px; height: 44px; object-fit: cover; border-radius: var(--radius-sm); display: block; }
/* Interactive report cells. The affordance is a real button, so it is reachable
   by keyboard and touch, but it is styled as the report's own text/photo: the
   printed page gains nothing (see the print guards in layout.css). */
.report-link {
  display: inline; padding: 0; border: none; background: none; font: inherit;
  color: inherit; text-align: left; cursor: pointer;
  text-decoration: underline; text-decoration-color: var(--border-strong);
  text-underline-offset: 2px; border-radius: var(--radius-xs);
}
.report-link:hover { color: var(--accent); text-decoration-color: var(--accent); }
.report-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.report-photo-btn {
  display: block; padding: 0; border: none; background: none; cursor: zoom-in;
  border-radius: var(--radius-sm); line-height: 0;
}
.report-photo-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.report-thumb-empty {
  display: grid; place-items: center; width: 44px; height: 44px;
  background: var(--bg-muted); border-radius: var(--radius-sm);
  font-size: 10px; color: var(--text-subtle); line-height: 1.2; text-align: center;
}

/* ============================================================
   ITEM DETAILS PANEL (shared)
   ------------------------------------------------------------
   One panel for the Inventory Report and the AI chat: photos plus the facts the
   record already holds. Rendered inside App.UI.openOverlay, so Escape, focus
   handling, the backdrop and scroll locking come from that one place.
   ============================================================ */
.item-panel { display: flex; flex-direction: column; gap: var(--space-4); }
.item-panel-photos { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.item-panel-photos.empty { display: block; }
.item-panel-photo {
  padding: 0; border: 1px solid var(--border); background: var(--bg-muted);
  border-radius: var(--radius-sm); overflow: hidden; cursor: zoom-in; line-height: 0;
  width: 84px; height: 84px;
}
.item-panel-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.item-panel-photo:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.item-panel-photo[aria-disabled='true'] { cursor: default; }
.item-panel-noimg {
  display: grid; place-items: center; width: 84px; height: 84px; padding: 4px;
  background: var(--bg-muted); border-radius: var(--radius-sm);
  font-size: 11px; color: var(--text-subtle); text-align: center;
}
.item-panel-fields {
  display: grid; grid-template-columns: minmax(90px, 130px) 1fr;
  gap: var(--space-2) var(--space-3); margin: 0;
}
.item-panel-field { display: contents; }
.item-panel-field dt { color: var(--text-muted); font-size: 12px; font-weight: 500; }
.item-panel-field dd { margin: 0; font-size: 13px; overflow-wrap: anywhere; }
@media (max-width: 480px) {
  .item-panel-fields { grid-template-columns: 1fr; gap: 2px 0; }
  .item-panel-field dt { margin-top: 8px; }
}
.report-group-subtotal { font-weight: 700; text-align: right; }
.report-foot { font-weight: 700; font-size: 14px; margin-top: 8px; padding-top: 12px; border-top: 2px solid var(--border-strong); }

/* Values */
.value-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 16px; }
.value-stat-value { font-size: 28px; font-weight: 600; }
.value-stat-value.warn { color: var(--danger); }
.value-stat-label { font-size: 12px; color: var(--text-muted); }
.value-nudge { margin-bottom: 16px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.value-nudge-text { flex: 1; font-size: 13px; min-width: 200px; }
.value-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px; margin-bottom: 16px; }
.value-empty { color: var(--text-muted); }
.value-bar-row { margin-bottom: 12px; }
.value-bar-row:last-child { margin-bottom: 0; }
.value-bar-label { display: flex; justify-content: space-between; gap: 8px; font-size: 12px; margin-bottom: 4px; }
.value-bar-value { font-weight: 600; white-space: nowrap; }
.value-bar-track { height: 8px; background: var(--bg-muted); border-radius: 4px; overflow: hidden; }
.value-bar-fill { height: 100%; border-radius: 4px; }
.value-top-item { display: flex; gap: 12px; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--border); }
.value-top-item:last-child { border-bottom: none; }
.value-top-rank { font-size: 12px; color: var(--text-subtle); width: 16px; text-align: center; flex-shrink: 0; }
.value-top-img { width: 40px; height: 40px; border-radius: var(--radius-sm); object-fit: cover; background: var(--bg-muted); flex-shrink: 0; }
.value-top-img.empty { display: grid; place-items: center; font-size: 11px; color: var(--text-subtle); }
.value-top-body { flex: 1; min-width: 0; }
.value-top-name { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.value-top-meta { font-size: 11px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.value-top-value { font-weight: 700; font-size: 13px; white-space: nowrap; }

/* Degraded-storage notice */
.app-banner {
  position: fixed; left: 50%; bottom: 16px; transform: translateX(-50%);
  max-width: min(720px, 92vw); display: flex; align-items: center; gap: 12px;
  padding: 10px 14px; border-radius: var(--radius); z-index: 200;
  background: var(--warning-subtle); color: var(--text);
  border: 1px solid var(--warning); font-size: 12.5px; box-shadow: var(--shadow);
}


/* ============================================================
   Stage 2: authentication screens + account chrome
   ============================================================ */
.app.signed-out { grid-template-columns: 1fr; }
.app.signed-out .sidebar { display: none; }
.auth-page { max-width: 420px; margin: 40px auto; }
.auth-card {
  background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 28px; box-shadow: var(--shadow); display: flex; flex-direction: column; gap: 14px;
}
.auth-title { font-size: 20px; font-weight: 600; }
.auth-subtitle { font-size: 13px; color: var(--text-muted); margin-top: -8px; }
.auth-field { display: flex; flex-direction: column; gap: 6px; }
.auth-field label { font-size: 12px; color: var(--text-muted); font-weight: 500; }
.auth-field input { padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-elevated); }
.auth-field input:focus { outline: none; border-color: var(--accent); }
.auth-submit { margin-top: 4px; }
.auth-error { background: var(--danger-subtle); color: var(--danger); border-radius: var(--radius-sm); padding: 8px 10px; font-size: 12px; }
.auth-switch { font-size: 12px; color: var(--text-muted); text-align: center; }
.auth-note { color: var(--text-subtle); }
.user-menu { display: flex; align-items: center; gap: 8px; margin-left: 8px; padding-left: 8px; border-left: 1px solid var(--border); }
.user-menu .user-email { font-size: 12px; color: var(--text-muted); max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-menu .user-inventory { font-size: 11px; color: var(--text-subtle); }
@media print { .user-menu { display: none !important; } }

/* ============================================================
   STORAGE HIERARCHY (locations, containers, storage context)
   ============================================================ */
.breadcrumbs { font-size: 12px; color: var(--text-muted); margin-bottom: 8px; display: flex; flex-wrap: wrap; gap: 2px; }
.breadcrumbs a { color: var(--accent); text-decoration: none; }
.breadcrumbs a:hover { text-decoration: underline; }

.storage-layout { display: grid; grid-template-columns: minmax(240px, 320px) 1fr; gap: 16px; align-items: start; }
@media (max-width: 900px) { .storage-layout { grid-template-columns: 1fr; } }
.storage-tree-card { padding: 12px; max-height: 70vh; overflow: auto; }
.tree-label { flex: 1; }
.tree-node.active { background: var(--bg-muted, rgba(13, 115, 119, 0.08)); border-radius: var(--radius-sm); }
.storage-detail-panel { display: flex; flex-direction: column; gap: 16px; }

.storage-name { cursor: pointer; font-weight: 500; }
.storage-name:hover { text-decoration: underline; }
.storage-sub { display: block; font-size: 11px; color: var(--text-muted); }
.storage-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 6px 0; border-bottom: 1px solid var(--border); cursor: pointer; }
.storage-row:last-child { border-bottom: none; }
.storage-group-title { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); margin: 12px 0 6px; }
.storage-group-sub { display: flex; justify-content: space-between; align-items: center; gap: 8px; font-size: 12px; color: var(--text-muted); margin: 6px 0 4px; }
.storage-description { font-size: 13px; line-height: 1.5; margin-bottom: 8px; }
.storage-notes { font-size: 12px; color: var(--text-muted); }
.storage-details { display: grid; grid-template-columns: 150px 1fr; gap: 8px 12px; font-size: 13px; margin: 0; }
.storage-detail-row { display: contents; }
.storage-detail-row dt { color: var(--text-muted); font-weight: 500; }
.storage-detail-row dd { margin: 0; }
.storage-hint { margin-top: 8px; }
.storage-warning { font-size: 12px; color: var(--danger); margin-top: 6px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }

/* Item thumbnail storage overlay */
.item-card-storage { position: absolute; left: 0; right: 0; bottom: 0; display: flex; flex-direction: column; gap: 1px; padding: 5px 7px; font-size: 11px; line-height: 1.25; color: #fff; background: linear-gradient(to top, rgba(0, 0, 0, 0.78), rgba(0, 0, 0, 0.35) 70%, rgba(0, 0, 0, 0)); }
.item-card-storage.empty { background: none; color: var(--text-subtle); font-style: italic; }
.item-card-storage-container { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.item-card-storage-location { opacity: .85; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.item-card-image { position: relative; }
.item-card-noimg { display: grid; place-items: center; height: 100%; min-height: 96px; font-size: 11px; color: var(--text-subtle); }

/* Toolbar / filter bar */
.filter-bar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 12px 0; }
.filter-bar select { padding: 6px 8px; font-size: 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-elevated); max-width: 220px; }
.filter-summary { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
/* .row-actions is defined once, with the icon-button block at the top of this
   file. A second definition here would silently win on gap/wrap. */
.tag-badge { display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 999px; font-size: 10px; text-transform: uppercase; letter-spacing: .04em; background: var(--bg-muted, rgba(0, 0, 0, .06)); color: var(--text-muted); }
.tag-badge.warn { background: var(--danger-subtle); color: var(--danger); }
.category-swatch { display: inline-block; width: 10px; height: 10px; border-radius: 3px; }
.suggestion-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; font-size: 12px; padding: 6px 0; border-bottom: 1px solid var(--border); }
.suggestion-row:last-child { border-bottom: none; }
.checkbox-row { display: flex; align-items: flex-start; gap: 8px; font-size: 12px; color: var(--text-muted); }
.gallery { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }

/* ============================================================
   HIERARCHY PICKER (searchable nested combobox)
   ============================================================ */
.hpick { position: relative; }
.hpick-field { display: flex; align-items: center; gap: 4px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-elevated); }
.hpick-input { flex: 1; border: none; background: transparent; padding: 8px 10px; font-size: 13px; min-width: 0; }
.hpick-input:focus { outline: none; }
.hpick-toggle { border: none; background: transparent; cursor: pointer; color: var(--text-muted); padding: 4px 8px; }
.hpick-list { position: absolute; z-index: 40; left: 0; right: 0; top: calc(100% + 4px); max-height: 260px; overflow: auto; background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: 0 10px 24px rgba(0, 0, 0, .12); display: none; }
.hpick.open .hpick-list { display: block; }
.hpick-option { display: flex; align-items: center; gap: 6px; width: 100%; text-align: left; padding: 6px 10px; background: none; border: none; cursor: pointer; font-size: 12px; color: var(--text); }
.hpick-option:hover { background: var(--bg-muted, rgba(0, 0, 0, .05)); }
.hpick-option.selected { font-weight: 600; }
.hpick-option.disabled { opacity: .45; cursor: not-allowed; }
.hpick-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hpick-path { margin-left: auto; font-size: 11px; color: var(--text-subtle); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 45%; }
.hpick-empty { padding: 8px 10px; font-size: 12px; color: var(--text-subtle); }

/* ============================================================
   AI SCAN (session destination, per-draft status)
   ============================================================ */
.scan-destination.set { border-left: 3px solid var(--accent); }
.scan-destination-path { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; margin-bottom: 10px; }
.scan-destination-label { font-size: 12px; color: var(--text-muted); }
.scan-destination-value { font-weight: 600; font-size: 14px; }
.scan-destination-sub { font-size: 12px; color: var(--text-muted); }
.scan-destination-pickers { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 700px) { .scan-destination-pickers { grid-template-columns: 1fr; } }
.scan-status { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.scan-status-destination { margin-left: auto; font-weight: 500; color: var(--text); }
.scan-status.importing { color: var(--text-muted); display: flex; align-items: center; gap: 8px; }
.scan-status.ok { color: var(--success-strong); font-weight: 600; }
.scan-status.warn { color: var(--warning-strong); font-weight: 600; }
.scan-status.failed { color: var(--danger); }

/* ============================================================
   PRODUCT IDENTIFICATION (scanner)
   ------------------------------------------------------------
   What an external source said, how confident it is, and what the user can do
   with it. Verified provider data and AI interpretation are styled DIFFERENTLY on
   purpose: they are not the same kind of statement.
   ============================================================ */
.scan-url {
  font-family: var(--font-mono); font-size: 12px; color: var(--text-muted);
  background: var(--bg-muted); border-radius: var(--radius-sm); padding: 8px 10px;
  margin: 8px 0; overflow-wrap: anywhere;
}
.scan-provenance { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 6px 0 10px; }
.scan-badge {
  font-size: 10px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  padding: 2px 8px; border-radius: var(--radius-pill);
}
.scan-badge-verified { background: var(--success-subtle); color: var(--success-strong); }
.scan-badge-ai { background: var(--accent-subtle); color: var(--accent); }
.scan-product-name { font-size: 15px; font-weight: 600; margin-bottom: 8px; }
.scan-product-fields {
  display: grid; grid-template-columns: minmax(90px, 130px) 1fr;
  gap: var(--space-2) var(--space-3); margin: 0 0 10px;
}
.scan-product-field { display: contents; }
.scan-product-field dt { color: var(--text-muted); font-size: 12px; font-weight: 500; }
.scan-product-field dd { margin: 0; font-size: 13px; overflow-wrap: anywhere; }
.scan-product-image { margin-bottom: 10px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.scan-providers { display: flex; flex-direction: column; gap: 4px; margin: 8px 0; }
.scan-provider {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: 12px; color: var(--text-muted);
  border-left: 3px solid var(--border-strong); padding: 2px 8px;
}
.scan-provider-ok { border-left-color: var(--success); }
.scan-provider-not-found { border-left-color: var(--border-strong); }
.scan-provider-error, .scan-provider-timeout, .scan-provider-rate-limited { border-left-color: var(--warning); }
.scan-provider-name { font-weight: 600; color: var(--text); }
.scan-provider-message { color: var(--text-subtle); }
.scan-ai { margin-top: 10px; }
.scan-ai-answer { margin-top: 10px; }
@media (max-width: 480px) {
  .scan-product-fields { grid-template-columns: 1fr; gap: 2px 0; }
  .scan-product-field dt { margin-top: 8px; }
}
.scan-card-status { margin-top: 8px; }
.scan-card-destination { margin-top: 12px; border-top: 1px dashed var(--border); padding-top: 10px; }
.scan-item-destination-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 8px; }
.scan-item-destination-label { font-size: 12px; font-weight: 600; }
.scan-empty { color: var(--text-muted); font-size: 13px; text-align: center; padding: 24px; }
.scan-placeholder { opacity: .7; }
.scan-error-card { border-left: 3px solid var(--danger); padding: 10px 12px; margin-bottom: 12px; background: var(--danger-subtle); border-radius: var(--radius-sm); }
.scan-error-title { font-weight: 600; font-size: 13px; margin-bottom: 4px; }
.scan-error-message { font-size: 12px; color: var(--danger); }

/* ============================================================
   LABELS (inline preview after container creation)
   ============================================================ */
.label-preview-inline { display: flex; justify-content: center; padding: 8px 0; }
.label-id { font-size: 9px; color: var(--text-subtle); letter-spacing: .08em; }


/* ============================================================
   SCAN + RESCAN (image rescan dialog, universal scan page)
   ============================================================ */
.rescan-preview { display: flex; justify-content: center; margin-bottom: 8px; }
.rescan-thumb { max-width: 100%; max-height: 220px; border-radius: 8px; border: 1px solid var(--border); }
.rescan-thumb.empty { display: grid; place-items: center; min-height: 90px; width: 100%; color: var(--text-subtle); font-size: 12px; background: var(--bg-muted); }
.rescan-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0; }
.rescan-replace { display: flex; align-items: flex-start; gap: 8px; font-size: 12px; color: var(--text-muted); margin-top: 8px; }
.rescan-fields { display: flex; flex-direction: column; gap: 6px; }
.rescan-field { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; padding: 6px 8px; border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: pointer; }
.rescan-field-label { font-weight: 600; font-size: 12px; min-width: 120px; }
.rescan-field-value { font-size: 13px; overflow-wrap: anywhere; }
.rescan-field-existing { font-size: 11px; color: var(--text-subtle); margin-left: auto; }
.scan-entry { display: flex; gap: 8px; flex-wrap: wrap; }
.scan-entry input { flex: 1; min-width: 200px; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-elevated); }
.scan-camera-wrap { background: #000; border-radius: var(--radius); overflow: hidden; margin-bottom: 10px; }
.scan-video { display: block; width: 100%; max-height: 340px; object-fit: cover; }
.scan-result .card-title { margin-bottom: 6px; }

/* Collapsible form sections (optional fields stay out of the way) */
.form-details > summary { cursor: pointer; list-style: none; }
.form-details > summary::-webkit-details-marker { display: none; }
.form-details > summary::before { content: '▸ '; color: var(--text-subtle); }
.form-details[open] > summary::before { content: '▾ '; }

/* Derived reminder section */
.reminder-derived { border-left: 3px solid var(--accent); }

/* The mobile menu button is only shown on narrow screens (see below). */
.sidebar-toggle { display: none; }

/* ============================================================
   STAGE 6 — scan modes, multi-photo review, add-photos, image chat
   ============================================================ */

/* Scan mode selector */
.scan-modes-card { margin-bottom: 16px; }
.scan-mode-list { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.scan-mode {
  display: flex; flex-direction: column; gap: 4px; text-align: left;
  padding: 12px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-surface); color: var(--text); cursor: pointer;
}
.scan-mode:hover { background: var(--bg-hover); }
.scan-mode.active { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-subtle); }
.scan-mode-label { font-size: 13px; font-weight: 600; }
.scan-mode-short { font-size: 11px; color: var(--text-muted); }
.scan-mode-desc { font-size: 11px; color: var(--text-subtle); }
.scan-mode-note { font-size: 11px; color: var(--text-subtle); font-style: italic; }
.scan-mode-plan { margin-top: 10px; font-size: 12px; color: var(--text-muted); }

/* Conflicts between photos (mode B) */
.scan-conflicts, .scan-matches { margin: 10px 0; }
.scan-conflicts-title, .scan-matches-title { font-size: 12px; font-weight: 600; margin-bottom: 6px; }
.scan-conflict, .scan-matches-box {
  border: 1px solid var(--warning, #b45309); border-radius: 8px;
  padding: 10px; margin-bottom: 8px; background: var(--bg-hover);
}
.scan-conflict.material { border-left: 4px solid var(--warning, #b45309); }
.scan-conflict-label { font-size: 12px; font-weight: 600; margin-bottom: 4px; }
.scan-conflict-options { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.scan-matches-box { border-color: var(--accent); }
.scan-match { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 4px 0; }
.scan-match-badge { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
.scan-match.exact .scan-match-badge { color: var(--danger); }
.scan-match.weak .scan-match-badge { color: var(--text-muted); }
.scan-match-name { font-size: 12px; font-weight: 500; }
.scan-matches-actions { margin-top: 8px; }

/* The photo strip on a multi-photo draft */
.scan-card-thumbs { margin-top: 10px; }
.scan-thumbs-label { font-size: 11px; color: var(--text-muted); margin-bottom: 4px; }
.scan-thumbs { display: flex; flex-wrap: wrap; gap: 8px; }
.scan-thumb { display: flex; flex-direction: column; gap: 2px; width: 84px; }
.scan-thumb img { width: 84px; height: 84px; object-fit: cover; border-radius: 6px; border: 1px solid var(--border); }
.scan-thumb.primary img { border-color: var(--accent); }
.scan-thumb-label { font-size: 10px; color: var(--text-subtle); overflow-wrap: anywhere; }
.scan-card-notes { margin-top: 8px; display: flex; flex-direction: column; gap: 2px; }
.scan-note { margin: 0; }

/* Side-by-side comparison of a draft and an existing record */
.scan-compare-table { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.scan-compare-row { display: grid; grid-template-columns: 1fr 1.2fr 1.2fr; gap: 8px; font-size: 12px; padding: 4px 0; border-bottom: 1px dashed var(--border); }
.scan-compare-row.head { font-weight: 600; color: var(--text-muted); }
.scan-compare-field { font-weight: 500; }
.scan-compare-value { overflow-wrap: anywhere; }

/* Add photos to an existing item */
.photo-add-files, .photo-add-status { margin-top: 10px; }
.photo-add-thumbs { display: flex; flex-wrap: wrap; gap: 8px; }
.photo-add-thumb { display: flex; flex-direction: column; gap: 2px; width: 96px; }
.photo-add-thumb img { width: 96px; height: 96px; object-fit: cover; border-radius: 6px; border: 1px solid var(--border); }
.photo-add-thumb-label { font-size: 10px; color: var(--text-subtle); overflow-wrap: anywhere; }
.photo-add-progress { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-muted); }
.photo-add-review { display: flex; flex-direction: column; gap: 10px; }
.photo-add-notes { display: flex; flex-direction: column; gap: 2px; }
.photo-add-changes { display: flex; flex-direction: column; gap: 8px; }
.photo-add-change { border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; }
.photo-add-change.replaces { border-left: 4px solid var(--warning, #b45309); }
.photo-add-change-main { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; cursor: pointer; }
.photo-add-change-label { font-size: 12px; font-weight: 600; min-width: 110px; }
.photo-add-change-values { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 12px; }
.photo-add-old { color: var(--text-muted); text-decoration: line-through; }
.photo-add-arrow { color: var(--text-subtle); }
.photo-add-new { font-weight: 600; }
.photo-add-change-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 4px; }
.photo-add-conflicts { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.photo-add-conflict { border: 1px solid var(--warning, #b45309); border-radius: 8px; padding: 10px; }
.photo-add-conflict.material { border-left: 4px solid var(--warning, #b45309); }
.photo-add-conflict-title { font-size: 12px; font-weight: 600; margin-bottom: 4px; }
.photo-add-conflict-options { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.photo-add-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.photo-add-summary { margin-top: 12px; display: flex; flex-direction: column; gap: 4px; }
.photo-add-summary-line { font-size: 12px; }
.photo-add-summary-actions { margin-top: 6px; }
.badge { display: inline-block; font-size: 10px; padding: 2px 6px; border-radius: 10px; border: 1px solid var(--border); }
.badge-new { color: var(--success-strong); border-color: var(--success-strong); }
.badge-warn { color: var(--warning-strong); border-color: var(--warning-strong); }

/* Photos in chat */
.chat-attachments { display: flex; flex-direction: column; gap: 6px; margin-bottom: 6px; }
.chat-attachment-label { font-size: 11px; color: var(--text-muted); }
.chat-attachment-chips, .chat-photo-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chat-attachment-chip, .chat-photo-chip {
  display: inline-flex; align-items: center; gap: 6px; font-size: 11px;
  border: 1px solid var(--border); border-radius: 12px; padding: 2px 8px;
}
.chat-attachment-thumb, .chat-photo-thumb { width: 22px; height: 22px; object-fit: cover; border-radius: 4px; }
.chat-attachment-name { max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-photo-row { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.chat-photo-label { font-size: 11px; color: var(--text-muted); }

/* ============================================================

/* ============================================================
   Stage 7 / W6 + W7 - administration screens
   ------------------------------------------------------------
   The administrator tables are dense by nature, so below 700px they stop being
   tables and become stacked cards. Nothing here relies on a fixed desktop width,
   and no element may cause page-level horizontal scrolling.
   ============================================================ */
.admin-page { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.admin-tabs { display: flex; flex-wrap: wrap; gap: 8px; }
.admin-content { min-width: 0; }
.admin-toolbar { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; justify-content: space-between; }
.admin-toolbar-fields { display: flex; flex-wrap: wrap; gap: 8px; flex: 1 1 320px; min-width: 0; }
.admin-toolbar-fields .input { min-width: 140px; flex: 1 1 160px; }
.admin-toolbar-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.admin-inline-label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--text-muted); min-width: 0; }
.admin-summary { font-size: 12px; color: var(--text-muted); margin: 8px 0; overflow-wrap: anywhere; }
.admin-loading { padding: 24px; color: var(--text-muted); font-size: 13px; }

/* --- tables ------------------------------------------------------------- */
.admin-table-wrap { overflow-x: auto; }
.admin-table { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.admin-row { display: grid; grid-template-columns: minmax(180px, 2fr) 90px 110px minmax(160px, 1.4fr) 140px 90px; gap: 10px; padding: 10px 12px; align-items: center; border-top: 1px solid var(--border); font-size: 13px; min-width: 0; }
.admin-row > div { min-width: 0; overflow-wrap: anywhere; }
.admin-row-head { border-top: none; background: rgba(0,0,0,0.03); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-muted); }
.admin-row-empty { display: block; color: var(--text-muted); font-size: 13px; }
.admin-cell-primary { font-weight: 600; }
.admin-cell-sub { color: var(--text-muted); font-size: 12px; }
.admin-row-actions { display: flex; gap: 6px; justify-content: flex-end; }

/* --- pills / chips ------------------------------------------------------ */
.pill { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 11px; line-height: 1.6; white-space: nowrap; }
.pill-ok { background: var(--success-subtle); color: var(--success-strong); }
.pill-warn { background: var(--warning-subtle); color: var(--warning-strong); }
.pill-muted { background: var(--bg-muted); color: var(--text-muted); }
.pill-accent { background: var(--accent-subtle); color: var(--accent); }
.chip { display: inline-block; padding: 2px 8px; border: 1px solid var(--border); border-radius: 6px; font-size: 11px; margin: 0 4px 4px 0; }
.admin-chip-list { display: flex; flex-wrap: wrap; }

/* --- feature switches --------------------------------------------------- */
.admin-feature-list { display: flex; flex-direction: column; gap: 2px; }
.admin-feature { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); }
.admin-feature:last-child { border-bottom: none; }
.admin-feature-main { display: flex; gap: 10px; align-items: flex-start; flex: 1; min-width: 0; cursor: pointer; }
.admin-feature-label { font-size: 13px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.admin-toggle { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 2px; }
.admin-toggle:disabled { opacity: 0.5; }

/* --- policy ------------------------------------------------------------- */
.admin-policy-row { display: flex; gap: 10px; align-items: flex-start; padding: 8px 0; border-bottom: 1px solid var(--border); cursor: pointer; }
.admin-policy-row:last-of-type { border-bottom: none; }

/* The tool switches: the same row pattern, in a scrollable list so twenty tools do not
   push the Save button off the screen. */
.admin-policy-tools { max-height: 320px; overflow: auto; border: 1px solid var(--border); border-radius: var(--radius); padding: 4px 12px; margin-top: 6px; }
.admin-policy-tools .admin-cell-sub { margin-bottom: 6px; }

/* The dry run: the exact instruction the chat would send, shown as plain text so nothing in
   it can be mistaken for the interface. */
.admin-policy-preview {
  margin-top: 10px; padding: 12px; max-height: 360px; overflow: auto;
  background: var(--bg-muted); border: 1px solid var(--border); border-radius: var(--radius);
  font-family: var(--font-mono, monospace); font-size: 12px; line-height: 1.5;
  white-space: pre-wrap; word-break: break-word;
}
.admin-section { padding: 14px 16px; }
.admin-section-title { font-weight: 600; font-size: 13px; margin: 10px 0 8px; }

/* --- account detail ----------------------------------------------------- */
.admin-modal { max-width: 760px; width: min(96vw, 760px); max-height: 88vh; display: flex; flex-direction: column; }
.admin-modal .modal-body { overflow-y: auto; padding-bottom: 8px; }
.admin-detail-head { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.admin-detail-pills { display: flex; gap: 6px; flex-wrap: wrap; }
.admin-usage-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 8px; margin-bottom: 12px; }
.admin-stat { border: 1px solid var(--border); border-radius: 8px; padding: 8px; text-align: center; min-width: 0; }
.admin-stat-value { font-size: 16px; font-weight: 600; }
.admin-stat-label { font-size: 11px; color: var(--text-muted); }
.admin-sessions { display: flex; flex-direction: column; gap: 6px; }
.admin-session { display: flex; justify-content: space-between; gap: 10px; border-bottom: 1px solid var(--border); padding-bottom: 6px; font-size: 12px; min-width: 0; }
.admin-session:last-child { border-bottom: none; }
.admin-pager { display: flex; gap: 8px; justify-content: center; margin-top: 10px; }
.admin-invite-form { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0; }
.admin-invite-form .input { flex: 1 1 200px; }
.admin-invite-row { display: flex; flex-wrap: wrap; gap: 10px; justify-content: space-between; border-top: 1px solid var(--border); padding: 6px 0; font-size: 12px; }
.is-disabled { opacity: 0.55; cursor: not-allowed; }

/* --- phones ------------------------------------------------------------- */
@media (max-width: 700px) {
  .admin-row { grid-template-columns: 1fr; gap: 4px; }
  .admin-row-head { display: none; }
  .admin-row-actions { justify-content: flex-start; margin-top: 6px; }
  .admin-toolbar { align-items: stretch; }
  .admin-toolbar-fields .input { flex: 1 1 100%; }
  .admin-modal { width: 96vw; max-height: 92vh; }
  .admin-feature { flex-wrap: wrap; }
}

.admin-section > .admin-section-title:first-child { margin-top: 0; }
.admin-danger { border-color: rgba(220, 38, 38, 0.35); }
.admin-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.admin-field-wide { display: block; margin-top: 10px; }

/* ============================================================
   RESPONSIVE — phones, tablets and desktops
   ============================================================ */
@media (max-width: 900px) {
  .sidebar-toggle { display: inline-flex; }
  .header-search { max-width: none; }
  .user-menu .user-inventory { display: none; }
}
@media (max-width: 700px) {
  /* Header wraps so global search keeps its own row instead of squeezing out. */
  .app-header { flex-wrap: wrap; }
  .header-search { order: 3; flex-basis: 100%; max-width: none; }
  .form-grid { grid-template-columns: 1fr; }
  .item-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
  .ai-catalog-grid { flex-direction: column; }
  .ai-catalog-grid .form-grid { min-width: 0; }
  .display-image-row { flex-direction: column; }
  .label-sheet { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
  .storage-details { grid-template-columns: 1fr; gap: 2px 0; }
  .storage-detail-row dt { margin-top: 6px; }
  .filter-bar select { max-width: none; flex: 1 1 140px; }
  .page-actions { flex-wrap: wrap; }
  /* Modals become full-screen sheets on phones. */
  .modal, .modal.lg { width: 100%; max-width: 100%; height: 100%; max-height: 100%; border-radius: 0; }
  .table-wrap { -webkit-overflow-scrolling: touch; }
  .report-table { min-width: 560px; }
  .rescan-field-label { min-width: 96px; }
  /* Stage 6: the mode cards stack, and a proposed change reads as one column. */
  .scan-mode-list { grid-template-columns: 1fr; }
  .scan-mode { padding: 10px; }
  .scan-compare-row { grid-template-columns: 1fr; gap: 2px; }
  .photo-add-change-label { min-width: 0; }
  .photo-add-change-values { font-size: 12px; }
}
@media (max-width: 480px) {
  .main { padding: 12px; }
  .brand span { display: none; }
  /* 0,2,1 so it beats the generic input rule loaded above; the old
     .header-search input (0,1,1) never applied. */
  .header-search input[type="text"] { font-size: 12px; }
  .scan-video { max-height: 240px; }
  .rescan-thumb { max-height: 160px; }
}


/* ============================================================
   ITEM PHOTO GALLERY
   One entry per logical photo; a preserved original is reached via
   "View original" instead of appearing as a duplicate gallery item.
   ============================================================ */
.photo-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; }
.photo-gallery-item { border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; background: var(--bg-elevated); display: flex; flex-direction: column; }
.photo-gallery-item img { display: block; width: 100%; height: 140px; object-fit: cover; background: var(--bg-muted); }
.photo-gallery-meta { display: flex; gap: 8px; justify-content: space-between; padding: 6px 8px 0; }
.photo-gallery-actions { display: flex; gap: 6px; flex-wrap: wrap; padding: 6px 8px 8px; margin-top: auto; }
@media (max-width: 700px) {
  .photo-gallery { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); }
  .photo-gallery-item img { height: 120px; }
/* ---- in-app photo viewer (lightbox) -------------------------------------
   The image is centred and constrained so portrait, landscape and unusually
   large photos all fit without breaking the layout. */
.photo-gallery-thumb { cursor: zoom-in; }
.lightbox-overlay { align-items: center; justify-content: center; padding: 16px; }
.lightbox-close { position: absolute; top: 12px; right: 12px; z-index: 2; font-size: 18px; }
.lightbox-stage { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; max-width: 1100px; }
.lightbox-image {
  display: block; max-width: 100%; max-height: 72vh; width: auto; height: auto;
  object-fit: contain; border-radius: var(--radius-sm); background: var(--bg-muted);
}
.lightbox-image-missing { min-width: 220px; min-height: 140px; opacity: 0.35; }
.lightbox-nav { font-size: 26px; line-height: 1; flex: 0 0 auto; }
.lightbox-status { text-align: center; color: var(--text-muted); padding: 8px; }
.lightbox-bar { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 8px; }
.lightbox-counter { font-size: 13px; color: var(--text-muted); }
.lightbox-thumbs { display: flex; gap: 8px; overflow-x: auto; max-width: 100%; padding-bottom: 4px; }
.lightbox-thumb {
  flex: 0 0 auto; padding: 0; border: 2px solid transparent; border-radius: var(--radius-sm);
  background: none; cursor: pointer; line-height: 0;
}
.lightbox-thumb img { width: 64px; height: 48px; object-fit: cover; border-radius: 4px; display: block; }
.lightbox-thumb.selected { border-color: var(--accent, #0d7377); }

@media (max-width: 700px) {
  /* Touch-friendly targets, and the image gets more of a small screen. */
  .lightbox-image { max-height: 60vh; }
  .lightbox-nav { font-size: 30px; padding: 10px 14px; }
  .lightbox-thumb img { width: 56px; height: 44px; }
}

}

/* ============================================================
   Stage 7 / W4 - sharing (owner panel + guest page)
   ============================================================ */
.share-card-title { font-weight: 600; font-size: 13px; margin-bottom: 4px; }
.share-form-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.share-form-row .input { flex: 1 1 200px; min-width: 0; }
.share-form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; margin-top: 10px; }
.share-form-grid label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--text-muted); min-width: 0; }
.share-form-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.share-list { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.share-row { display: flex; flex-wrap: wrap; gap: 10px; justify-content: space-between; align-items: center; border: 1px solid var(--border); border-radius: 8px; padding: 10px; min-width: 0; }
.share-row-main { min-width: 0; flex: 1 1 220px; }
.share-row-title { font-size: 13px; font-weight: 600; overflow-wrap: anywhere; }
.share-row-sub { font-size: 12px; color: var(--text-muted); overflow-wrap: anywhere; }
.share-row-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.share-new-link { border: 1px solid var(--accent); border-radius: 8px; padding: 10px; margin-top: 12px; background: var(--accent-subtle); }
.share-new-link-title { font-size: 13px; font-weight: 600; margin-bottom: 4px; }

/* --- the guest page (no account, no sidebar) ---------------------------- */
.share-guest-page { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.share-guest-state { max-width: 520px; margin: 24px auto; text-align: center; }
.share-guest-header { display: flex; flex-wrap: wrap; gap: 10px; justify-content: space-between; align-items: center; }
.share-guest-title { font-size: 18px; font-weight: 600; overflow-wrap: anywhere; }
.share-guest-message { font-size: 13px; color: var(--text-muted); overflow-wrap: anywhere; }
.share-guest-form { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; justify-content: center; }
.share-guest-form .input { flex: 1 1 200px; }
.share-guest-footer { margin-top: 12px; }
.share-item-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; }
.share-item-card { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--bg-elevated); display: flex; flex-direction: column; min-width: 0; }
.share-item-photo { display: block; width: 100%; height: 140px; object-fit: cover; background: var(--bg-muted); }
.share-item-no-photo { display: grid; place-items: center; font-size: 12px; color: var(--text-muted); }
.share-item-body { padding: 8px 10px 10px; min-width: 0; }
.share-item-name { font-size: 13px; font-weight: 600; overflow-wrap: anywhere; }
.share-item-meta { font-size: 11.5px; color: var(--text-muted); }
.share-item-desc { font-size: 11.5px; color: var(--text-muted); margin-top: 4px; overflow-wrap: anywhere; }

@media (max-width: 700px) {
  .share-form-row .input { flex: 1 1 100%; }
  .share-row { flex-direction: column; align-items: stretch; }
  .share-row-actions { justify-content: flex-start; }
  .share-item-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 10px; }
  .share-item-photo { height: 120px; }
  .share-guest-state { margin: 12px auto; }
}

/* ============================================================
   STAGE 8 — reviewing a bulk change, the mobile chat, safe areas
   ============================================================ */

/* A bulk change is reviewed record by record before it is confirmed. */
.ai-batch-head { margin-top: 10px; font-size: 12px; color: var(--text-muted); }
.ai-batch-list { margin-top: 6px; display: flex; flex-direction: column; gap: 6px; max-height: 320px; overflow-y: auto; }
.ai-batch-row { display: flex; align-items: center; gap: var(--space-2); cursor: pointer; }
.ai-batch-row .ai-ref-tile { flex: 1 1 auto; }
.ai-batch-check { width: 18px; height: 18px; flex: 0 0 auto; accent-color: var(--accent); }

/* An answer the user stopped is incomplete, and says so. */
.ai-interrupted {
  margin-top: 8px; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  font-size: 12px; color: var(--text-muted);
  border-left: 3px solid var(--warning-strong); background: var(--warning-subtle);
  border-radius: var(--radius-sm); padding: 6px 10px;
}

/* The live region that carries "Thinking…", "Stopped" and failures to a screen
   reader. It is visible text, and never a second spinner. */
.chat-status { font-size: 12px; color: var(--text-muted); min-height: 0; }
.chat-status:empty { display: none; }

/* Jump back to the newest message after reading history. */
.chat-jump-latest {
  align-self: center; font-size: 12px; padding: 6px 14px;
  background: var(--bg-elevated); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius-pill);
  box-shadow: var(--shadow-md);
}
.chat-jump-latest[hidden] { display: none; }
.chat-jump-latest:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.chat-actions-stop { justify-content: flex-end; }

/* Photos attached to a message: the name is kept, the bytes are not. */
.chat-photo-thumb-gone {
  display: inline-grid; place-items: center; font-size: 9.5px; color: var(--text-subtle);
  background: var(--bg-muted); border: 1px dashed var(--border-strong); padding: 0 4px;
}
.chat-photo-note { margin-top: 6px; }

/* Choosing a photo that is already in the inventory. */
.chat-photo-picker { margin-top: 10px; display: flex; flex-direction: column; gap: 8px; }
.chat-photo-pick-row { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.chat-photo-pick-row .ai-ref-tile { flex: 1 1 220px; }
.chat-photo-pick-actions { display: flex; gap: 6px; flex-wrap: wrap; }

/* ---- the mobile conversation drawer -------------------------------------- */
.conv-drawer-toggle { display: none; }

@media (max-width: 860px) {
  /* Transcript first: the conversation list becomes a drawer over it. */
  .ai-chat-layout { grid-template-columns: minmax(0, 1fr); }
  .conv-drawer-toggle { display: inline-flex; }
  .ai-conv-drawer { display: none; }
  .ai-chat-layout.drawer-open .ai-conv-drawer { display: block; }
  .ai-conv-drawer .ai-conv-panel { max-height: 45dvh; overflow-y: auto; }
  /* The drawer panel is ALREADY a scroll container, so the list inside it must not
     create a second one: the rows keep their height and the panel scrolls. (Two
     nested scrollbars in a 45dvh drawer is how the list looked broken.) */
  .ai-conv-drawer .ai-conv-list { max-height: none; overflow: visible; overscroll-behavior: auto; }
}

/* The drawer's account block: the phone's copy of the header's user menu. It is hidden
   wherever the header shows it, so the same controls exist exactly once at any width. */
.sidebar-account { display: none; }
.sidebar-account-email {
  font-size: 11.5px; color: var(--text-muted); padding: 0 12px 6px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (max-width: 640px) {
  .sidebar-account { display: block; margin-bottom: 0; }
}

/* ---- the mobile chat transcript ------------------------------------------ */
@media (max-width: 700px) {
  /* The transcript owns the height it has, in real device pixels (dvh), so the
     composer is not pushed off the screen by a mobile browser's toolbars. */
  .ai-chat-page { gap: var(--space-3); }
  /* flex: 0 0 auto matters: the transcript is a flex item, and a plain `height`
     would be overridden by its flex basis (which is why it grew to the full
     content height and pushed the composer off the screen). */
  .chat-messages { flex: 0 0 auto; max-height: none; height: 52dvh; min-height: 220px; padding: var(--space-3); }
  .message { max-width: 94%; }
  .message-bubble { padding: 10px 12px; }
  .ai-chat-main { gap: var(--space-3); }
  /* The dock (status + quick actions + composer) is pinned to the bottom of the view,
     so the quick actions can never slide underneath the chat box - they are inside it.
     The transcript keeps the height it always had and scrolls on its own. */
  .chat-dock {
    position: sticky; bottom: 0; z-index: 5;
    display: flex; flex-direction: column; gap: var(--space-2);
    padding: var(--space-2) var(--space-3) calc(var(--space-2) + env(safe-area-inset-bottom, 0px));
    margin: 0 calc(-1 * var(--space-3));
    background: var(--bg-surface); border-top: 1px solid var(--border);
  }
  .chat-dock .chat-status:empty { display: none; }
  .chat-dock .chat-jump-latest { align-self: center; }
  .chat-input-area { position: static; bottom: auto; background: none; border-top: 0; margin: 0; padding: 0; }
  .chat-quick-actions { overflow-x: auto; flex-wrap: nowrap; padding-bottom: 2px; }
  .chat-quick-actions .btn { flex: 0 0 auto; }
  .chat-actions { gap: 6px; }
  .chat-actions .btn { flex: 0 1 auto; }
  /* On a narrow phone the composer's row is too wide to fit, and wrapping it made the
     dock tall enough to squeeze the transcript. The labels give way (the buttons keep
     their icon, their tooltip and their accessible name), so the row stays one line and
     the transcript keeps real room. */
  @media (max-width: 380px) {
    .chat-btn-label, .chat-hint { display: none; }
    .chat-input-area textarea { min-height: 48px; padding: 10px; }
  }
  .ai-batch-list { max-height: 42dvh; }
  .ai-ref-cards { overflow-x: visible; }
  /* The tile is already compact; on a phone it keeps two per row and the same
     fixed height, so the block stays predictable. */
  .ai-ref-tile { --tile-size: 26px; --tile-height: 44px; }
  .ai-ref-block.expanded .ai-ref-cards { max-height: calc(4 * 44px + 3 * 8px); }
}

/* ---- installed-PWA safe areas -------------------------------------------- */
/* The notch and the home indicator are part of the viewport once the app is
   installed and displayed standalone, so every edge that touches the screen edge
   accounts for them. */
.app-header { padding-top: calc(12px + env(safe-area-inset-top, 0px)); }
.sidebar { padding-left: calc(12px + env(safe-area-inset-left, 0px)); }
.main { padding-bottom: env(safe-area-inset-bottom, 0px); }
.modal-overlay {
  padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px)
           env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
}
.toasts { padding-bottom: env(safe-area-inset-bottom, 0px); padding-right: env(safe-area-inset-right, 0px); }
@media (orientation: landscape) and (max-height: 520px) {
  .chat-messages { flex: 0 0 auto; height: 46dvh; min-height: 160px; }
  .ai-conv-drawer .ai-conv-panel { max-height: 60dvh; }
}
/* ---- transitions ---------------------------------------------------------- */
/* Subtle, consistent, and driven by the motion tokens. The global
   prefers-reduced-motion rule in base.css collapses these to nothing, so a user
   who asks for no motion gets none. */
.item-card, .data-row, .ai-action-card, .ai-review-card, .card, .chat-jump-latest {
  transition: box-shadow var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard),
              transform var(--duration-fast) var(--ease-standard);
}
.item-card:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
.btn { transition: background-color var(--duration-fast) var(--ease-standard),
       border-color var(--duration-fast) var(--ease-standard),
       color var(--duration-fast) var(--ease-standard); }
.message { transition: opacity var(--duration-base) var(--ease-out); }

/* Touch devices: every icon-only control is a real 44px target, in the header and
   everywhere else. This is the contract the browser tests measure. */
@media (pointer: coarse) {
  .btn-icon, .icon-btn, .sidebar-toggle { min-width: var(--hit-touch); min-height: var(--hit-touch); }
}

/* ---- keyboard focus, as a floor ------------------------------------------ */
/* A keyboard user must always be able to see where they are. Components above
   refine this; this rule is the floor, so nothing is ever invisible when it is
   focused. */
a:focus-visible, summary:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-xs);
}
/* A link that has to be a button: it does something (opens a record) rather than
   going somewhere, so it is a real button and reaches the keyboard. */
.link-btn {
  display: inline; padding: 0; background: none; border: none; text-align: left;
  font: inherit; font-weight: 600; color: var(--text); cursor: pointer;
  border-radius: var(--radius-xs);
}
.link-btn:hover { color: var(--accent); text-decoration: underline; }
.link-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---- Item Types editor ---------------------------------------------------- */
.type-field-list { display: flex; flex-direction: column; gap: var(--space-2); margin: var(--space-2) 0; }
.type-field-row { display: flex; align-items: center; gap: var(--space-2); }
.type-field-row input { flex: 1 1 auto; min-width: 0; }
.type-field-row select { flex: 0 0 auto; }

/* ---- skeleton loading ---------------------------------------------------- */
/* A skeleton says "content is coming, in this shape" instead of spinning. */
.skeleton {
  position: relative; overflow: hidden;
  background: var(--bg-muted); border-radius: var(--radius-sm);
}
.skeleton::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, var(--bg-hover), transparent);
  transform: translateX(-100%);
  animation: skeleton-sweep 1.2s var(--ease-standard) infinite;
}
@keyframes skeleton-sweep { to { transform: translateX(100%); } }
.skeleton-row { height: 56px; margin-bottom: var(--space-2); }
.skeleton-tile { height: var(--tile-img); border-radius: var(--radius); }
.skeleton-line { height: 12px; margin: 6px 0; }
.skeleton-line.short { width: 40%; }
.skeleton-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: var(--space-3); }

/* ---- narrow-screen tables ------------------------------------------------- */
/* A data table stays a table on a wide screen. On a narrow one each row becomes
   a labelled card, which is readable without horizontal scrolling. The labels
   come from the cell's own data-label attribute. Screen only, so a printout
   (which is laid out for paper, not for the device) is never restacked. */
@media screen and (max-width: 700px) {
  .table-wrap.responsive table,
  .table-wrap.responsive tbody,
  .table-wrap.responsive tr,
  .table-wrap.responsive td { display: block; width: 100%; }
  .table-wrap.responsive thead { display: none; }
  .table-wrap.responsive tr {
    background: var(--bg-elevated); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: var(--shadow-sm);
    padding: var(--space-2) var(--space-3); margin-bottom: var(--space-2);
  }
  .table-wrap.responsive td {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3);
    padding: 4px 0; border: none; font-size: var(--text-base);
  }
  .table-wrap.responsive td::before {
    content: attr(data-label); flex: 0 0 auto;
    font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--text-subtle);
  }
  .table-wrap.responsive td > * { min-width: 0; overflow-wrap: anywhere; }
}


/* ============================================================
   MOBILE NAVIGATION (js/nav.js)
   ------------------------------------------------------------
   The bottom bar is a phone surface: it is hidden above 700px (where the sidebar
   is always on screen), hidden when signed out, and hidden in print. Everything
   that would otherwise sit under it — the main scroll area, the sticky chat
   composer, the storage banner — offsets by --bottom-nav-h, so one token keeps
   them in step.

   These rules live here rather than in layout.css on purpose: components.css is
   loaded last, and several rules they must override (the safe-area padding on
   .main, the composer's sticky bottom) are declared in this file.
   ============================================================ */

/* The drawer's backdrop: the touch target that closes it. Fixed, so it covers the
   content but never the drawer itself (z-index 45 < the sidebar's 50). */
.nav-backdrop {
  position: fixed; inset: 0; z-index: 45;
  background: rgba(31, 29, 26, 0.4);
  border: 0; padding: 0;
}
.nav-backdrop[hidden] { display: none; }

.bottom-nav { display: none; }

@media (max-width: 700px) {
  /* The bar itself. Safe-area padding keeps it clear of the home indicator when
     the app is installed and displayed standalone. */
  .bottom-nav {
    display: flex; align-items: stretch; justify-content: space-around;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    min-height: var(--bottom-nav-h);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
    background: var(--bg-surface); border-top: 1px solid var(--border);
  }
  /* The scroll area reserves the bar's height, so the last row of a list is never
     trapped underneath it. This must come after the base .main rule in this file,
     which it does. */
  .main { padding-bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0px)); }
  /* The composer dock is pinned to the bottom of the view, so it is lifted by the bar
     as well - otherwise Send would sit behind it. The quick actions travel with it, so
     they are never covered by the chat box. */
  .chat-dock { bottom: var(--bottom-nav-h); }
  /* At the very end of the page the dock sits at its own position, so it reserves the
     bar's height too: its last row (Send) is never behind the navigation. */
  .chat-dock { margin-bottom: var(--bottom-nav-h); }
  /* The storage banner is fixed 16px from the bottom edge. */
  .app-banner { bottom: calc(16px + var(--bottom-nav-h) + env(safe-area-inset-bottom, 0px)); }
}

/* One destination. The label is always visible (an icon-only bar is a guessing
   game on a phone), and the whole item is a real touch target. */
.bottom-nav-item {
  flex: 1 1 0; min-width: 0; min-height: var(--hit-touch);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; padding: 6px 2px; border: 0; background: none; cursor: pointer;
  color: var(--text-muted); font-size: 10px; line-height: 1.15;
}
.bottom-nav-item .bottom-nav-icon { display: flex; }
.bottom-nav-item .bottom-nav-label {
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bottom-nav-item.active { color: var(--accent); }
.bottom-nav-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--radius-sm); }
.bottom-nav-item .count {
  font-size: 9px; color: var(--text-subtle); background: var(--bg-muted);
  padding: 0 4px; border-radius: var(--radius-pill);
}

/* A preview of the bar in Settings: the same markup, at the same size, so the
   preview and the real bar cannot drift apart. It is decorative (the list above it
   is the real control), so it carries aria-hidden and holds no controls. */
.bottom-nav-preview { display: flex; justify-content: center; }
.bottom-nav-preview .bottom-nav {
  display: flex; position: static; inset: auto; z-index: auto;
  width: 100%; max-width: 420px; border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden;
  padding-bottom: 0; min-height: var(--bottom-nav-h);
}
.bottom-nav-preview .bottom-nav-item { cursor: default; }

/* The Settings picker: one row per destination, never a drag-only interaction
   (this is configured on a phone). */
.bottom-nav-picker { display: flex; flex-direction: column; gap: 4px; }
.bottom-nav-choice {
  display: flex; align-items: center; gap: 10px; min-height: var(--hit-touch);
  padding: 6px 8px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-elevated);
}
.bottom-nav-choice-label { flex: 1; min-width: 0; font-size: 13px; display: flex; align-items: center; gap: 8px; }
.bottom-nav-choice-label span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bottom-nav-choice-actions { display: flex; align-items: center; gap: 4px; }
.bottom-nav-choice input[type="checkbox"] { width: 20px; height: 20px; }

/* Signed out there is nothing to open and nothing to navigate to. */
.app.signed-out .sidebar-toggle { display: none; }
.app.signed-out .bottom-nav { display: none; }

@media print {
  .bottom-nav, .nav-backdrop { display: none !important; }
}

/* ============================================================
   AI CHAT - STRUCTURED BLOCKS, PROGRESS, FAILURE AND EXPORT
   ------------------------------------------------------------
   Everything a validated model answer can render (see
   js/ai-chat-contract.js + js/ai-chat-render.js), plus the
   in-chat progress, the failure/retry state and the copy/export
   controls.

   Every declaration uses a token declared in base.css (the
   design-token contract test enforces that), so the light and
   dark themes both work without a second rule set.
   ============================================================ */

.ai-blocks { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.ai-block { min-width: 0; }
.ai-block-heading { margin: 0; font-size: var(--text-lg); line-height: 1.3; }
.ai-block-caption { font-size: var(--text-xs); font-weight: 600; letter-spacing: 0.03em;
  text-transform: uppercase; color: var(--text-subtle); margin-bottom: var(--space-1); }
.ai-block-list { margin: 0; padding-left: 20px; }
.ai-block-list li { margin: 2px 0; }
.ai-block-list li .markdown-inline, .ai-block-check .markdown-inline { display: inline; }
.markdown-inline p { margin: 0; display: inline; }

/* Checklist: a real list, with an aria-hidden marker rather than an icon font. */
.ai-block-checklist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.ai-block-check { display: flex; align-items: flex-start; gap: var(--space-2); font-size: var(--text-base); }
.ai-block-check-mark { color: var(--accent); flex: 0 0 auto; line-height: 1.4; }
.ai-block-check.done { color: var(--text-muted); }

/* Notes: the four tones the contract allows, and nothing else. */
.ai-block-note { border-left: 3px solid var(--info); background: var(--info-subtle);
  border-radius: var(--radius-sm); padding: var(--space-2) var(--space-3); font-size: var(--text-base); }
.ai-block-note-success { border-left-color: var(--success); background: var(--success-subtle); }
.ai-block-note-warning { border-left-color: var(--warning); background: var(--warning-subtle); }
.ai-block-note-danger { border-left-color: var(--danger); background: var(--danger-subtle); }

/* Tables: the markdown renderer's focusable scroll wrapper, reused verbatim so a
   wide comparison table can never stretch the chat column. */
.ai-block-table-wrap { margin-top: var(--space-1); }
.ai-block-table { width: 100%; border-collapse: collapse; font-size: var(--text-base); }
.ai-block-table th, .ai-block-table td { padding: 6px 10px; border-bottom: 1px solid var(--border);
  text-align: left; vertical-align: top; }
.ai-block-table th { font-weight: 600; color: var(--text-muted); white-space: nowrap; }
.ai-block-table-comparison tr td:first-child { font-weight: 600; }
.ai-block-align-center { text-align: center; }
.ai-block-align-right { text-align: right; }

/* Item cards: App.AITools.renderRefs already styles the tile grid. */
.ai-block-items .ai-ref-cards { margin-top: var(--space-1); }

/* Expandable sections: a real <details>, so keyboard and find-in-page work. */
.ai-block-details > summary { cursor: pointer; font-size: var(--text-base); font-weight: 600;
  color: var(--text-muted); padding: 2px 0; }
/* Generated forms: the app's own field classes, so the controls look native. */
.ai-block-form { border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-3); background: var(--bg-elevated); }
.ai-block-form-desc { font-size: var(--text-base); color: var(--text-muted); margin-bottom: var(--space-2); }
.ai-block-field { margin-top: var(--space-2); }
.ai-block-form-actions { display: flex; align-items: center; gap: var(--space-3);
  margin-top: var(--space-3); flex-wrap: wrap; }
.ai-block-form-status { font-size: var(--text-sm); color: var(--text-muted); }
.ai-block-form-status.ok { color: var(--success-strong); }
.ai-block-form-status.bad { color: var(--danger); }

/* Suggested actions and follow-up prompts: compact, wrap, never a wall of buttons. */
.ai-block-action-row { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* ---- in-chat progress ---------------------------------------------------- */
.chat-progress { border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-elevated); padding: var(--space-2) var(--space-3); font-size: var(--text-sm); }
.chat-progress > summary { cursor: pointer; display: flex; align-items: center; gap: var(--space-2);
  color: var(--text-muted); min-height: 24px; }
.chat-progress-head { display: flex; align-items: center; gap: var(--space-2); flex: 1; min-width: 0; }
.chat-progress-title { font-weight: 600; color: var(--text); }
.chat-progress-time { color: var(--text-subtle); font-size: var(--text-xs); }
.chat-progress-steps { list-style: none; margin: var(--space-2) 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 3px; }
.chat-progress-step { display: flex; align-items: baseline; gap: var(--space-2); color: var(--text-muted); }
.chat-progress-step-mark { width: 14px; flex: 0 0 auto; color: var(--text-subtle); }
.chat-progress-step.done .chat-progress-step-mark { color: var(--success); }
.chat-progress-step.active { color: var(--text); }
.chat-progress-step.active .chat-progress-step-mark { color: var(--accent); }
.chat-progress-step.failed { color: var(--danger); }
.chat-progress-step.failed .chat-progress-step-mark { color: var(--danger); }
.chat-progress-step.skipped { color: var(--text-subtle); }
.chat-progress-detail { color: var(--text-subtle); }
.chat-progress-actions { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-2); }
.chat-progress-stop { font-size: var(--text-xs); }

/* ---- failure, retry and cancelled states -------------------------------- */
.ai-failure { border: 1px solid var(--danger); border-left-width: 3px; border-radius: var(--radius-sm);
  background: var(--danger-subtle); padding: var(--space-2) var(--space-3);
  display: flex; flex-direction: column; gap: 4px; }
.ai-failure-title { font-weight: 600; color: var(--danger); }
.ai-failure-reason { font-size: var(--text-base); color: var(--text); word-break: break-word; }
.ai-failure-hint { font-size: var(--text-sm); color: var(--text-muted); }
.ai-failure-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-1); }
.ai-partial-note { border-left: 3px solid var(--warning); background: var(--warning-subtle);
  border-radius: var(--radius-sm); padding: 4px var(--space-2); font-size: var(--text-sm); }
.ai-superseded { font-size: var(--text-xs); color: var(--text-subtle); font-style: italic; }

/* ---- follow-up suggestions --------------------------------------------- */
.chat-followups { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.chat-followup { max-width: 100%; text-align: left; white-space: normal; }
.chat-followup-label { font-size: var(--text-xs); color: var(--text-subtle); align-self: center; }

/* ---- copy / export controls -------------------------------------------- */
.message-tools { display: flex; flex-wrap: wrap; gap: 2px; margin-top: var(--space-1); }
.message-tool { font-size: var(--text-xs); padding: 3px 8px; color: var(--text-muted);
  background: none; border: 1px solid var(--border); border-radius: var(--radius-pill); cursor: pointer; }
.message-tool:hover { color: var(--text); background: var(--bg-hover); }
.message-tool:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.chat-export-bar { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }

/* ---- phones ------------------------------------------------------------- */
@media (max-width: 700px) {
  .ai-block-table th, .ai-block-table td { padding: 5px 8px; white-space: normal; }
  .ai-block-form-actions { flex-direction: column; align-items: stretch; }
  .ai-block-form-actions .btn { width: 100%; }
  .chat-followups .btn { flex: 1 1 auto; }
  .chat-progress > summary { min-height: var(--hit-touch); }
}



/* ============================================================
   THE PHONE LAYOUT (≤640px): one row of chrome, one scroller
   ------------------------------------------------------------
   WHY THIS IS A SINGLE BLOCK: the phone header, the compact page header and the
   chat's flex chain all have to win over the rules above (including the safe-area
   padding and the 700px/480px header rules), so they live together, after them, in
   one place that can be read as a whole.

   The shape is: a 44px header, then the view, then the fixed bottom bar. The chat
   page fills its space EXACTLY, which is what makes the transcript the only thing
   that scrolls and the dock an opaque block pinned directly above the bar - no
   thread content can show behind it or below it.
   ============================================================ */
@media (max-width: 640px) {
  /* --- 1. The header: ONE 44px row ------------------------------------- */
  .app-header {
    display: flex; flex-wrap: nowrap; align-items: center; gap: 6px;
    min-height: 44px; padding: 6px 8px;
    padding-top: calc(6px + env(safe-area-inset-top, 0px));
  }
  /* The mark alone identifies the app; the word would take the search's room. */
  .app-header .brand > span { display: none; }
  .brand { flex: 0 0 auto; min-width: 0; }
  /* The search fills the middle of the row... */
  .header-search { flex: 1 1 auto; min-width: 0; max-width: none; order: 0; }
  /* ...and only the two controls that have nowhere else to live sit beside it.
     Settings, AI Catalog and the account block are all in the navigation drawer
     (and in the bottom bar), so nothing becomes unreachable - sign-out included. */
  .header-actions { flex: 0 0 auto; flex-wrap: nowrap; gap: 2px; margin-left: 0; }
  .header-actions #settingsBtn,
  .header-actions #aiCatalogBtn,
  .header-actions .user-menu { display: none; }

  /* --- 2. A compact page header --------------------------------------- */
  .page-header {
    display: flex; flex-direction: column; align-items: stretch;
    flex-wrap: nowrap; gap: 6px; padding: 8px; margin-bottom: 12px;
  }
  .page-subtitle {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1;
    overflow: hidden; margin-top: 0;
  }
  /* One horizontally scrollable row of actions, never three stacked rows. */
  .page-actions { display: flex; flex-wrap: nowrap; gap: 6px; overflow-x: auto; padding-bottom: 2px; }
  .page-actions > * { flex: 0 0 auto; }

  /* --- 3. The strict flex column -------------------------------------- */
  .app { height: 100dvh; display: flex; flex-direction: column; }
  .main { flex: 1 1 auto; min-height: 0; }
  /* The chat page fits the space it is given, so nothing in it needs to scroll
     except the transcript. `height: 100%` resolves against .main, which is a flex
     item with a definite height. */
  .ai-chat-page { height: 100%; min-height: 0; display: flex; flex-direction: column; gap: 8px; }
  .ai-chat-layout {
    flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
    gap: 8px; position: relative;
  }
  .ai-chat-main { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: 8px; }
  /* THE only scroller. `min-height: 0` (not a fixed floor) is what makes the page fit at
     ANY height: the flex algorithm gives the transcript exactly the space the dock and the
     chrome leave over (259px at 360x740, 94px on a 320x568 phone), so the composer can
     never be pushed under the bottom bar. A hard min-height pushed the dock past the
     bottom of the page on short phones, which is exactly the overlap this layout removes. */
  .chat-messages {
    flex: 1 1 auto; min-height: 0; height: auto; max-height: none;
    overflow-y: auto; overscroll-behavior: contain;
  }
  /* The conversation list is an overlay over the transcript, never a second column
     (and never a flex sibling that would take the transcript's height). */
  .ai-conv-drawer { position: absolute; top: 0; left: 0; right: 0; z-index: 5; }
  .ai-conv-drawer .ai-conv-panel { max-height: 60dvh; }

  /* --- 4. The dock: in flow, opaque, directly above the bottom bar ----- */
  .chat-dock {
    position: relative; flex-shrink: 0;
    /* `bottom: auto` matters: the 700px block above sets `bottom: var(--bottom-nav-h)`
       for the OLD sticky dock, and on a relatively positioned element that shifts the
       whole dock UP by 56px (thread content then shows under it). The dock is in flow
       now, so it has no offset at all. */
    bottom: auto; margin: 0;
    gap: 6px; padding: 6px 8px calc(6px + env(safe-area-inset-bottom, 0px));
    background: var(--bg-surface); border-top: 1px solid var(--border);
  }
  .chat-dock .chat-status:empty { display: none; }
  /* It floats just above the dock, over the transcript it belongs to. */
  .chat-jump-latest {
    position: absolute; top: -40px; left: 50%; transform: translateX(-50%);
    align-self: auto;
  }
  /* One scrollable row of quick chips. */
  .chat-quick-actions { display: flex; flex-wrap: nowrap; gap: 6px; overflow-x: auto; padding-bottom: 2px; }
  .chat-quick-actions .btn { flex: 0 0 auto; }
  .chat-actions { display: flex; flex-wrap: nowrap; justify-content: flex-end; gap: 6px; }
  .chat-actions .btn { flex: 0 1 auto; }
  /* The labels give way so 📷 / 🖼 / Send stay on ONE row (each button keeps its
     icon, its tooltip and its accessible name). */
  .chat-btn-label, .chat-hint { display: none; }
  .chat-input-area { position: static; margin: 0; padding: 0; background: none; border-top: 0; }
  .chat-input-area textarea { min-height: 40px; max-height: 120px; }
}
