/* Mobile-first styling for the inventory system.
   Rule of thumb: nothing scrolls sideways, and every control a person taps
   on the shop floor is at least 44px tall. */

:root {
  --bg: #f6f5f2;
  --surface: #ffffff;
  --ink: #1d1b18;
  --muted: #6b6560;
  --line: #ddd8d0;
  --accent: #7a5c3e;
  --accent-ink: #ffffff;
  --ok-bg: #e6f2e6;
  --ok-ink: #1f5220;
  --warn-bg: #fdf1dc;
  --warn-ink: #6b4a10;
  --err-bg: #fbe6e4;
  --err-ink: #7a2018;
  --info-bg: #e4eef7;
  --info-ink: #1f4a6b;
  --radius: 10px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

a { color: var(--accent); }

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

.topbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.brand { font-weight: 700; text-decoration: none; color: var(--ink); }

.nav, .account { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.account { margin-left: auto; font-size: 14px; color: var(--muted); }
.nav a, .account a { text-decoration: none; padding: 6px 2px; }
.nav a:hover, .account a:hover { text-decoration: underline; }

.nav-cta {
  background: var(--accent);
  color: var(--accent-ink) !important;
  padding: 8px 12px !important;
  border-radius: var(--radius);
}

.who { font-weight: 600; color: var(--ink); }

.page { max-width: 1100px; margin: 0 auto; padding: 16px; }

h1 { font-size: 22px; margin: 0 0 4px; }
h2 { font-size: 17px; margin: 24px 0 8px; }

.subtitle { color: var(--muted); margin: 0 0 16px; font-size: 14px; }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 16px;
}

.row { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; }
/* Rows whose fields carry help text: bottom-aligning them would line up
   the help rather than the inputs, lifting that field above the rest. */
.row-top { align-items: flex-start; }
.grow { flex: 1 1 220px; }
.spread { display: flex; flex-wrap: wrap; gap: 12px; justify-content: space-between; align-items: center; }

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

.messages { list-style: none; padding: 0; margin: 0 0 16px; }
.msg { padding: 10px 14px; border-radius: var(--radius); margin-bottom: 8px; }
.msg-success { background: var(--ok-bg); color: var(--ok-ink); }
.msg-info { background: var(--surface); border: 1px solid var(--line); }
.msg-warning { background: var(--warn-bg); color: var(--warn-ink); }
.msg-error { background: var(--err-bg); color: var(--err-ink); }

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

.field { margin-bottom: 14px; }
.field label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 4px; }
.field .help { font-size: 12px; color: var(--muted); margin-top: 4px; }
.field .errors { color: var(--err-ink); font-size: 13px; margin: 4px 0 0; padding-left: 18px; }

input[type=text], input[type=search], input[type=password], input[type=number],
input[type=date], input[type=email], select, textarea {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  font-size: 16px; /* 16px keeps iOS from zooming on focus */
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
}

textarea { min-height: 80px; }

/* A fixed, non-editable prefix joined to an input - the parent's code beside
   the segment being typed. */
.input-group { display: flex; align-items: stretch; }
.input-group input { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.input-group input:only-child {
  border-top-left-radius: var(--radius);
  border-bottom-left-radius: var(--radius);
}

.input-prefix {
  display: flex;
  align-items: center;
  padding: 0 12px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 16px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--muted);
  background: var(--bg);
  border: 1px solid var(--line);
  border-right: none;
  border-radius: var(--radius) 0 0 var(--radius);
}
.input-prefix[hidden] { display: none; }

/* --- Rows of locations ------------------------------------------------
   On a phone each row stacks: code, then name, then Remove. Nothing is
   narrowed to fit, and nothing scrolls sideways. The column headings only
   make sense once the row is actually laid out in columns, so they appear
   with the wider layout. */

.rows-header { display: none; }

.location-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  margin-bottom: 10px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.location-row input { width: 100%; }
.location-row .row-remove { align-self: flex-start; }
.location-row .row-remove[hidden] { display: none; }

#add-row { margin-bottom: 16px; }

@media (min-width: 620px) {
  .rows-header {
    display: flex;
    gap: 12px;
    padding: 0 12px 4px;
    font-size: 13px;
    font-weight: 600;
  }
  .rows-header span:first-child { flex: 1 1 40%; }
  .rows-header span:last-child { flex: 1 1 60%; }
  .rows-header em { font-weight: 400; font-style: normal; color: var(--muted); }

  .location-row {
    flex-direction: row;
    align-items: center;
    background: transparent;
    border: none;
    padding: 0 12px;
    margin-bottom: 8px;
  }
  .location-row-code { flex: 1 1 40%; min-width: 0; }
  .location-row-name { flex: 1 1 60%; min-width: 0; }
  .location-row .row-remove { align-self: auto; flex: none; }
}

/* --- File inputs -------------------------------------------------------
   One box the width of the form: the file's name, then Browse. The real
   input is moved inside and made transparent, still covering the whole box,
   so it keeps the click, the keyboard and the camera. Transparent and not
   hidden on purpose - display:none or visibility:hidden would take the
   click with it. */

.filepick {
  position: relative;
  display: flex;
  align-items: stretch;
  min-height: 44px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

.filepick input[type=file] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

.filepick-name {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  padding: 0 12px;
  font-size: 16px; /* as elsewhere, so iOS does not zoom */
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.filepick-chosen { color: var(--ink); }

.filepick-browse {
  display: flex;
  align-items: center;
  padding: 0 18px;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  background: var(--bg);
  border-left: 1px solid var(--line);
}

.filepick:hover { border-color: var(--accent); }
/* The input carries the focus but cannot show it, being transparent. */
.filepick:focus-within { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }

input[type=checkbox] { width: 20px; height: 20px; vertical-align: middle; }
.check label { display: inline; font-weight: 400; }

.btn, button, input[type=submit] {
  min-height: 44px;
  padding: 10px 18px;
  font-size: 15px;
  font-weight: 600;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

.btn-primary, button.btn-primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.btn-danger { color: var(--err-ink); border-color: var(--err-ink); background: var(--surface); }
.btn-block { width: 100%; }

.linkish {
  background: none; border: none; padding: 0; min-height: 0;
  color: var(--accent); font: inherit; font-weight: 400; cursor: pointer; text-decoration: none;
}
.linkish:hover { text-decoration: underline; }

.actions { display: flex; flex-wrap: wrap; gap: 10px; margin: 16px 0; }
/* Inside a card, or beside a heading, the buttons are the last thing in a box
   that already has its own padding - a bottom margin there is a gap inside the
   box rather than between boxes. Standing on their own, as the toolbar under
   an item's or a location's name, they need one or they sit flush against
   whatever comes next. */
.card .actions, .spread .actions { margin-bottom: 0; }
/* Buttons inside a table row. Narrower, but never shorter: a control that
   loses its height is the one nobody can hit on a phone. */
.btn-small { padding: 6px 12px; font-size: 14px; }

/* A split button: the ordinary press on the left, the other ways of doing it
   behind the arrow. <details> rather than a script, so it opens with the
   keyboard, closes on a second press and works before any JavaScript has
   loaded. The script only teaches it the one thing <details> does not know -
   that a menu shuts when you click away from it. */
.split { position: relative; display: inline-flex; }
.split-main { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.split-toggle {
  border-top-left-radius: 0; border-bottom-left-radius: 0;
  margin-left: -1px; /* one border between the halves rather than two */
  padding-left: 12px; padding-right: 12px;
  list-style: none;
}
.split-toggle::-webkit-details-marker { display: none; }
.split-more[open] .split-toggle { background: var(--bg); }

.split-menu {
  position: absolute; z-index: 5;
  /* Lined up with the left edge of the button rather than the right edge of
     the arrow. These actions are left-aligned and wrap, so a button is nearly
     always near the left of the screen: anchoring to the right put a 220px
     menu at x = -35, off the edge of the page. */
  top: calc(100% + 4px); left: 0;
  min-width: 220px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.split-menu a {
  display: flex; align-items: center;
  min-height: 44px; /* a menu on a phone is tapped, not clicked */
  padding: 0 12px;
  border-radius: 8px;
  color: inherit; text-decoration: none;
  font-size: 15px; white-space: nowrap;
}
.split-menu a:hover, .split-menu a:focus { background: var(--bg); }
/* width:1% makes a table cell shrink to its content, so the actions take the
   space they need and the title column gets the rest. */
.actions-cell { width: 1%; white-space: nowrap; }
/* Columns worth having on a desk and not worth pushing the buttons off the
   edge of a phone. */
.col-wide { display: none; }
/* Stacked on a phone, side by side on a desk. Side by side at 375px puts the
   second button off the edge of the screen, which is how a working control
   becomes an invisible one. */
.row-actions { display: flex; flex-direction: column; gap: 8px; align-items: stretch; }
.row-actions form { margin: 0; }
.actions form { margin: 0; }

/* Something waiting to be done, said quietly. Not an error - nothing is
   wrong - and not a message that came from an action, so neither of those
   shapes fits it. */
.notice {
  background: var(--info-bg);
  color: var(--info-ink);
  padding: 10px 14px;
  border-radius: var(--radius);
  margin: 0 0 14px;
  font-size: 14px;
}

.nonfield { background: var(--err-bg); color: var(--err-ink); padding: 10px 14px; border-radius: var(--radius); margin-bottom: 14px; }

/* --- Item list ------------------------------------------------------- */

.results { display: grid; gap: 10px; }

.result {
  position: relative; /* the item's name is stretched across this */

  /* One number for the picture and the column it sits in, so the two can
     never disagree. It grows with the screen: on a phone the width has to be
     shared with the name and the pills, on a desktop it does not. This list
     is read by eye more than by asset number, and 56px was a favicon - too
     small to tell one brass lamp from another without opening both. */
  --thumb: 80px;

  display: grid;
  /* Two columns on a phone. A third one left about ninety pixels for the
     name and every pill, so a two-word name wrapped and each pill took a
     line of its own. The price and the status go under the name instead,
     and the third column comes back as soon as there is room for it. */
  grid-template-columns: var(--thumb) 1fr;
  gap: 4px 12px;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
  text-decoration: none;
  color: inherit;
}

.result:hover { border-color: var(--accent); }

/* Stacks the number, name and place, each sized to its own content so a pill
   is not stretched across the card. */
.result-body { min-width: 0; display: flex; flex-direction: column; align-items: flex-start; }

/* The name is the link; this stretches it over the whole card so anywhere on
   the row still opens the item. Anything meant to take its own taps has to
   be drawn above it - see .sku-copy and .pill-link. */
.result-link { color: inherit; text-decoration: none; font-weight: 600; display: block; }
.result-link::after { content: ""; position: absolute; inset: 0; }
.result:hover .result-link { text-decoration: underline; }

/* Holds the card and, when the list is selectable, the checkbox beside it. */
.result-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }

/* Beside the card, not inside it. The item's name is stretched across the
   card, so a checkbox in there would open the item when ticked. */
/* These are laid out with flex, and an author display rule beats the
   display:none the hidden attribute relies on. Without this they are marked
   up hidden and shown anyway - which is the whole point of hiding them. */
.pick[hidden], .pick-all[hidden], .bulk-bar[hidden] { display: none; }

.pick {
  /* Shift-clicking a range would otherwise drag a text selection across the
     rows it spans. */
  user-select: none;
  -webkit-user-select: none;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  cursor: pointer;
}
.pick input { width: 20px; height: 20px; margin: 0; cursor: pointer; }

.pick-all {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 4px;
  font-size: 14px;
  color: var(--muted);
  cursor: pointer;
}
.pick-all input { width: 20px; height: 20px; margin: 0; cursor: pointer; }

/* Sticky, because a selection is made while scrolling a long list and the
   thing to do with it must not be at the bottom of that list. */
.bulk-bar {
  position: sticky;
  bottom: 10px;
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  justify-content: space-between;
  margin: 10px 0;
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
}
.bulk-said { display: flex; flex-direction: column; }
.bulk-count { font-weight: 600; }
.bulk-elsewhere { font-size: 13px; color: var(--muted); }
.bulk-elsewhere[hidden] { display: none; }
.bulk-capped { font-size: 13px; color: var(--muted); }
/* Something not known rather than something known to be nothing. */
.subtle { color: var(--muted); font-style: italic; }
.bulk-capped[hidden] { display: none; }
.bulk-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.result-row .result { flex: 1 1 260px; min-width: 0; }


/* The photograph a visual match was actually made on, beside the row. Not the
   item's primary picture: the point is to show which of its pictures looked
   like the one in hand, so a wrong match is obvious at a glance rather than
   after opening the item. */
.match-why {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 6px;
  text-decoration: none;
  color: var(--muted);
  font-size: 11px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.match-why img {
  width: 56px; height: 56px; object-fit: cover;
  border-radius: 6px; display: block;
}
.match-why:hover { border-color: var(--accent); }

.thumb {
  grid-row: 1 / -1; /* beside both lines when the price wraps under the name */
  width: var(--thumb, 56px); height: var(--thumb, 56px);
  border-radius: 8px;
  object-fit: cover;
  background: var(--bg);
  border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  color: var(--muted); font-size: 11px;
}

.result .sku { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 13px; color: var(--muted); }


.icon { width: 13px; height: 13px; flex: none; fill: none; stroke: currentColor;
        stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }

/* The name link is stretched over the whole card, so anything meant to take
   its own taps has to be drawn above it. */
.sku-copy {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 3px;
  min-height: 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
.sku-copy .icon-done { display: none; }
.sku-copy.is-copied { color: var(--ok-ink); }
.sku-copy.is-copied .icon-copy { display: none; }
.sku-copy.is-copied .icon-done { display: block; }

/* Small enough not to shout in a list of twenty-five, with a hit area larger
   than it looks. A control that is 18px tall is one nobody can press on a
   phone - and here a mis-tap opens the item instead, which is worse than
   nothing happening. */
/* Takes a small mark past a 44px tap target without moving anything. */
.sku-copy::before { content: ""; position: absolute; inset: -14px -13px; }
.pill-link::before { content: ""; position: absolute; inset: -13px -6px; }
.sku-copy:hover, .sku-copy:focus-visible { background: var(--accent); color: var(--accent-ink); }
.sku-copy.is-copied:hover { background: none; color: var(--ok-ink); }
.result .name { font-weight: 600; }
.result .meta { font-size: 13px; color: var(--muted); }
.result .right {
  grid-column: 2; /* under the name on a phone, beside it on a desktop */
  display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: center;
  font-size: 13px;
}
.result .price { font-weight: 600; font-size: 15px; }

/* Wider than a phone: a bigger picture, and the price back in its own column. */
@media (min-width: 620px) {
  .result {
    --thumb: 104px;
    grid-template-columns: var(--thumb) 1fr auto;
    gap: 12px;
  }
  .result .right { grid-column: auto; display: block; text-align: right; }
  .result .price { display: block; }
}

.pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  background: var(--bg);
  border: 1px solid var(--line);
  color: var(--muted);
}
.pill-available { background: var(--ok-bg); color: var(--ok-ink); border-color: transparent; }
.pill-sold { background: var(--err-bg); color: var(--err-ink); border-color: transparent; }
.pill-on_hold, .pill-in_repair { background: var(--warn-bg); color: var(--warn-ink); border-color: transparent; }
.pill-archived { background: #e9e6e1; color: var(--muted); border-color: transparent; }

/* Where an item is. Blue for somewhere, red for nowhere - so a shelf full of
   uncatalogued placements is visible running down the column rather than
   found by reading every row. */
.pill-where { background: var(--info-bg); color: var(--info-ink); border-color: transparent; }
.pill-nowhere { background: var(--err-bg); color: var(--err-ink); border-color: transparent; }
/* The asset number is on the listing. Same green as an available item,
   because it means the same kind of thing: nothing to do here. */
.pill-labelled { background: var(--ok-bg); color: var(--ok-ink); border-color: transparent; }
/* How many of a set are left. A count, not a verdict, so it is plain. */
.pill-stock { background: var(--bg); color: var(--muted); }

/* A promise the shop has already broken. Marked on the row rather than only
   on the date, so it is visible while walking past with a trolley. */
.result-late { border-color: var(--err-ink); }

/* A button inside a card whose name link is stretched over the whole thing.
   Without this the link is on top and pressing Picked opens the item instead
   - which is not a subtle failure, but it is an invisible one to any test
   that only reads the markup. Same reason .sku-copy and .pill-link are
   raised. */
.result-action { position: relative; z-index: 1; }

/* The asset number, and the one control that copies it. A pill so it reads as
   one thing at a glance down the column, with the icon inside it rather than
   a button beside it - twenty-five rows cannot spare the width for the word. */
.pill-sku {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 1px 4px 1px 8px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-weight: 400;
  letter-spacing: 0.02em;
}
.pill-sku .sku { font-size: 12px; }

/* A pill that is a link: the eBay listing, beside where the item is. Outlined
   rather than filled, deliberately - filled in the same blue as the location
   it sits next to, the two read as one thing, and only one of them is a
   control. Above the stretched name link for the same reason the copy control
   is. */
.pill-link {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--surface);
  color: var(--accent);
  border-color: var(--line);
  text-decoration: none;
}
.pill-link:hover, .pill-link:focus-visible { background: var(--accent); color: var(--accent-ink); }

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

/* --- Detail ---------------------------------------------------------- */

.detail-grid { display: grid; gap: 16px; grid-template-columns: 1fr; }

dl.facts { display: grid; grid-template-columns: minmax(120px, 32%) 1fr; gap: 8px 16px; margin: 0; }
dl.facts dt { color: var(--muted); font-size: 13px; }
dl.facts dd { margin: 0; overflow-wrap: anywhere; }
/* A fact with a copy button beside it, rather than under it. */
.fact-with-copy { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }

/* Both codes for the same SKU, side by side, each labelled for what reads it. */
.code-strip {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 16px;
  margin: 0 0 16px; padding: 12px;
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
}
.code-strip figure { margin: 0; }
.code-strip img { height: 48px; display: block; max-width: 100%; }
.code-strip .code-qr { height: 72px; width: 72px; }
.code-strip figcaption { margin-top: 4px; font-size: 11px; color: var(--muted); }
.code-strip-value { margin: 0 0 4px; font-size: 15px; font-weight: 600; }

.mono { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; }
.sku-big { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 20px; font-weight: 700; }

/* The number the intake form is holding. Read off the screen and typed into a
   listing on another site, so it is given room rather than tucked into the
   subtitle line. */
.held-sku {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  margin: 0 0 16px;
  padding: 10px 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.held-sku .sku-big { user-select: all; }

/* Sits beside a number rather than among the page's actions, so it is smaller
   and quieter than a normal button. Its label changes to say what happened,
   so it needs room for the longest of those without the layout jumping. */
.btn-copy {
  padding: 2px 10px;
  min-height: 0;
  font-size: 12px;
  min-width: 90px;
}
.held-sku-note { flex: 1 1 260px; font-size: 13px; color: var(--muted); }

.history { list-style: none; margin: 0; padding: 0; }
.history li { padding: 10px 0; border-bottom: 1px solid var(--line); font-size: 14px; }
.history li:last-child { border-bottom: none; }
.history .when { color: var(--muted); font-size: 13px; }

.photos { display: flex; flex-wrap: wrap; gap: 12px; }
.photo { margin: 0; width: 120px; }
.photo img {
  width: 120px; height: 120px; object-fit: cover;
  border-radius: 8px; border: 1px solid var(--line); display: block;
}
.photo-primary img { border-color: var(--accent); border-width: 2px; }
/* Stacked, not side by side: 120px cannot hold two controls a finger can
   hit, and side by side they were 43x18 with 26px between them. A tap that
   missed did nothing, and one that drifted onto the neighbour made the photo
   primary - which looks exactly like a Remove button that does not work. */
.photo figcaption {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  text-align: center;
  gap: 6px;
  margin-top: 6px;
  font-size: 12px;
}
.photo figcaption form { margin: 0; }

/* .linkish is deliberately small where it sits inside a sentence. Here it is
   the only way to act on a photo, so it gets the 44px this stylesheet asks
   for at the top, across the full width of the picture above it. */
.photo figcaption .linkish {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 44px;
  padding: 4px 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
}
.photo figcaption .linkish:hover { text-decoration: none; border-color: var(--accent); }
.linkish.danger { color: var(--err-ink); }
.photo figcaption .linkish.danger { border-color: #e9cdc9; }

/* --- Locations ------------------------------------------------------- */

.tree { list-style: none; margin: 0; padding: 0; }
.tree li {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  padding: 10px 0; border-bottom: 1px solid var(--line);
}
.tree li:last-child { border-bottom: none; }
.tree .code { font-family: ui-monospace, Menlo, Consolas, monospace; font-weight: 600; }
.tree .count { margin-left: auto; font-size: 13px; color: var(--muted); }
.crumbs { font-size: 13px; color: var(--muted); margin: 0 0 8px; }

/* --- Scan screen ----------------------------------------------------- */

.scan input[type=text] { font-size: 20px; min-height: 56px; letter-spacing: 0.04em; }
.scan .field label { font-size: 15px; }
.scan .step { font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); }

.scan-camera { width: 100%; margin: -4px 0 14px; }

/* The two jobs, offered as targets rather than as words in a dropdown. This
   page is read standing up, one-handed, often with something in the other
   hand, so the choice has to be hittable without looking twice. */
.mode-choice { display: grid; gap: 12px; margin: 16px 0 24px; }
.mode {
  display: block;
  padding: 18px 20px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
  color: inherit;
}
.mode:hover, .mode:focus { border-color: var(--accent); }
.mode-name { display: block; font-size: 22px; font-weight: 600; }
.mode-what {
  display: block; margin-top: 4px;
  font-size: 15px; color: var(--muted);
}
.mode .pill { margin-top: 10px; }

/* One location asked a level at a time. Wraps, because four levels of
   dropdown do not sit beside each other on a phone - and because the filter
   card already has four controls across it on a desk. */
.cascade { display: flex; flex-wrap: wrap; gap: 8px; }
.cascade select { flex: 1 1 150px; min-width: 0; }
/* A field holding a cascade wants more of a row than its neighbours. */
.grow-wide { flex: 2 1 340px; }

/* The one control on a screen somebody is looking at while holding a plate. */
.btn-big { min-height: 56px; font-size: 17px; }

@media (min-width: 620px) {
  .mode-choice { grid-template-columns: 1fr 1fr; }
}

.scan-overlay {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(0, 0, 0, 0.85);
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
}
.scan-overlay-inner { width: min(520px, 100%); text-align: center; }
.scan-overlay video {
  width: 100%; max-height: 70vh; border-radius: var(--radius); background: #000;
}
.scan-overlay-hint { color: #fff; font-size: 14px; margin: 12px 0; }

/* --- Import / export ------------------------------------------------- */

.export-list, .plain-list { list-style: none; margin: 0; padding: 0; }
.export-list li {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
  padding: 8px 0;
}
.plain-list li { padding: 8px 0; border-bottom: 1px solid var(--line); }
.plain-list li:last-child { border-bottom: none; }
.plain-list .meta, .export-list .meta { color: var(--muted); font-size: 13px; }

.map-grid { display: grid; gap: 0 16px; grid-template-columns: 1fr; }

.tally { display: flex; flex-wrap: wrap; gap: 24px; margin-bottom: 16px; }
.tally-item { display: flex; flex-direction: column; }
.tally-number { font-size: 28px; font-weight: 700; line-height: 1.1; }
.tally-label { font-size: 13px; color: var(--muted); }
.tally-ok .tally-number { color: var(--ok-ink); }
.tally-bad .tally-number { color: var(--err-ink); }

/* Wide tables scroll inside their own box; the page never does. */
.scroll-x { overflow-x: auto; }

table.grid { border-collapse: collapse; width: 100%; font-size: 14px; }
table.grid th, table.grid td {
  text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
table.grid th { font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
table.grid tr.row-bad { background: var(--err-bg); }
/* A title long enough to push a row's buttons off a phone screen is better
   wrapped than scrolled sideways to reach. */
table.grid td.wrap { white-space: normal; min-width: 14ch; }

/* --- Pagination ------------------------------------------------------ */

.pager { display: flex; gap: 10px; align-items: center; justify-content: center; margin-top: 16px; flex-wrap: wrap; }
.pager .where { color: var(--muted); font-size: 14px; }

/* --- Wider screens --------------------------------------------------- */

@media (min-width: 760px) {
  .col-wide { display: table-cell; }
  .row-actions { flex-direction: row; align-items: center; }
  .detail-grid { grid-template-columns: 1.6fr 1fr; align-items: start; }
  .map-grid { grid-template-columns: 1fr 1fr; }
  dl.facts { grid-template-columns: 160px 1fr; }
}
