/* The Sell Pile — a lot sheet, not a storefront.
 *
 * One world, two registers. The public side is the sheet a buyer reads; the
 * owner side is the same sheet at work, denser and with the stamp pad open.
 * Materials: kraft board, manila paper, printed rules, rubber-stamp ink.
 * Nothing here is a card, a pill, or a badge. */

/* ---------------------------------------------------------------- faces --
 * Self-hosted, like htmx and the camera bundle: this is a PWA on condo wifi
 * and a webfont from a CDN is a blank page in a basement. Archivo is variable
 * on both weight and width, so the condensed sheet furniture and the prose
 * are one 90KB file. Courier carries only what a clerk would have typed. */

@font-face {
  font-family: "Archivo";
  src: url("/static/fonts/archivo-normal-400-800.woff2") format("woff2-variations");
  font-weight: 400 800;
  font-stretch: 62% 125%;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Courier Prime";
  src: url("/static/fonts/courier-prime-normal-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Courier Prime";
  src: url("/static/fonts/courier-prime-normal-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Courier Prime";
  src: url("/static/fonts/courier-prime-italic-400.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

/* ---------------------------------------------------------------- stock -- */

:root {
  /* Ink slate carries the page and manila is laid on top of it: the blotter
     under the paper, not more paper. A warm board tinted the sheet and the
     photographs both; a cool dark neutral lets the manila be the only warm
     thing on the screen, and lets a real photograph of a real room be the
     only colour. Ink clears 4.5:1 on every sheet ground below. */
  --kraft: #24313d;
  --kraft-deep: #1a242d;
  --sheet: #efe6d1;
  --sheet-sunk: #e5d9bd;

  --ink: #191510;
  /* Secondary is tinted out of the stock's own hue. A grey here would read as
     a different piece of paper. */
  --ink-2: #574b33;
  --ink-on-kraft: #efe6d1;

  --rule: #c0b091;
  --rule-strong: #8c7b58;
  --rule-kraft: #4e6377;

  /* One pad, six inks. Every state on this sheet is stamped in one of them. */
  --stamp: #5b3a8c;
  --stamp-wash: rgba(91, 58, 140, 0.13);
  --oxblood: #8e2a1e;
  --stamp-blue: #1f4e79;
  --stamp-green: #2f6b3c;
  --stamp-sepia: #7a4b1e;
  --graphite: #3c3730;

  --tag-hole: #8a7a58;

  /* The tooth of the board, as two hairline screens. */
  --tooth-light: rgba(233, 240, 247, 0.035);
  --tooth-dark: rgba(0, 0, 0, 0.16);

  /* A paper lift, never a glow: offset down, soft, neutral, and cheap. */
  --lift: 0 2px 3px rgba(0, 0, 0, 0.26), 0 10px 26px rgba(0, 0, 0, 0.3);
  --lift-press: 0 1px 1px rgba(0, 0, 0, 0.35);

  --measure: 68ch;
  --gutter: clamp(1rem, 4vw, 2.5rem);

  color-scheme: light;

  /* Browser surfaces the design still owns. */
  accent-color: var(--stamp);
  caret-color: var(--stamp);
  scrollbar-color: var(--rule-strong) var(--kraft);
}

::selection {
  background: var(--stamp);
  color: var(--sheet);
}

::-webkit-scrollbar {
  width: 12px;
  height: 12px;
}

::-webkit-scrollbar-track {
  background: var(--kraft);
}

::-webkit-scrollbar-thumb {
  background: var(--rule-strong);
  border: 3px solid var(--kraft);
  border-radius: 0;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--kraft);
  /* The body's ground is the board, so the body's default ink is the one that
     reads on it. Every paper surface sets `--ink` for itself below. */
  color: var(--ink-on-kraft);
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 16px;
  font-variation-settings: "wdth" 100;
  line-height: 1.5;
  padding-bottom: env(safe-area-inset-bottom);
  /* The tooth of board stock. Two hairline screens, no image request. */
  background-image:
    repeating-linear-gradient(
      90deg,
      var(--tooth-dark) 0 1px,
      transparent 1px 3px
    ),
    repeating-linear-gradient(
      0deg,
      var(--tooth-light) 0 1px,
      transparent 1px 4px
    );
}

/* Every figure on this sheet is a typed figure: same column, same width. */
.short-code,
.lot-price,
.lot-dims,
.tally,
.unsent-slot,
.signed-in-as,
.just-now {
  font-family: "Courier Prime", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
}

a {
  color: var(--stamp);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

a:hover {
  text-decoration-thickness: 2px;
}

:focus-visible {
  outline: 2px solid var(--stamp);
  outline-offset: 2px;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------- chrome -- */

.topbar {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding: 0.6rem var(--gutter);
  padding-top: calc(0.6rem + env(safe-area-inset-top));
  background: var(--kraft-deep);
  color: var(--sheet);
  border-bottom: 3px double var(--rule-kraft);
}

.brand {
  font-weight: 800;
  font-variation-settings: "wdth" 78;
  font-size: 1.05rem;
  letter-spacing: 0.02em;
  color: var(--sheet);
  text-decoration: none;
}

.brand:hover {
  text-decoration: underline;
  text-decoration-thickness: 2px;
}

.account {
  margin-left: auto;
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
}

/* On every page on purpose: a browser defaulted to the wrong Google account
   has to be obvious at a glance. */
.signed-in-as {
  color: var(--sheet);
  opacity: 0.86;
  font-size: 0.78rem;
  overflow-wrap: anywhere;
}

.unsent-slot:not([hidden]) {
  font-size: 0.78rem;
  color: var(--sheet);
  border: 1px solid var(--rule-kraft);
  padding: 0.1rem 0.45rem;
}

main {
  padding: var(--gutter);
  padding-top: clamp(1.25rem, 4vw, 2.25rem);
}

.sitefoot {
  display: flex;
  gap: 1.5rem;
  padding: 1.25rem var(--gutter) 2rem;
  border-top: 1px solid var(--rule-kraft);
  margin-top: 3rem;
  font-size: 0.85rem;
}

.sitefoot a {
  color: var(--ink-on-kraft);
}

/* -------------------------------------------------------------- controls --
 * A control on this sheet is stamped, not rounded. The 2px corner is the
 * bite of a rubber die, and it is the only corner radius in the system. */

button,
.signin,
.stamp-control {
  font: inherit;
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 0.84rem;
  font-weight: 700;
  font-variation-settings: "wdth" 88;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.5rem 0.85rem;
  min-height: 2.75rem;
  border: 2px solid currentColor;
  border-radius: 2px;
  background: transparent;
  color: var(--stamp);
  text-decoration: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* The die sits very slightly off square until it is pressed. */
  transform: rotate(-0.6deg);
  transition:
    transform 180ms cubic-bezier(0.2, 0.9, 0.3, 1),
    background-color 180ms cubic-bezier(0.2, 0.9, 0.3, 1);
}

.topbar button,
.topbar .signin {
  color: var(--sheet);
  min-height: 2.25rem;
  padding: 0.3rem 0.6rem;
  font-size: 0.78rem;
}

button:hover,
.signin:hover,
.stamp-control:hover {
  background: var(--stamp-wash);
  transform: rotate(0deg);
}

.topbar button:hover,
.topbar .signin:hover {
  background: rgba(239, 230, 209, 0.16);
}

button:active,
.stamp-control:active {
  transform: rotate(0deg) translateY(1px) scale(0.99);
  box-shadow: var(--lift-press);
}

button[disabled] {
  color: var(--ink-2);
  opacity: 0.55;
  cursor: not-allowed;
  transform: rotate(0deg);
}

/* ----------------------------------------------------------- the sheet -- */

.sheet {
  color: var(--ink);
  background: var(--sheet);
  box-shadow: var(--lift);
  max-width: 78rem;
  margin: 0 auto;
  padding-bottom: clamp(1.25rem, 3vw, 2rem);
  border-top: 4px solid var(--ink);
}

.sheet-head {
  padding: clamp(1.25rem, 4vw, 2.25rem) clamp(1rem, 3vw, 2rem) 0;
}

/* The sheet says what it is at display scale and then says how much of it is
   left. No image above it, and no eyebrow: the title carries itself. */
.sheet-title {
  font-size: clamp(2.5rem, 9vw, 5.25rem);
  font-weight: 800;
  font-variation-settings: "wdth" 76;
  letter-spacing: -0.03em;
  line-height: 0.88;
  text-transform: uppercase;
  text-wrap: balance;
  margin: 0;
}

.tally {
  margin: 0.75rem 0 0;
  font-size: 0.95rem;
  color: var(--ink-2);
}

.tally strong {
  color: var(--ink);
  font-weight: 700;
}

.sheet-note {
  margin: 0.4rem 0 0;
  max-width: var(--measure);
  font-size: 0.95rem;
  color: var(--ink-2);
}

/* Filters are typed labels along the sheet's margin, not chips in pills. */
.sheet-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1.1rem;
  margin-top: 1.5rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--rule);
  font-size: 0.78rem;
  font-weight: 700;
  font-variation-settings: "wdth" 88;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.sheet-filters a {
  color: var(--ink-2);
  text-decoration: none;
  padding: 0.5rem 0;
  border-bottom: 3px solid transparent;
}

.sheet-filters a:hover {
  color: var(--ink);
  border-bottom-color: var(--rule-strong);
}

.sheet-filters a[aria-current="true"] {
  color: var(--ink);
  border-bottom-color: var(--stamp);
}

/* ------------------------------------------------------------- the lots -- */

.lots {
  width: 100%;
  border-collapse: collapse;
  margin-top: 1.25rem;
}

.lots caption {
  text-align: left;
  padding: 0 clamp(1rem, 3vw, 2rem) 0.5rem;
  font-size: 0.85rem;
  color: var(--ink-2);
}

.lots thead th {
  text-align: left;
  font-size: 0.75rem;
  font-weight: 700;
  font-variation-settings: "wdth" 84;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding: 0.5rem clamp(0.5rem, 1.5vw, 1rem);
  border-bottom: 2px solid var(--ink);
  white-space: nowrap;
}

.lots thead th:first-child {
  padding-left: clamp(1rem, 3vw, 2rem);
}

.lots thead th:last-child,
.lots td:last-child {
  text-align: right;
  padding-right: clamp(1rem, 3vw, 2rem);
}

.lots tbody tr {
  border-bottom: 1px solid var(--rule);
}

.lots tbody tr:nth-child(even) {
  background: rgba(140, 123, 88, 0.06);
}

.lots tbody tr:hover {
  background: var(--stamp-wash);
}

.lots td {
  padding: 0.7rem clamp(0.5rem, 1.5vw, 1rem);
  vertical-align: top;
}

.lots td:first-child {
  padding-left: clamp(1rem, 3vw, 2rem);
}

/* Figures line up on their right edge, which is the whole reason they are
   typed in a fixed-width face in a column of their own. */
.lots thead th:nth-child(6),
.lots td.col-price {
  text-align: right;
}

/* The plate's column is exactly the plate: without a width the table's auto
   layout hands the photograph column the slack it takes from the item. */
.lots td.col-plate,
.lots thead th:first-child {
  width: 136px;
}

/* The plate. Square, because the sheet's column is square; the uncropped
   photographs live on the lot's own page. */
.lot-plate {
  width: 136px;
  height: 136px;
  object-fit: cover;
  display: block;
  background: var(--sheet-sunk);
  border: 1px solid var(--rule-strong);
}

/* A lot with no photograph is a real and ordinary state: the dumpster pass
   never photographs anything. It gets the tag it would wear in the room. */
.lot-plate--none {
  display: grid;
  place-content: center;
  width: 136px;
  height: 136px;
  background:
    repeating-linear-gradient(
      -45deg,
      rgba(140, 123, 88, 0.16) 0 6px,
      transparent 6px 12px
    ),
    var(--sheet-sunk);
  border: 1px dashed var(--rule-strong);
  font-family: "Courier Prime", ui-monospace, monospace;
  font-size: 0.72rem;
  color: var(--ink-2);
  text-align: center;
  line-height: 1.2;
}

.lot-no {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-family: "Courier Prime", ui-monospace, monospace;
  font-size: 0.78rem;
  color: var(--ink-2);
  margin-bottom: 0.15rem;
}

/* The punched eyelet a real tag hangs by. */
.lot-no::before {
  content: "";
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  border: 1px solid var(--tag-hole);
  flex: none;
}

.short-code {
  font-weight: 700;
  font-size: 0.92rem;
  letter-spacing: 0.06em;
  color: var(--ink);
  white-space: nowrap;
}

.lot-title {
  font-weight: 600;
  font-size: 1.02rem;
  line-height: 1.25;
  margin: 0;
}

.lot-title a {
  color: var(--ink);
  text-decoration: none;
}

.lot-title a:hover {
  text-decoration: underline;
  text-decoration-color: var(--stamp);
  text-decoration-thickness: 2px;
}

.lot-desc {
  margin: 0.2rem 0 0;
  font-size: 0.88rem;
  line-height: 1.45;
  color: var(--ink-2);
  max-width: 46ch;
}

.lot-dims {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.78rem;
  color: var(--ink-2);
}

.lot-room {
  font-size: 0.88rem;
  color: var(--ink-2);
  white-space: nowrap;
}

/* Room, condition, and the short code each appear once. The wide sheet reads
   them out of their own columns; the narrow sheet reads them from the stack
   inside the item cell, and each layout hides the copy it is not using.
   display:none takes the unused copy out of the accessibility tree too, so
   nothing is announced twice. */
.lot-code-inline,
.lot-meta-row,
.field-label {
  display: none;
}

.lot-price {
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--ink);
  white-space: nowrap;
}

.lot-price--free {
  color: var(--stamp);
  letter-spacing: 0.08em;
}

/* --------------------------------------------------------------- stamps --
 * Condition and state are struck onto the sheet, in the pad's own inks,
 * slightly off-square. The word is always present; the ink never replaces it. */

.stamp {
  display: inline-block;
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 0.75rem;
  font-weight: 700;
  font-variation-settings: "wdth" 84;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.18rem 0.45rem;
  border: 1.5px solid currentColor;
  border-radius: 2px;
  transform: rotate(-1.5deg);
  white-space: nowrap;
}

/* Condition is a fact about the object, and the word carries it. A green NEW
   beside a red DAMAGED is a status-colour system, which is the store chrome
   the sheet refuses; only a real warning earns the second ink. */
.stamp-new,
.stamp-good,
.stamp-worn    { color: var(--ink-2); }
.stamp-new     { color: var(--ink); }
.stamp-damaged { color: var(--oxblood); }

/* State travels with the action, not with the condition: a lot's condition is
   a fact about the object, its state is a fact about the queue. */
.col-action {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.4rem;
}

/* Held lots stay on the sheet, greyed and labelled, and stay requestable —
   hiding them is the version where three people email to ask whether the
   dresser is really gone. The sheet gets marked up instead of re-sorted.
   The mark is drawn ACROSS the row, not parked beside it: a china-marker
   stroke through the whole lot, kept off the text's own band so the row
   stays as readable as every other one. */
.lot--held {
  background-color: var(--stamp-wash);
}

/* The mark goes across the photograph, not across the words. Running a stroke
   through the whole row put a violet line behind the secondary text and took
   it to 2.2:1, and this sheet is committed to AA — crossing out the picture is
   also what somebody actually does to a lot sheet with a marker in their hand.
   The slot is the containing block: a table row is not a reliable one. */
.plate-slot {
  position: relative;
  display: block;
  width: fit-content;
}

.lot--held .plate-slot::after,
.lot--gone .plate-slot::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    to bottom right,
    transparent calc(50% - 2px),
    currentColor calc(50% - 2px),
    currentColor calc(50% + 2px),
    transparent calc(50% + 2px)
  );
}

.lot--held .plate-slot::after {
  color: color-mix(in srgb, var(--stamp) 85%, transparent);
}

/* Departed lots take the second stroke that closes the X out entirely. */
.lot--gone .plate-slot::after {
  color: color-mix(in srgb, var(--oxblood) 85%, transparent);
  background:
    linear-gradient(
      to bottom right,
      transparent calc(50% - 2px),
      currentColor calc(50% - 2px),
      currentColor calc(50% + 2px),
      transparent calc(50% + 2px)
    ),
    linear-gradient(
      to top right,
      transparent calc(50% - 2px),
      currentColor calc(50% - 2px),
      currentColor calc(50% + 2px),
      transparent calc(50% + 2px)
    );
}

.lots tbody tr.lot--held:hover {
  background-color: color-mix(in srgb, var(--stamp) 18%, transparent);
}

.lot--held .lot-plate,
.lot--held .lot-plate--none,
.lot--held .lot-plates img {
  filter: grayscale(0.7) brightness(0.92);
}

.lot--held .lot-title a {
  color: var(--ink-2);
}

.stamp-held {
  color: var(--stamp);
  border-width: 2px;
  transform: rotate(-3deg);
  font-size: 0.8rem;
}

/* A departed lot is struck through, not deleted: the record of what has
   already gone is what makes the pile legibly finite. */
.lot--gone {
  color: var(--ink-2);
}

.lot--gone .lot-plate,
.lot--gone .lot-plate--none {
  filter: grayscale(1) brightness(0.8) opacity(0.55);
}

.lot--gone .lot-title {
  text-decoration: line-through;
  text-decoration-color: var(--oxblood);
  text-decoration-thickness: 2px;
}

.lot--gone .lot-price {
  text-decoration: line-through;
  text-decoration-color: var(--oxblood);
  color: var(--ink-2);
}

.stamp-gone {
  color: var(--oxblood);
  border-width: 2px;
  transform: rotate(2deg);
  font-size: 0.8rem;
}

/* The one authored moment: state marks ink in once, on load, as though the
   sheet were being stamped in front of you. Staggered by row, then over. */
@media (prefers-reduced-motion: no-preference) {
  .stamp-held,
  .stamp-gone {
    animation: ink-in 420ms cubic-bezier(0.16, 1, 0.3, 1) both;
    animation-delay: calc(var(--row, 0) * 55ms);
  }

  @keyframes ink-in {
    from {
      opacity: 0;
      transform: rotate(-9deg) scale(1.35);
      filter: blur(1.5px);
    }
    to {
      opacity: 1;
      filter: blur(0);
    }
  }
}

/* ------------------------------------------------------------- lot page -- */

.sheet-foot {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.25rem;
  margin: 0 clamp(1rem, 3vw, 2rem);
  padding-top: 0.6rem;
  border-top: 3px double var(--ink);
  font-family: "Courier Prime", ui-monospace, monospace;
  font-size: 0.85rem;
  color: var(--ink-2);
}

.sheet-foot strong {
  color: var(--ink);
}

/* Authored stand-in content says so, where a visitor could mistake it for a
   real listing. */
.synthetic-note {
  margin: 0.75rem clamp(1rem, 3vw, 2rem) 0;
  padding: 0.5rem 0.7rem;
  border: 2px dashed var(--rule-strong);
  border-radius: 2px;
  font-size: 0.85rem;
  color: var(--ink-2);
  max-width: var(--measure);
}

.lot-detail {
  color: var(--ink);
  max-width: 78rem;
  margin: 0 auto;
  background: var(--sheet);
  box-shadow: var(--lift);
  border-top: 4px solid var(--ink);
  padding: clamp(1.25rem, 4vw, 2.25rem);
}

.lot-detail-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1rem;
  border-bottom: 2px solid var(--ink);
  padding-bottom: 0.75rem;
}

.lot-detail-head h1 {
  font-size: clamp(1.75rem, 5vw, 3rem);
  font-weight: 800;
  font-variation-settings: "wdth" 82;
  letter-spacing: -0.02em;
  line-height: 1;
  margin: 0;
  text-wrap: balance;
}

.lot-detail > p {
  max-width: var(--measure);
  line-height: 1.6;
}

/* Plates are a fixed-width strip, not a stretched grid: with one or two
   photographs on a wide sheet, `1fr` columns grew the box far past the
   photograph inside it and the letterbox became the picture. */
.lot-plates {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 22rem));
  justify-content: start;
  gap: 0.75rem;
  margin: 1.25rem 0;
  align-items: start;
}

/* The plate fills its own slot here; `fit-content` is for the sheet's square
   plate, where the slot has to shrink to the photograph. */
.lot-plates .plate-slot {
  width: 100%;
}

.lot-plates img {
  width: 100%;
  height: clamp(260px, 34vh, 420px);
  object-fit: contain;
  display: block;
  border: 1px solid var(--rule-strong);
  background: var(--sheet-sunk);
}

.lot-plates .lot-plate--none {
  width: 100%;
  height: clamp(260px, 34vh, 420px);
}

.lot-detail-head .lot-price {
  margin-left: auto;
  font-size: 1.5rem;
}

.lot-detail-head .lot-no {
  margin-bottom: 0;
}

/* What the Owner wrote. It sits under the photographs and above the clerical
   facts because it is the one thing on this page a stranger reads in
   sentences. The only block in the app holding markup rather than one string,
   so its paragraphs, lists and links are styled here and nowhere else. */
.lot-desc-detail {
  max-width: var(--measure);
  margin: 1.25rem 0 0;
  font-size: 1.05rem;
  line-height: 1.6;
}

.lot-desc-detail p {
  margin: 0 0 0.75rem;
}

.lot-desc-detail p:last-child {
  margin-bottom: 0;
}

.lot-desc-detail ul {
  margin: 0 0 0.75rem;
  padding-left: 1.25rem;
}

/* A link out of the sell pile is the one place a stranger leaves it, so it
   looks like a link and not like emphasis. */
.lot-desc-detail a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.lot-desc-detail a:hover,
.lot-desc-detail a:focus-visible {
  text-decoration-thickness: 2px;
}

/* The clerical facts, as a real definition list with printed rules. Held to
   the measure with the rest of the page's text: a hairline run across 78rem
   with four characters sitting at its left end is a rule with nothing under
   it. */
.lot-facts {
  margin: 1.25rem 0;
  max-width: var(--measure);
  border-top: 1px solid var(--rule);
}

.lot-facts div {
  display: flex;
  gap: 1rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--rule);
}

.lot-facts dt {
  flex: 0 0 9rem;
  font-size: 0.75rem;
  font-weight: 700;
  font-variation-settings: "wdth" 84;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding-top: 0.15rem;
}

.lot-facts dd {
  margin: 0;
  font-family: "Courier Prime", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
}

.request-block {
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 3px double var(--rule-strong);
}

.request-block .stamp-control {
  font-size: 1rem;
  padding: 0.85rem 1.6rem;
  border-width: 3px;
}

.request-note {
  margin: 0.6rem 0 0;
  font-size: 0.85rem;
  color: var(--ink-2);
  max-width: var(--measure);
}

/* The request form. The fields are the system's fields, extended below rather
   than retyped: see `.review-field`. */
.request-form {
  display: grid;
  gap: 0.75rem;
  max-width: var(--measure);
  margin-top: 0.5rem;
}

/* The public register's own sizing on the shared field: a stranger on a phone
   filling this in once, rather than the Owner going through dozens. */
.request-field textarea,
.request-field input {
  font-size: 1.05rem;
  padding: 0.55rem 0.6rem;
  min-height: 3rem;
}

.request-field--offer input {
  width: 8rem;
}

.request-form .stamp-control {
  justify-self: start;
  font-size: 1rem;
  padding: 0.85rem 1.6rem;
  border-width: 3px;
}

.request-refusal {
  margin: 0 0 0.75rem;
  max-width: var(--measure);
}

/* A held lot keeps its control. The note says why rather than greying it out,
   because the ask is what builds the runner-up queue. */
.held-note {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  margin: 0 0 0.75rem;
  font-size: 0.9rem;
  color: var(--ink-2);
  max-width: var(--measure);
}

/* Beside the sentence, not inside its line box: an inline stamp is taller than
   the line it sits on and the wrapped line ran underneath it. */
.held-note .stamp-held {
  flex: none;
}

/* The Winner, told on the page as well as by mail, because the one email that
   matters is the one that cannot be allowed to land in spam. */
.won-note {
  margin: 0;
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 1.15rem;
  font-weight: 800;
  font-variation-settings: "wdth" 84;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--stamp);
}

.lot-back {
  margin-top: 1.5rem;
  font-size: 0.85rem;
}

/* ------------------------------------------------------- owner register --
 * The same sheet with the stamp pad open: tighter rows, the six dispositions
 * as their own inks, and every tap target the size it already was. Nothing
 * below may cost a tap — the thirty-second budget outranks this stylesheet. */

.intake-screen {
  max-width: 48rem;
  margin: 0 auto;
}

.camera {
  margin-bottom: 0.75rem;
}

/* The one control a thumb touches for the whole thirty seconds. It is the
   sheet's own shutter: a wide plate with a stamped rule, sized so it is hit
   without looking, and a strip of what has been taken so far. */
.camera-control {
  color: var(--ink);
  background: var(--sheet);
  box-shadow: var(--lift);
  border-top: 4px solid var(--ink);
  padding: 0.75rem;
  display: grid;
  gap: 0.6rem;
}

.camera-button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 4.5rem;
  padding: 0.75rem 1rem;
  border: 3px solid var(--ink);
  border-radius: 2px;
  background: var(--sheet-sunk);
  color: var(--ink);
  font-weight: 800;
  font-variation-settings: "wdth" 84;
  font-size: 1.05rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
}

.camera-button:active {
  background: var(--ink);
  color: var(--sheet);
}

.camera-button input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.camera-button:focus-within {
  outline: 2px solid var(--stamp);
  outline-offset: 2px;
}

.camera-busy {
  margin: 0;
  font-family: "Courier Prime", ui-monospace, monospace;
  font-size: 0.85rem;
  color: var(--ink-2);
}

/* The frames already taken, laid out like exposures along an edge. */
.camera-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.camera-frame {
  padding: 0;
  min-height: 0;
  border: 1px solid var(--rule-strong);
  border-radius: 0;
  transform: none;
  background: var(--sheet-sunk);
  position: relative;
}

.camera-frame img,
.camera-frame-empty {
  display: block;
  width: 72px;
  height: 72px;
  object-fit: cover;
}

/* Tapping a frame discards it, so it says so rather than relying on the tap. */
.camera-frame::after {
  content: "\00d7";
  position: absolute;
  top: -1px;
  right: -1px;
  width: 1.15rem;
  height: 1.15rem;
  display: grid;
  place-content: center;
  background: var(--oxblood);
  color: var(--sheet);
  font-size: 0.9rem;
  line-height: 1;
}

.camera-error {
  margin: 0;
  padding: 0.6rem 0.7rem;
  background: var(--oxblood);
  color: var(--sheet);
  font-weight: 600;
  border-radius: 2px;
}

.intake {
  color: var(--ink);
  background: var(--sheet);
  box-shadow: var(--lift);
  border-top: 4px solid var(--ink);
  padding: 0.85rem;
  display: grid;
  gap: 0.6rem;
}

.intake-title {
  font: inherit;
  font-size: 1.05rem;
  width: 100%;
  padding: 0.7rem 0.75rem;
  min-height: 3rem;
  background: var(--sheet-sunk);
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  color: var(--ink);
}

.intake-title::placeholder {
  color: var(--ink-2);
  opacity: 1;
}

.intake-title:focus-visible {
  outline: 2px solid var(--stamp);
  outline-offset: 1px;
  background: var(--sheet);
}

.room-chooser {
  border: 1px solid var(--rule);
  border-radius: 2px;
  background: var(--sheet-sunk);
}

.room-chooser summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.6rem 0.75rem;
  min-height: 2.75rem;
  cursor: pointer;
  list-style: none;
}

.room-chooser summary::-webkit-details-marker {
  display: none;
}

.room-label {
  font-size: 0.75rem;
  font-weight: 700;
  font-variation-settings: "wdth" 84;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.room-current {
  font-weight: 700;
  font-size: 1rem;
}

.room-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0 0.75rem 0.75rem;
}

.room-chip {
  display: inline-flex;
}

.room-chip input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.room-chip span {
  display: inline-flex;
  align-items: center;
  padding: 0.55rem 0.75rem;
  min-height: 2.75rem;
  border: 1.5px solid var(--rule-strong);
  border-radius: 2px;
  background: var(--sheet);
  font-size: 0.9rem;
  cursor: pointer;
}

.room-chip input:checked + span {
  background: var(--ink);
  color: var(--sheet);
  border-color: var(--ink);
}

.room-chip input:focus-visible + span {
  outline: 2px solid var(--stamp);
  outline-offset: 2px;
}

/* Six submit buttons, so this works with no JavaScript at all. Each is its
   own ink; the word is always the label and the colour only accompanies it. */
.dispositions {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.4rem;
}

.disposition {
  min-height: 3.25rem;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  border-width: 2px;
  transform: rotate(0deg);
}

/* Weighted by how often a thumb actually lands on them. `sell`, `store` and
   `trash` are the three real decisions and take the top row at full size;
   `unsorted`, `donate` and `keep` sit under them, still full tap targets and
   still in fixed positions so the muscle memory a fixed grid buys is kept.
   If `unsorted` turns out to be the most-tapped on a fast pass, moving it up
   is a one-line change: it is the order of these two blocks, nothing else. */
.disposition-sell,
.disposition-store,
.disposition-trash {
  order: 1;
  min-height: 4.25rem;
  font-size: 1rem;
  border-width: 3px;
}

.disposition-unsorted,
.disposition-donate,
.disposition-keep {
  order: 2;
  min-height: 3rem;
  font-size: 0.82rem;
  border-width: 2px;
}

.disposition:hover {
  transform: rotate(-0.8deg);
}

.disposition-unsorted { color: var(--graphite); }
.disposition-sell     { color: var(--stamp); }
.disposition-store    { color: var(--stamp-blue); }
.disposition-trash    { color: var(--oxblood); }
.disposition-donate   { color: var(--stamp-green); }
.disposition-keep     { color: var(--stamp-sepia); }

.disposition:hover,
.disposition:focus-visible {
  background: color-mix(in srgb, currentColor 12%, transparent);
}

/* Loud, and it must not imply the record is safe on the phone. */
.save-error:not([hidden]) {
  margin: 0;
  padding: 0.7rem 0.75rem;
  background: var(--oxblood);
  color: var(--sheet);
  font-weight: 600;
  border-radius: 2px;
}

/* The owner's list is the same lot sheet, one flick denser. */
.item-list {
  color: var(--ink);
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  background: var(--sheet);
  box-shadow: var(--lift);
  border-top: 4px solid var(--ink);
}

/* Wrapping, since the haul tick joined the row: on a phone the two stamped
   acts drop to a line of their own rather than sliding off the right edge. */
.item-row {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 0.7rem;
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--rule);
}

.item-row:nth-child(even) {
  background: rgba(140, 123, 88, 0.06);
}

.item-thumb {
  width: 64px;
  height: 64px;
  object-fit: cover;
  flex: none;
  border: 1px solid var(--rule-strong);
  background: var(--sheet-sunk);
}

.item-thumb--none {
  width: 64px;
  height: 64px;
  flex: none;
  display: grid;
  place-content: center;
  background:
    repeating-linear-gradient(
      -45deg,
      rgba(140, 123, 88, 0.16) 0 5px,
      transparent 5px 10px
    ),
    var(--sheet-sunk);
  border: 1px dashed var(--rule-strong);
  font-family: "Courier Prime", ui-monospace, monospace;
  font-size: 0.72rem;
  color: var(--ink-2);
  text-align: center;
  line-height: 1.15;
}

.row-text {
  flex: 1 1 10rem;
  min-width: 0;
}

.row-top {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.item-title {
  font-weight: 600;
  overflow-wrap: anywhere;
}

.item-title.untitled {
  color: var(--ink-2);
  font-style: italic;
  font-weight: 400;
}

.row-bottom {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-top: 0.15rem;
  font-size: 0.85rem;
  color: var(--ink-2);
}

.item-disposition {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  font-weight: 600;
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* The colour chip accompanies the word and never replaces it. */
.chip {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 2px;
  background: currentColor;
  transform: translateY(-0.02em) rotate(-8deg);
  flex: none;
}

.chip-unsorted { color: var(--graphite); }
.chip-sell     { color: var(--stamp); }
.chip-store    { color: var(--stamp-blue); }
.chip-trash    { color: var(--oxblood); }
.chip-donate   { color: var(--stamp-green); }
.chip-keep     { color: var(--stamp-sepia); }

.just-now {
  font-size: 0.78rem;
  color: var(--ink-2);
}

/* A departed row is struck through, not removed: the record of what has
   already gone is what makes the remaining work legibly finite. The same
   treatment the public sheet gives a gone lot, at list density. */
.item-row--departed {
  color: var(--ink-2);
}

.item-row--departed .item-thumb,
.item-row--departed .item-thumb--none {
  filter: grayscale(1) brightness(0.8) opacity(0.55);
}

.item-row--departed .item-title {
  text-decoration: line-through;
  text-decoration-color: var(--oxblood);
  text-decoration-thickness: 2px;
}

/* The two acts at the right of a row are the same control twice over, and
   they travel together when the row wraps. */
.row-departed,
.row-delete {
  flex: none;
  margin-left: auto;
}

.row-departed + .row-delete {
  margin-left: 0;
}

.row-departed button,
.row-delete button {
  color: var(--oxblood);
  font-size: 0.75rem;
  padding: 0.35rem 0.55rem;
  min-height: 2.75rem;
  border-width: 1.5px;
}

/* The haul: one act over everything ticked. Quieter than Intake, because the
   thumb lands here once a room rather than once a thing. */
.haul {
  margin: 1rem 0 0;
  padding: 0.6rem 0.75rem;
  background: var(--sheet-sunk);
  border: 1px solid var(--rule);
}

.haul-hint {
  margin: 0 0 0.45rem;
  font-size: 0.8rem;
  color: var(--ink-2);
}

.haul-acts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.haul-act {
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.4rem 0.6rem;
  min-height: 2.75rem;
}

/* The same six inks the Disposition Pad carries, at the register's state-word
   size. A class of their own rather than the pad's: one word, two controls,
   and a selector that matched both would be a control that answers for the
   wrong form. */
.haul-act-departed  { color: var(--oxblood); }
.haul-act-unsorted  { color: var(--graphite); }
.haul-act-sell      { color: var(--stamp); }
.haul-act-store     { color: var(--stamp-blue); }
.haul-act-trash     { color: var(--oxblood); }
.haul-act-donate    { color: var(--stamp-green); }
.haul-act-keep      { color: var(--stamp-sepia); }

/* Silent unless the haul left something behind, and then loud: nineteen of
   twenty applied quietly is the version that loses a record. */
.haul-report {
  margin: 0.5rem 0 0;
  padding: 0.6rem 0.75rem;
  background: var(--oxblood);
  color: var(--sheet);
  font-size: 0.85rem;
  border-radius: 2px;
}

.haul-skipped {
  white-space: nowrap;
  font-weight: 600;
}

/* A thumb-sized target, aligned with the top line of the row it picks. */
.haul-pick {
  flex: none;
  width: 1.4rem;
  height: 1.4rem;
  margin: 0.2rem 0 0;
  accent-color: var(--oxblood);
}

/* Shown only while the list has no rows, so the first save does not leave a
   row sitting under the words "Nothing recorded yet." */
.empty {
  color: var(--ink-2);
  margin: 1rem 0 0;
  padding: 1.5rem 0.75rem;
  background: var(--sheet);
  border-top: 4px solid var(--ink);
  box-shadow: var(--lift);
  text-align: center;
  font-style: italic;
}

.item-list:not(:empty) + .empty {
  display: none;
}

/* ------------------------------------------------------- prose surfaces --
 * Privacy, terms, and the two refusal pages. A reader here is reading, so
 * the measure is a reading measure and the sheet gets out of the way. */

.placeholder,
.legal,
.gate,
.not-owner,
.error {
  color: var(--ink);
  max-width: 44rem;
  margin: 0 auto;
  background: var(--sheet);
  box-shadow: var(--lift);
  border-top: 4px solid var(--ink);
  padding: clamp(1.25rem, 4vw, 2.5rem);
}

.placeholder h1,
.legal h1,
.gate h1,
.not-owner h1,
.error h1 {
  font-size: clamp(1.75rem, 5vw, 2.75rem);
  font-weight: 800;
  font-variation-settings: "wdth" 80;
  letter-spacing: -0.02em;
  line-height: 1.02;
  text-wrap: balance;
  margin: 0 0 0.75rem;
}

.legal h2 {
  font-size: 1.25rem;
  font-weight: 700;
  font-variation-settings: "wdth" 88;
  letter-spacing: -0.01em;
  margin: 2rem 0 0.5rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--rule);
}

.legal p,
.legal li,
.gate p,
.not-owner p,
.error p,
.placeholder p {
  max-width: var(--measure);
  line-height: 1.6;
}

.legal ul,
.legal ol {
  padding-left: 1.25rem;
}

.legal li {
  margin-bottom: 0.4rem;
}

.updated {
  font-family: "Courier Prime", ui-monospace, monospace;
  font-size: 0.82rem;
  color: var(--ink-2);
  margin-top: 0;
}

/* A draft policy says so where it cannot be missed — struck across the page
   the way the sheet marks everything else, rather than tabbed down one side. */
.draft-notice {
  display: block;
  border: 2px solid var(--oxblood);
  border-radius: 2px;
  color: var(--oxblood);
  background: transparent;
  padding: 0.55rem 0.8rem;
  font-weight: 700;
  font-variation-settings: "wdth" 88;
  letter-spacing: 0.04em;
  transform: rotate(-0.7deg);
  width: fit-content;
  max-width: 100%;
}

.gate-out {
  font-size: 0.9rem;
  color: var(--ink-2);
  padding-top: 0.75rem;
  border-top: 1px solid var(--rule);
}

/* The address this page names is the one it SAW. It never names the one it
   expected: any signed-in stranger can reach it (ADR-0004). */
.not-owner .seen-address {
  font-family: "Courier Prime", ui-monospace, monospace;
  font-weight: 700;
  background: var(--stamp-wash);
  padding: 0.1rem 0.3rem;
  overflow-wrap: anywhere;
}

/* --------------------------------------------------------------- narrow --
 * Two steps, not one. Room and Cond fold into the item cell first, because
 * seven columns on a laptop squeezed the title and the description into a
 * two-word ribbon 300px tall; the table itself only gives up at 52rem. */

@media (max-width: 68rem) {
  .lots thead th:nth-child(4),
  .lots thead th:nth-child(5),
  .lots td.col-room,
  .lots td.col-cond {
    display: none;
  }

  /* Each figure keeps its column name when its column cannot survive. Same
     type as the wide sheet's thead, so the row still reads as a document
     rather than a list. */
  .lot-meta-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.5rem 1.1rem;
    margin-top: 0.5rem;
    padding-top: 0.4rem;
    border-top: 1px solid var(--rule);
  }

  .field {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
  }

  .field-label,
  .lots td.col-price::before {
    display: block;
    font-family: "Archivo", system-ui, sans-serif;
    font-size: 0.72rem;
    font-weight: 700;
    font-variation-settings: "wdth" 84;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-2);
  }
}

@media (max-width: 52rem) {
  /* Ten categories and four price bands wrap to five lines on a phone, which
     is most of the first screen spent on chrome before a single lot. Same
     labels, tighter leading. */
  .sheet-filters {
    gap: 0 0.9rem;
    margin-top: 1.1rem;
    font-size: 0.74rem;
  }

  .sheet-filters a {
    padding: 0.35rem 0;
  }

  /* The lot sheet stops being a table and becomes a stack of tags. Every
     figure keeps its column label, because a price with no header is a
     number floating on paper. */
  .lots,
  .lots tbody,
  .lots tr,
  .lots td {
    display: block;
    width: 100%;
  }

  .lots thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }

  .lots caption {
    display: block;
    padding: 0 clamp(1rem, 4vw, 1.5rem) 0.75rem;
  }

  .lots tbody tr {
    display: grid;
    grid-template-columns: 108px 1fr;
    grid-template-areas:
      "plate item"
      "price action";
    align-items: start;
    gap: 0.5rem 0.85rem;
    padding: 0.9rem clamp(1rem, 4vw, 1.5rem);
    border-bottom: 1px solid var(--rule);
  }

  .lots td {
    padding: 0;
  }

  .lots td.col-plate { grid-area: plate; padding-left: 0; }
  .lots td.col-item  { grid-area: item; }
  .lots td.col-price { grid-area: price; align-self: center; }
  .lots .col-action {
    grid-area: action;
    justify-self: end;
    text-align: right;
    padding-right: 0;
  }

  /* Carried by the stack inside the item cell instead. Qualified with `td`
     because `.lots td { display: block }` above outranks a bare class. */
  .lots td.col-lot {
    display: none;
  }

  .lot-plate,
  .lot-plate--none {
    width: 108px;
    height: 108px;
  }

  .lot-code-inline {
    /* flex, not block: the punched eyelet is a flex child and collapses to a
       hairline otherwise. */
    display: flex;
    margin-bottom: 0.1rem;
  }

  .lots td.col-price {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    /* The column it was right-aligned in does not exist down here: the price
       sits under the plate with its own label above it. */
    text-align: left;
  }

  .lots td.col-price::before {
    content: "Price";
  }

  .lot-desc {
    max-width: none;
  }

  .lot-facts dt {
    flex-basis: 7rem;
  }
}

@media (max-width: 26rem) {
  .topbar {
    gap: 0.4rem;
  }
}

/* ------------------------------------------------- the owner's own bar --
 * Two words beside the wordmark, and the count of photographs still on the
 * phone. Nobody but the Owner is shown that either exists. */

.ownernav {
  display: flex;
  gap: 0.9rem;
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 0.78rem;
  font-weight: 700;
  font-variation-settings: "wdth" 88;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.ownernav a {
  color: var(--ink-on-kraft);
  text-decoration: none;
  padding: 0.35rem 0;
  border-bottom: 2px solid transparent;
}

.ownernav a:hover {
  border-bottom-color: var(--rule-kraft);
}

.signout {
  margin: 0;
}

/* The number that has to reach zero before the Owner leaves a room. It is a
   count on a tag, not a notification badge. */
.unsent-count {
  font-family: "Courier Prime", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
}

/* A photograph the server will never accept. Named rather than retried, in the
   one ink this sheet reserves for a refusal. */
.unsent-refused {
  font: inherit;
  padding: 0.1rem 0.4rem;
  min-height: 0;
  border: 1px solid currentColor;
  border-radius: 2px;
  background: transparent;
  color: var(--oxblood);
  cursor: pointer;
}

/* ----------------------------------------------------- the sheet, filed --
 * What slice 5 adds to the sheet the design established: two rows of filters
 * rather than one, and the way to the next page. */

.filter-row {
  flex: 1 0 100%;
  display: flex;
  flex-wrap: wrap;
  gap: 0 1.1rem;
}

/* The `Older` link sits in a row of its own, under the last lot, and reads as
   the sheet's own footer rather than as another lot. */
.lot-more td {
  text-align: center;
  padding: 1rem;
}

.older {
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 0.78rem;
  font-weight: 700;
  font-variation-settings: "wdth" 88;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--stamp);
  text-decoration: none;
  border-bottom: 2px solid currentColor;
}

.empty-note {
  color: var(--ink-2);
  font-style: italic;
  margin: 1rem 0 0;
}

/* --------------------------------------------------------------- review --
 * The sitting-down pass: the same sheet with the stamp pad open, one inline
 * form per row. Dozens in a sitting, so nothing here costs a round trip and
 * nothing here is a detail page. */

.review-screen {
  max-width: 60rem;
  margin: 0 auto;
  color: var(--ink);
  background: var(--sheet);
  box-shadow: var(--lift);
  border-top: 4px solid var(--ink);
  padding: clamp(1rem, 3vw, 1.75rem);
}

.review-screen h1 {
  font-size: clamp(1.75rem, 6vw, 3rem);
  font-weight: 800;
  font-variation-settings: "wdth" 76;
  letter-spacing: -0.02em;
  line-height: 0.9;
  text-transform: uppercase;
  margin: 0;
}

/* How a description may be written, said once at the top of the screen. */
.review-note {
  color: var(--ink-2);
  font-size: 0.9rem;
  line-height: 1.5;
  max-width: 52ch;
  margin: 0.6rem 0 0;
}

.review-note code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.85em;
  white-space: nowrap;
}

.review-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1.1rem;
  margin-top: 1rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--rule);
  font-size: 0.78rem;
  font-weight: 700;
  font-variation-settings: "wdth" 88;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.review-filter {
  color: var(--ink-2);
  text-decoration: none;
  padding: 0.5rem 0;
  border-bottom: 3px solid transparent;
}

.review-filter-current {
  color: var(--ink);
  border-bottom-color: var(--stamp);
}

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

.review-row {
  border-bottom: 1px solid var(--rule);
  padding: 1rem 0;
}

.review-form {
  display: grid;
  gap: 0.6rem;
}

.review-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.review-thumb {
  width: 64px;
  height: 64px;
  object-fit: cover;
  display: block;
  background: var(--sheet-sunk);
  border: 1px solid var(--rule-strong);
}

.review-thumb-empty {
  border-style: dashed;
}

/* Where a `sell` thing sits in its lifecycle, struck onto the row in the same
   ink the public sheet uses for a state. */
.sale-status {
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 0.72rem;
  font-weight: 700;
  font-variation-settings: "wdth" 84;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.sale-status-listed {
  color: var(--stamp);
}

/* The one field that is typed on every row, so it is the one that gets the
   width and the type size. */
.review-title {
  font: inherit;
  font-size: 1.05rem;
  width: 100%;
  padding: 0.55rem 0.6rem;
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  background: var(--sheet-sunk);
  color: var(--ink);
}

/* The paragraph a buyer reads after the four words of the title. Two lines
   deep at rest, because most of these are two lines. */
.review-description {
  font: inherit;
  font-size: 0.95rem;
  width: 100%;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  background: var(--sheet-sunk);
  color: var(--ink);
  resize: vertical;
}

.review-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 0.9rem;
}

.review-field,
.request-field {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 0.72rem;
  font-weight: 700;
  font-variation-settings: "wdth" 84;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.review-field select,
.review-field input,
.request-field textarea,
.request-field input {
  font: inherit;
  font-size: 0.95rem;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--ink);
  padding: 0.5rem 0.55rem;
  min-height: 2.75rem;
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  background: var(--sheet-sunk);
}

.review-field-price input {
  width: 7rem;
}

.review-field-dimensions input {
  width: 12rem;
}

.review-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* Listing is a gate and not a toggle, so the control that opens it is the one
   stamped control on the row. */
.review-list-it {
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 0.84rem;
  font-weight: 700;
  font-variation-settings: "wdth" 88;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--stamp);
  border: 2px solid currentColor;
  border-radius: 2px;
  background: transparent;
  padding: 0.5rem 0.85rem;
  min-height: 2.75rem;
  cursor: pointer;
}

/* A Draft is a proposal and the control that asks for one reads like one: the
   stamped ink is reserved for listing, which is the act a stranger sees. A
   draft in flight says so and refuses a second tap. Try again is the same
   control, because it asks for the same thing. */
.review-draft-it,
.review-try-again {
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 0.84rem;
  font-weight: 700;
  font-variation-settings: "wdth" 88;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  background: transparent;
  padding: 0.5rem 0.85rem;
  min-height: 2.75rem;
  cursor: pointer;
}

.review-draft-it[disabled],
.review-try-again[disabled] {
  opacity: 0.55;
  cursor: progress;
}

.review-listed {
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 0.75rem;
  font-weight: 700;
  font-variation-settings: "wdth" 84;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--stamp);
}

/* Loud, and naming exactly what is missing: fixing it has to be one glance and
   not a guess. The refusal on the request form and the stale-hold flag in the
   inbox are the same panel wearing the same ink — a struck panel, like the
   draft notice, rather than a tab down one side. Nothing else in this world
   carries a coloured edge on one side only. */
.review-refusal,
.request-refusal,
.inbox-stale-note {
  padding: 0.5rem 0.7rem;
  border: 2px solid var(--oxblood);
  border-radius: 2px;
  background: transparent;
  color: var(--oxblood);
  font-size: 0.9rem;
  width: fit-content;
  max-width: 100%;
}

.review-refusal {
  margin: 0;
}

/* Where a Draft stands, when it has anywhere to stand: quieter than a refusal,
   because nothing here was refused — a thing is waiting, or it needs a
   photograph, or it failed and there is a button for that. */
.review-draft-note {
  margin: 0;
  padding: 0.35rem 0 0;
  color: var(--ink-2);
  font-size: 0.85rem;
}

/* A five-column table on a 390px screen scrolls or it breaks the page. */
.table-scroll {
  overflow-x: auto;
}

/* ------------------------------------------------------------- the inbox --
 * The Owner's own queue: one thing per row, everybody who asked underneath it,
 * and the stale holds pushed to the top. Same stock and the same stamped
 * controls as Review, because it is the same sitting. */

.inbox-stale-note {
  margin: 1rem 0 0;
}

.inbox-item--stale {
  background: rgba(142, 42, 30, 0.05);
}

.inbox-title {
  font-weight: 700;
  color: var(--ink);
}

.inbox-winner {
  margin-top: 0.6rem;
  padding: 0.6rem 0.7rem;
  background: var(--stamp-wash);
  border-left: 3px solid var(--stamp);
}

.inbox-winner-line,
.inbox-request-line {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  font-size: 0.95rem;
}

/* Typed figures and addresses are Courier throughout, tabular so a column of
   them lines up. */
.inbox-email,
.inbox-when,
.inbox-offer {
  font-family: "Courier Prime", ui-monospace, monospace;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}

.inbox-message {
  margin: 0.35rem 0 0;
  font-size: 0.95rem;
  color: var(--ink);
  max-width: var(--measure);
}

.inbox-queue {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
}

.inbox-request {
  padding: 0.6rem 0;
  border-top: 1px solid var(--rule);
  display: grid;
  gap: 0.4rem;
  justify-items: start;
}

.inbox-decisions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 0.6rem;
}

.inbox-retry {
  margin-top: 0.4rem;
}

/* ---------------------------------------------------------------- boxes --
 * The hallway pass: an open box, a marker, and four characters read off tape.
 * The same sheet as Review, because it is the same Owner doing the same kind
 * of work — the field the codes go into is the one loud control on the page. */

.box-make {
  display: grid;
  gap: 0.6rem;
  justify-items: start;
  margin-top: 1rem;
  padding-top: 0.8rem;
  border-top: 1px solid var(--rule);
}

.box-destination {
  display: block;
  font-size: 0.9rem;
  font-weight: 700;
  font-variation-settings: "wdth" 88;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
}

/* What gets taped on the box. Sized in the page rather than in the SVG, which
   carries no dimensions of its own so the label sheet can print it at whatever
   the paper asks for. */
.box-qr {
  display: block;
  width: 180px;
  height: auto;
  margin-top: 1rem;
  background: #fff;
  border: 1px solid var(--rule);
}

/* Where a box's own page and the list both point at the printer. A line of
   type, not a control: printing is what happens after packing, not during. */
.sheet-link {
  margin: 1rem 0 0;
  font-size: 0.85rem;
}

/* The tick that puts one box on the sheet, wrapped around the code so the
   whole of it is the target a thumb has to hit. */
.pick-box {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
}

.pick-box input {
  width: 1.1rem;
  height: 1.1rem;
  accent-color: var(--stamp);
}

.label-picker .review-list-it {
  margin-top: 1rem;
}

/* The packing loop lives here: the field comes back empty on every put, so the
   next code goes in without a tap in between. */
.pack {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1rem;
  padding: 0.8rem 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}

.pack .intake-title {
  flex: 1 1 12rem;
  font-family: "Courier Prime", ui-monospace, monospace;
  text-transform: uppercase;
  letter-spacing: 0.18em;
}

.pack .request-refusal {
  flex: 1 1 100%;
}

.box-item .review-head {
  justify-content: flex-start;
}

.box-item .row-delete {
  margin-left: auto;
}

.box-delete {
  margin-top: 1.2rem;
}

.box-delete button {
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--oxblood);
  border: 1.5px solid currentColor;
  border-radius: 2px;
  background: transparent;
  padding: 0.5rem 0.85rem;
  min-height: 2.75rem;
  cursor: pointer;
}

/* --------------------------------------------------------------- repost --
 * One lot laid out as somebody else's form asks for it. It is the Owner's
 * screen but it wears the lot page's paper, because it is the same lot and the
 * trip here is one tap off that page. See docs/adr/0022. */

.repost {
  color: var(--ink);
  max-width: 48rem;
  margin: 0 auto;
  background: var(--sheet);
  box-shadow: var(--lift);
  border-top: 4px solid var(--ink);
  padding: clamp(1.25rem, 4vw, 2.25rem);
}

.repost-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1rem;
  border-bottom: 2px solid var(--ink);
  padding-bottom: 0.75rem;
}

.repost-head h1 {
  font-size: clamp(1.4rem, 4vw, 2.25rem);
  font-weight: 800;
  font-variation-settings: "wdth" 82;
  letter-spacing: -0.02em;
  line-height: 1;
  margin: 0;
  text-wrap: balance;
}

.repost-note,
.repost-hint {
  max-width: var(--measure);
  font-size: 0.85rem;
  color: var(--ink-2);
}

.repost-fields {
  margin: 1.5rem 0;
}

.repost-field {
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--rule);
}

.repost-field dt {
  font-size: 0.75rem;
  font-weight: 700;
  font-variation-settings: "wdth" 84;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.repost-field dd {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  margin: 0.35rem 0 0;
}

/* The box the paste comes out of. Monospaced: the Owner is checking a price
   and a set of dimensions against what is about to go on a stranger's screen. */
.repost-field input,
.repost-field textarea {
  flex: 1 1 auto;
  min-width: 0;
  font-family: "Courier Prime", ui-monospace, monospace;
  font-size: 0.95rem;
  padding: 0.55rem 0.6rem;
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  background: var(--sheet-sunk);
  color: var(--ink);
  resize: vertical;
}

.repost-copy {
  flex: 0 0 auto;
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--stamp);
  border: 1.5px solid currentColor;
  border-radius: 2px;
  background: transparent;
  padding: 0.5rem 0.85rem;
  min-height: 2.75rem;
  cursor: pointer;
}

.repost-plates h2 {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-bottom: 0.4rem;
}

.repost-plate-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  list-style: none;
  padding: 0;
  margin: 0.75rem 0 0;
}

.repost-plate-list a {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  font-family: "Courier Prime", ui-monospace, monospace;
  font-size: 0.75rem;
  text-decoration: none;
  color: var(--ink-2);
}

.repost-plate-list img {
  width: 96px;
  height: 96px;
  object-fit: cover;
  border: 1px solid var(--rule-strong);
  background: var(--sheet-sunk);
}

.repost-back {
  margin-top: 1.5rem;
  font-size: 0.85rem;
}

/* The sell pile's way out, on the row rather than the lot page: the list is the
   one Owner screen carrying a sell thing at every Sale Status. */
.row-repost {
  font-family: "Archivo", system-ui, sans-serif;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--stamp);
  text-decoration: none;
  border: 1px solid currentColor;
  border-radius: 2px;
  padding: 0.35rem 0.5rem;
  align-self: center;
  white-space: nowrap;
}

/* ----------------------------------------------------------- the tally --
 * The one screen that is read and not worked: a stock-take sheet, ruled, with
 * the figures in the clerk's column on the right. Nothing on it is a control
 * except the one line somebody else is waiting behind. */

.tallies {
  list-style: none;
  margin: 1.2rem 0 0;
  padding: 0;
  border-top: 1px solid var(--rule);
}

.tally {
  display: grid;
  grid-template-columns: 1fr auto auto auto auto;
  align-items: baseline;
  gap: 0 0.5rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--rule);
}

.tally-name {
  font-weight: 700;
  font-variation-settings: "wdth" 88;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-size: 0.78rem;
}

/* Two columns of typed figures, so the eye runs down them rather than across
   the words between. */
.tally-here,
.tally-departed,
.dashboard .asking,
.dashboard .sold-count,
.dashboard .sold-value,
.dashboard .open-requests {
  font-family: "Courier Prime", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

.tally-here {
  min-width: 3ch;
  text-align: right;
}

/* What has left is a fact about the past, and it sits back from what is still
   in the condo. */
.tally-departed {
  min-width: 3ch;
  text-align: right;
  color: var(--ink-2);
}

.tally-label {
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  min-width: 4ch;
}

.dashboard .money {
  margin: 1.1rem 0 0;
  font-size: 1.05rem;
}

.dashboard .waiting {
  margin: 0.4rem 0 0;
}

.dashboard .waiting a {
  color: var(--stamp);
  font-weight: 700;
}

.dashboard-heading {
  margin: 1.6rem 0 0;
  font-size: 0.78rem;
  font-weight: 700;
  font-variation-settings: "wdth" 88;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
}

/* Rooms nobody has walked into yet: a list of names, not a progress bar. */
.untouched {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0 1.1rem;
}

.untouched-room {
  font-weight: 700;
  color: var(--oxblood);
}

/* The catalog as a file. A link and not a button: it goes somewhere, and what
   it goes to is a download rather than an act. */
.dashboard .download {
  margin: 1.6rem 0 0;
}

.dashboard .download a {
  color: var(--stamp);
  font-weight: 700;
}
