:root {
  --ink: #111;
  --muted: #8a8a8e;
  --line: #ececee;
  --fill: #f5f5f7;
  --blue: #0071e3;
  --green: #34c759;
  --red: #ff3b30;
  --red-ink: #b3261e;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  padding: 0 0 132px;
  background: #fff;
  color: var(--ink);
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI",
    Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

.wrap { max-width: 560px; margin: 0 auto; padding: 22px 18px 0; }

h1 { font-size: 26px; font-weight: 700; letter-spacing: -0.02em; margin: 6px 0 2px; }
.sub { font-size: 13px; color: var(--muted); margin: 0 0 18px; }

.head-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.help-link { flex: 0 0 auto; font-size: 13px; font-weight: 600; padding: 7px 14px;
  background: var(--fill); color: var(--ink); border-radius: 980px; text-decoration: none; }

.card { background: var(--fill); border-radius: 18px; padding: 15px 17px; margin-bottom: 18px; }
.row { display: flex; justify-content: space-between; align-items: center; }
.row + .row { margin-top: 9px; }
.label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; }
.value { font-size: 15px; font-weight: 600; }

.dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--muted); margin-right: 6px; vertical-align: middle; }
.dot.on { background: var(--green); }
.dot.bad { background: var(--red); }

.section { font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; margin: 22px 0 11px; }

.photo { margin-bottom: 22px; }
.photo img { width: 100%; border-radius: 14px; display: block; background: var(--fill); }
.time { font-size: 15px; font-weight: 600; color: var(--ink); margin: 10px 0 4px; }
.desc { font-size: 14px; line-height: 1.45; }
.desc.waiting { color: var(--muted); font-style: italic; }
.desc.failed { color: var(--red); }
/* The replay speaker, inline at the end of the description.
 *
 * Every property here is undoing the global `button` rule (width: 100%,
 * background: var(--blue), color: #fff, padding: 14px) - without them this
 * renders as a full-width blue slab under every description. Both background
 * AND color are restated: changing only the background paints white on white,
 * which is how the ⋯ menu once shipped invisible (PITFALLS 16 and 21). */
.desc-speak {
  width: auto; display: inline-block; vertical-align: baseline;
  background: none; color: #6b6b6b; font-size: 14px; line-height: 1;
  padding: 2px 4px; margin-left: 6px;
}
.tags { margin-top: 8px; display: flex; gap: 6px; flex-wrap: wrap; }
.tag { font-size: 11px; background: var(--fill); color: #6e6e73; border-radius: 980px; padding: 3px 9px; }

.photo a { display: block; }
.photo-actions { margin-top: 10px; display: flex; gap: 8px; }
.photo-actions a, .photo-actions button {
  width: auto; flex: 0 0 auto; font-size: 13px; font-weight: 600;
  border-radius: 980px; padding: 7px 14px; text-decoration: none;
  background: var(--fill); color: var(--ink); border: 0; cursor: pointer;
}
.photo-actions button.delete { background: #fff0ef; color: var(--red); }

.empty { color: var(--muted); text-align: center; padding: 40px 0; }

.bar {
  position: fixed; left: 0; right: 0; bottom: 0;
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: saturate(180%) blur(18px);
  border-top: 1px solid var(--line);
  padding: 12px 18px calc(16px + env(safe-area-inset-bottom));
}
.bar-inner { max-width: 560px; margin: 0 auto; }

button {
  width: 100%; font: inherit; font-weight: 600; font-size: 16px;
  background: var(--blue); color: #fff; border: 0; border-radius: 980px;
  padding: 14px; cursor: pointer;
}
button:disabled { background: #c7c7cc; cursor: default; }

.progress { height: 3px; background: #e8e8ea; border-radius: 3px; margin-top: 10px; overflow: hidden; }
.progress > i { display: block; height: 100%; width: 0; background: var(--blue); border-radius: 3px; transition: width 0.25s ease; }
.progress-text { font-size: 11px; color: var(--muted); text-align: center; margin-top: 6px; min-height: 15px; }

.notice { background: #fff8e8; border: 1px solid #f0c266; border-radius: 14px; padding: 13px 15px; font-size: 13px; margin-bottom: 18px; }
.notice.error { background: #fff0ef; border-color: #f5a9a4; }
.notice a { color: var(--blue); }

.app-bar { text-align: center; padding: 10px 8px 0; }
.hint { color: var(--muted); font-size: 12px; text-align: center; padding: 16px 8px 0; line-height: 1.5; }

.subrow { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.subrow .sub { margin: 0; }
.lang-control { display: flex; align-items: center; gap: 8px; }
.translating-hint { font-size: 12px; color: var(--muted); font-style: italic; }
#language { font: inherit; font-size: 14px; padding: 6px 10px; border: 1px solid #d8d8dc; border-radius: 10px; background: var(--fill); color: var(--ink); }
#language:disabled { color: var(--muted); }

.folder-row { display: flex; align-items: center; gap: .55rem; width: 100%;
  padding: .7rem .8rem; border: 0; border-bottom: 1px solid var(--line, #e3e3e7);
  background: none; font: inherit; color: inherit; text-align: left;
  text-decoration: none; }
.folder-row .count { margin-left: auto; opacity: .6; font-size: .85em; }
.folder-row.new-folder { color: #0071e3; }
.folder-item { display: flex; align-items: center; }
.folder-item .folder-row { flex: 1 1 auto; min-width: 0; }
.row-menu { width: auto; flex: 0 0 auto; background: none; border: 0;
  color: #6b6b6b; font-size: 1.25rem; line-height: 1; padding: .55rem .7rem;
  cursor: pointer; }
.row-menu:hover { color: #111; }
.share-badge { margin-left: .35rem; font-size: .85em; }
.latest { border: 1px solid #0071e3; border-radius: 12px; overflow: hidden;
  margin-bottom: 1rem; }
.latest-img { width: 100%; display: block; }
.latest-body { padding: .6rem .75rem; }
.latest-label { font-size: .7rem; text-transform: uppercase; letter-spacing: .05em;
  color: #0071e3; font-weight: 700; }
.latest-desc { margin: .25rem 0 0; }
.latest-desc.pending { opacity: .6; }
.session-row { display: flex; gap: .4rem; overflow-x: auto; margin-bottom: 1rem; }
.session-row .thumb { flex: 0 0 auto; width: 56px; height: 56px; padding: 0;
  border: 0; border-radius: 8px; overflow: hidden; background: none; }
.session-row .thumb img { width: 100%; height: 100%; object-fit: cover; }
.load-more { width: 100%; padding: .7rem; margin-top: .5rem; }
/* The global `button` rule is a full-width blue pill, which is right for the
   one call to action on the page and wrong for everything here. Without the
   four rules below, "Select" looked like a page-level call to action and
   "Delete" looked EXACTLY like "Move to..." - a destructive bulk action
   wearing the same clothes as a benign one. Found by opening the page; no
   unit test can see an unstyled class. */
.linkish { width: auto; flex: 0 0 auto; background: none; color: var(--blue);
  padding: .5rem .4rem; font-size: 14px; }
.select-bar { display: flex; align-items: center; gap: .6rem; padding: .5rem .2rem; }
.select-bar button { width: auto; flex: 0 0 auto; background: none;
  color: var(--blue); padding: .4rem .5rem; font-size: 15px; }
.select-count { flex: 1; font-size: 14px; color: var(--muted); }
.primary { background: var(--blue); color: #fff; }
.danger { background: #fff0ef; color: var(--red); }

/* Fixed, not sticky. `position: sticky; bottom: 0` sticks only while the
   element's own containing block is on screen, and this bar renders ABOVE the
   photo list - so after scrolling a folder of 60 photos the customer had
   selected some and had no Move or Delete anywhere on screen. It also has to
   out-stack .bar, the fixed Connect bar, for the same reason .sheet does; it
   stays BELOW .sheet-backdrop so the move sheet still dims it. Covering the
   Connect bar while selecting is deliberate: you are filing photos, not
   taking them. */
.select-actions { display: flex; gap: .5rem; justify-content: center;
  position: fixed; z-index: 35; left: 0; right: 0; bottom: 0; background: #fff;
  border-top: 1px solid var(--line);
  padding: 10px 18px calc(12px + env(safe-area-inset-bottom)); }
/* max-width keeps the pair the same width as .bar-inner on a desktop screen
   while the white background still spans it. */
.select-actions button { flex: 1 1 0; max-width: 276px; padding: .7rem; }
/* .bar is taller than .select-actions, so it pokes out above it. Select mode
   is the one moment the Connect bar has nothing to offer, and .select-actions
   exists only in select mode - so its presence is the condition. */
body:has(.select-actions) .bar { display: none; }
/* Both sit above .bar, the fixed Connect bar. All three are position:fixed at
   the bottom of the screen, and .bar comes LATER in the DOM, so with the
   default z-index:auto it painted on top and the move sheet was invisible -
   the customer tapped "Move to..." and got a dimmed screen and nothing else.
   Found by opening the page; no unit test can see a stacking-order bug. */
.sheet-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.35); z-index: 40; }
.sheet { position: fixed; left: 0; right: 0; bottom: 0; background: #fff; z-index: 50;
  border-radius: 14px 14px 0 0; padding: .5rem 0 max(.5rem, env(safe-area-inset-bottom));
  max-height: 70vh; overflow-y: auto; }
.sheet-title { padding: .4rem .8rem; opacity: .6; font-size: .85em; }
/* Explicit color, not inherited: the global `button` rule (above) sets
   color: #fff, and .sheet sits on a white background - without this every
   row in the ⋯ menu and the move sheet rendered white-on-white, readable
   only by clicking blind. .sheet-row.danger below restores the destructive
   red for "Delete folder": .danger (above) sets the same color, but
   .sheet-row comes later in the file, so at equal specificity .sheet-row's
   plain color would otherwise win and Delete folder would read as a normal
   row. */
.sheet-row { display: block; width: 100%; padding: .8rem; border: 0; background: none;
  font: inherit; text-align: left; color: var(--ink); }
.sheet-row.danger { color: var(--red); }
/* glassesPickerHtml's empty-scan message, matching .sheet-row's own padding
   so "No glasses found" does not sit flush against the sheet's edges. NOT
   given its own color/background: .sheet-row above already fixed the
   white-on-white bug for every row in this sheet, including the picker's,
   and .sheet already carries z-index:50 above .bar - redeclaring either here
   would only risk re-introducing what was already fixed once. */
.sheet-panel p { padding: .8rem; opacity: .6; }
.share-url { padding: .5rem .8rem; font-family: ui-monospace, Menlo, monospace;
  font-size: .8rem; word-break: break-all; color: #333; }
.share-count { float: right; opacity: .8; }
.share-toggle { display: flex; align-items: center; justify-content: space-between;
  padding: .7rem .8rem; font-size: .95rem; }
.share-toggle input { width: auto; flex: 0 0 auto; }
.share-opened { padding: .3rem .8rem .6rem; opacity: .6; font-size: .85em; }
.sheet .primary, .sheet .linkish { margin: .4rem .8rem; }
.view-header { display: flex; align-items: center; gap: .5rem; margin: .5rem 0 .2rem; }
.view-header .back { color: var(--blue); text-decoration: none; font-size: 20px;
  line-height: 1; padding: .2rem .3rem; }
.view-header .view-name { font-weight: 600; font-size: 17px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
/* Pushes the folder's ⋯ to the end of the header, the same way .folder-row
   .count is pushed to the end of a folder row - .row-menu itself is already
   flex: 0 0 auto (see above), it just has no reason to sit anywhere but the
   trailing edge here. */
.view-header .row-menu { margin-left: auto; }
.trash-header { display: flex; align-items: center; justify-content: space-between;
  gap: .6rem; margin: .5rem 0; }
.trash-header .trash-note { color: var(--muted); font-size: .85em; }
.trash-header button { flex: 0 0 auto; padding: .5rem .8rem; }
.lib-head { display: flex; gap: .5rem; align-items: center; margin: .5rem 0; }
.lib-head .search { flex: 1; padding: .5rem .7rem; border-radius: 8px;
  border: 1px solid var(--line, #d1d1d6); font: inherit; }

.devices { display: flex; gap: 8px; margin-bottom: 18px; }
.device {
  flex: 1; font: inherit; font-size: 14px; padding: 10px 12px;
  border: 1px solid #d8d8dc; border-radius: 12px;
  background: var(--fill); color: var(--muted);
}
.device[aria-pressed="true"] { background: var(--blue); border-color: var(--blue); color: #fff; }

.stage {
  position: relative; width: 100%; aspect-ratio: 4 / 3;
  background: #000; border-radius: 14px; overflow: hidden; margin-bottom: 14px;
}
.stage video { position: absolute; top: 50%; left: 50%; transform-origin: center center; }

.settings { margin-bottom: 18px; font-size: 14px; }
.settings summary { color: var(--blue); cursor: pointer; padding: 4px 0; }
.settings .controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 10px 0; }
.settings select { font: inherit; font-size: 14px; padding: 6px 10px; border: 1px solid #d8d8dc; border-radius: 10px; background: var(--fill); color: var(--ink); }
.settings button {
  font: inherit; font-size: 13px; padding: 7px 12px; width: auto;
  border: 1px solid #d8d8dc; border-radius: 10px; background: var(--fill); color: var(--ink);
}
.settings button[aria-pressed="true"] { border-color: var(--blue); color: var(--blue); }

#retryHost { margin-bottom: 18px; }

/* --red is about 3:1 on white, which is under WCAG AA for body text. It stays
   as the badge and border red; --red-ink is the darker one text uses. */
.rfid-broken { color: var(--red-ink); font-size: 13px; }
.rfid-dest { color: var(--ink); font-size: 13px; }
.rfid-code { font-size: 12px; color: var(--muted); font-family: ui-monospace, monospace; }
#scan { font: inherit; font-size: 20px; padding: 16px 18px; width: 100%;
        border: 1px solid #d8d8dc; border-radius: 14px; }

/* Per-photo AI: the toggle, the panel, the editable result, saved answers.
 *
 * Every button rule here sets BOTH background and color. The global `button`
 * rule is `background: var(--blue); color: #fff`, so a rule that changes only
 * the background paints white text on it - which is exactly how the ⋯ menu
 * shipped invisible once already (see tests/style.test.js). */
.ask { margin-top: 10px; font-size: 13px; }
.ask-toggle { display: flex; align-items: center; gap: 6px; cursor: pointer;
  color: var(--ink); }
.ask .ask-body { display: none; }
.ask.open .ask-body { display: block; margin-top: 8px; }
.ask-item { width: 100%; padding: 8px 10px; font: inherit; color: var(--ink);
  background: #fff; border: 1px solid var(--line); border-radius: 10px; }
.ask-buttons { display: flex; gap: 8px; margin-top: 8px; }
.ask-buttons button { flex: 1; width: auto; font-size: 13px; padding: 8px;
  background: var(--fill); color: var(--ink); }
.ask-rule { border: 0; border-top: 1px solid var(--line); margin: 12px 0; }
.ask-describe { font-size: 13px; padding: 8px; background: var(--fill);
  color: var(--ink); }
.ask-asked { color: var(--muted); font-size: 12px; }
.ask-value { font-size: 30px; font-weight: 700; width: 100%; text-align: center;
  font-family: inherit; color: var(--ink); background: transparent;
  border: 1px solid transparent; border-radius: 10px; padding: 4px; }
.ask-value:focus { border-color: var(--blue); background: #fff; }
.ask-note { width: 100%; margin-top: 6px; font: inherit; color: var(--ink);
  background: #fff; border: 1px solid var(--line); border-radius: 10px;
  padding: 8px 10px; }
.ask-answer-actions { display: flex; gap: 8px; margin-top: 8px; }
.ask-answer-actions button { width: auto; flex: 0 0 auto; font-size: 13px;
  padding: 7px 14px; background: var(--fill); color: var(--ink); }
.ask-answer-actions button.primary { background: var(--blue); color: #fff; }
.ask-answer.absent .ask-value { color: var(--red-ink); }
.ask-waiting { padding: 8px 0; color: var(--muted); }
.ask-failed { padding: 8px 0; color: var(--red-ink); }
.ask-answers { list-style: none; padding: 0; margin: 8px 0 0; }
.ask-saved { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
  padding: 6px 0; border-top: 1px solid var(--line); }
.ask-saved-q { color: var(--muted); font-size: 12px; }
.ask-saved-v { font-weight: 700; font-size: 16px; color: var(--ink); }
.ask-was { color: var(--muted); font-size: 11px; }
.ask-saved-n { flex-basis: 100%; color: #545458; }
.ask-speak, .ask-del { width: auto; flex: 0 0 auto; margin-left: auto;
  background: none; color: #6b6b6b; font-size: 15px; line-height: 1;
  padding: 4px 6px; }
.ask-del { margin-left: 0; }
.ask-export { margin-left: auto; font-size: 12px; color: var(--blue); }
