/* RiskieAuction — a small, hand-written stylesheet. No build step, no framework.
   Colours are defined once as custom properties and swapped for dark mode. */

:root {
  color-scheme: light dark;

  --bg: #f6f7f9;
  --surface: #ffffff;
  --border: #dfe3e8;
  --text: #16181d;
  --muted: #5c6470;
  --accent: #1f5fd0;
  --accent-text: #ffffff;
  --danger: #b3261e;

  --radius: 10px;
  --gap: 1rem;
  --measure: 68rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14161a;
    --surface: #1c1f25;
    --border: #2e333c;
    --text: #e8eaee;
    --muted: #a0a8b4;
    --accent: #6fa1ff;
    --accent-text: #10131a;
    --danger: #ff8a80;
  }
}

* { box-sizing: border-box; }

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

a { color: var(--accent); }
a:hover { text-decoration: none; }

/* ---------------------------------------------------------------- layout */

.container {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 2rem 1.25rem 4rem;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem var(--gap);
  padding: 0.75rem 1.25rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.topbar nav {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-weight: 650;
  letter-spacing: -0.01em;
  color: var(--text);
  text-decoration: none;
}

/* Emoji come in at their own scale; nudge it to sit with the text. */
.brand-mark { font-size: 1.15em; line-height: 1; }

.footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.5rem var(--gap);
  max-width: var(--measure);
  margin: 0 auto;
  padding: 1.25rem;
  color: var(--muted);
  font-size: 0.875rem;
  border-top: 1px solid var(--border);
}

/* ---------------------------------------------------------------- text */

h1 { font-size: 1.6rem; letter-spacing: -0.02em; margin: 0 0 1rem; }
h2 { font-size: 1.2rem; margin: 2rem 0 0.75rem; }

.lead { font-size: 1.05rem; }
.muted { color: var(--muted); }

/* ---------------------------------------------------------------- blocks */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem;
}

.details {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.4rem 1.5rem;
  margin: 0 0 1.5rem;
}
.details dt { color: var(--muted); }
.details dd { margin: 0; }

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

.button,
button {
  font: inherit;
  cursor: pointer;
  /* A thumb needs a target, not a line of text: 44px is the smallest that is
     comfortably hittable, and it costs nothing with a mouse. */
  min-height: 2.75rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  border-radius: 8px;
  padding: 0.45rem 0.9rem;
  background: var(--accent);
  color: var(--accent-text);
  text-decoration: none;
}

.button:hover,
button:hover { filter: brightness(1.08); }

button.link {
  background: none;
  border: none;
  padding: 0;
  min-height: 0;
  color: var(--accent);
  text-decoration: underline;
}

form.inline { display: inline; }

/* A refused row in a queue: the field keeps what was typed and says why. */
.field-error { border-color: var(--danger) !important; }
.field-message { color: var(--danger); margin: 0.2rem 0 0; }

/* ------------------------------------------------------------------- tabs */

/* Two switchers that carry a whole area between them, so they are marked rather than
   hinted at: the listing filters are muted text because there are six of them above a
   list that shows what it is; these two look the same and lead somewhere else. */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0 0 1.5rem;
}
.tabs a {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.75rem;
  padding: 0.45rem 1rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--muted);
  font-weight: 600;
  text-decoration: none;
}
.tabs a:hover { color: var(--text); }
.tabs a.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-text);
}
/* The count rides inside the active tab, so it has to turn around with it: the chip
   keeps its own background otherwise, and on the filled tab that left dark digits on a
   dark circle — the number was there and unreadable. Taking the tab's text colour for
   the chip and the tab's fill for the digits inverts the pair instead of recolouring
   half of it. */
.tabs a.active .tag {
  background: var(--accent-text);
  border-color: var(--accent-text);
  color: var(--accent);
}

/* The two amounts on a contested lot buy different things, so they are two buttons
   rather than one and a number to retype. */
.bid-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }

/* A worked example: the steps of one calculation stack without gaps, so they read as
   one movement, and what follows is set apart as the separate thought it is. */
.worked > p { margin: 0; }
.worked-next { margin: 0.75rem 0 0; }

/* --------------------------------------------------------------- ladder */

/* One box per device: filled where a bid has settled it, marked where a draw will,
   empty where nobody has bid. The ladder says who; this says how many, which a list
   of bidders cannot show — three names marked "draw" say nothing about whether they
   are playing for one device or for three. */
.units {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  list-style: none;
  margin: 0 0 0.4rem;
  padding: 0;
}
.unit {
  width: 2rem;
  height: 1.6rem;
  border-radius: 5px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.8125rem;
  font-weight: 650;
}
.unit-won { background: #2e7d32; }
.unit-drawn { background: #9a7400; color: #fff; }
.unit-free { border: 1px dashed var(--border); }
.units-caption { margin: 0 0 0.75rem; }

@media (prefers-color-scheme: dark) {
  .unit-won { background: #81c784; }
  .unit-drawn { background: #e0c200; color: #10131a; }
}

/* Who is winning a lot. Ordinary text in a grid, so a phone renders it at the size
   it renders everything else at; only the bar is drawn, because a width in percent
   cannot be written onto an element under the content security policy. */
.ladder {
  list-style: none;
  margin: 1rem 0 0.5rem;
  padding: 0;
  font-size: 0.875rem;
}
.ladder-row {
  display: grid;
  grid-template-columns: minmax(5rem, 9rem) 1fr auto 3.2rem;
  align-items: center;
  gap: 0.5rem 0.75rem;
  padding: 0.2rem 0;
}
.ladder-name { overflow-wrap: anywhere; }
.ladder-track { display: block; }
.ladder-track svg { display: block; width: 100%; height: 0.7rem; }
.ladder-amount { font-variant-numeric: tabular-nums; white-space: nowrap; }
.ladder-tag {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: currentColor;
}

.ladder-win rect { fill: #2e7d32; }
.ladder-win .ladder-tag { color: #2e7d32; }
.ladder-draw rect { fill: #9a7400; }
.ladder-draw .ladder-tag { color: #9a7400; }
.ladder-out rect { fill: var(--border); }
.ladder-out .ladder-name,
.ladder-out .ladder-amount { color: var(--muted); }

/* The line where the units run out, with the one number that matters on it. It is a
   row of its own, so the label cannot land on top of the bid above it. */
.ladder-cut {
  border-top: 1px dashed var(--text);
  margin: 0.45rem 0 0.15rem;
  padding-top: 0.3rem;
  text-align: right;
  font-size: 0.8125rem;
  /* The one number everybody in the picture pays, so it reads as text and not as a
     footnote about the rows above it. */
  color: var(--text);
}
.ladder-more { padding-top: 0.3rem; font-size: 0.8125rem; color: var(--muted); }

@media (prefers-color-scheme: dark) {
  .ladder-win rect { fill: #81c784; }
  .ladder-win .ladder-tag { color: #81c784; }
  .ladder-draw rect { fill: #e0c200; }
  .ladder-draw .ladder-tag { color: #e0c200; }
}

/* On a phone the name gets its own line, so the bar keeps a usable width. */
@media (max-width: 34rem) {
  .ladder-row {
    grid-template-columns: 1fr auto 3.2rem;
    row-gap: 0.15rem;
  }
  .ladder-name { grid-column: 1 / -1; }
}

/* ------------------------------------------------------------ condition */

/* A traffic light for how worn the item is: a box beside the title on the item page,
   a chip on the cards where the same judgement is made at a glance across a dozen of
   them. The colour carries the meaning, so the word stays beside it — red and green
   are the two colours most often not seen as different, and a bar nobody can read is
   worse than no bar.

   The tone classes carry nothing but the colour, so both shapes share one scale. The
   tint is that colour at low opacity over the page, which keeps it legible in either
   scheme without a second palette; where color-mix is missing the tint drops out and
   the coloured edge and text remain. */
.condition-new    { color: #2e7d32; background: color-mix(in srgb, #2e7d32 8%, transparent); }
.condition-good   { color: #1f5fd0; background: color-mix(in srgb, #1f5fd0 8%, transparent); }
.condition-fair   { color: #9a7400; background: color-mix(in srgb, #9a7400 10%, transparent); }
.condition-worn   { color: #c05600; background: color-mix(in srgb, #c05600 10%, transparent); }
.condition-broken { color: #b3261e; background: color-mix(in srgb, #b3261e 10%, transparent); }
/* Words from before the list existed: the shape, none of the claim. */
.condition-plain  { color: var(--muted); background: none; }

@media (prefers-color-scheme: dark) {
  .condition-new    { color: #81c784; background: color-mix(in srgb, #81c784 12%, transparent); }
  .condition-good   { color: #86b0ff; background: color-mix(in srgb, #86b0ff 12%, transparent); }
  .condition-fair   { color: #e0c200; background: color-mix(in srgb, #e0c200 12%, transparent); }
  .condition-worn   { color: #ff9d4d; background: color-mix(in srgb, #ff9d4d 12%, transparent); }
  .condition-broken { color: #ff7b72; background: color-mix(in srgb, #ff7b72 12%, transparent); }
}

.condition {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.25rem 0.6rem;
  margin: 0 0 1rem;
  padding: 0.5rem 0.9rem;
  border: 1px solid currentColor;
  border-left-width: 5px;
  border-radius: 8px;
}
.condition-plain.condition { border-color: var(--border); }
.condition-label {
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.75;
}
.condition-value { font-weight: 650; }

/* On a card it sits beside the status badge and has to read as its sibling: the same
   pill, wearing the scale's colour instead of the muted grey. Not upper-cased — these
   are sentences, and "USED, SIGNIFICANT SIGNS OF USE" shouts across a grid. */
.condition-chip {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  border: 1px solid currentColor;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  /* It carries the short form, which fits. A pill that wraps stops being one. */
  white-space: nowrap;
}
.condition-plain.condition-chip { border-color: var(--border); font-weight: 400; }

/* ---------------------------------------------------------------- combobox */

/* Without JavaScript this is a plain list under the field: still useful, because it
   shows which categories exist. The script adds .js and it becomes a dropdown. */
.combobox { position: relative; }
.combobox-options {
  list-style: none;
  margin: 0.35rem 0 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  max-height: 14rem;
  overflow-y: auto;
}
.combobox.js .combobox-options {
  position: absolute;
  z-index: 5;
  left: 0;
  right: 0;
  margin-top: 0.15rem;
  box-shadow: 0 6px 18px rgb(0 0 0 / 18%);
}
.combobox-options li {
  padding: 0.4rem 0.6rem;
  cursor: default;
}
.combobox.js .combobox-options li { cursor: pointer; }
.combobox-options li + li { border-top: 1px solid var(--border); }
.combobox-options li.current,
.combobox.js .combobox-options li:hover { background: var(--bg); }

/* The sign-in page is all there is to see without a session, so it gets room. */
.signin { max-width: 34rem; margin: 3rem auto; text-align: center; }
.signin h1 { margin-bottom: 0.75rem; }
.signin .lead { margin-bottom: 1.75rem; }

/* ---------------------------------------------------------------- listings */

.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gap);
}

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  font-size: 0.9375rem;
}
/* Above a table rather than beside a heading, the row needs its own space. */
nav.filters + * { margin-top: 1.25rem; }
/* The second row is a choice inside the first, so it sits under it rather than beside
   the heading, and reads one step quieter. */
.filters-category { margin: 0.75rem 0 0; font-size: 0.875rem; }
.filters a { color: var(--muted); text-decoration: none; }
.filters a.active { color: var(--text); font-weight: 600; }

/* The search above a list. The field needs min-width: 0 or it keeps the twenty
   characters an input is intrinsically wide and pushes the button out of the row. */
.search {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  margin: 1.25rem 0 0;
}
.search input { flex: 1 1 18rem; min-width: 0; }
nav.filters + .search { margin-top: 1rem; }
.search + * { margin-top: 1.25rem; }

.item-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 1.25rem;
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
}

.item-card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem;
}

.item-link {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  color: var(--text);
  text-decoration: none;
}

.thumb {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 8px;
  background: var(--bg);
}
.thumb-empty {
  border: 1px dashed var(--border);
}

.item-title { font-weight: 600; line-height: 1.3; }

.item-meta,
.item-price {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}
.item-price strong { font-size: 1.15rem; }

.draw-hint { color: var(--accent); margin: 0; }

/* ---------------------------------------------------------------- badges */

.badge {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.badge-open { border-color: var(--accent); color: var(--accent); }
.badge-sold { border-color: #2e7d32; color: #2e7d32; }
.badge-cancelled { border-color: var(--danger); color: var(--danger); }
.badge-draft { border-style: dashed; }

@media (prefers-color-scheme: dark) {
  .badge-sold { border-color: #81c784; color: #81c784; }
}

/* ---------------------------------------------------------------- item page */

.breadcrumb { margin: 0 0 1rem; font-size: 0.9375rem; }

.item-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
}
.item-head h1 { margin: 0; }

.item-detail {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(16rem, 1fr);
  gap: 2rem;
  align-items: start;
}

@media (max-width: 48rem) {
  .item-detail { grid-template-columns: 1fr; }
}

.description {
  /* Descriptions are plain text; keep the author's line breaks without ever
     putting their input through the HTML parser. */
  white-space: pre-wrap;
}

.gallery {
  margin: 0 0 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.gallery-main {
  width: 100%;
  max-height: 28rem;
  object-fit: contain;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.thumb-link {
  display: block;
  border: 2px solid transparent;
  border-radius: 8px;
  line-height: 0;
}
.thumb-link img {
  width: 5rem;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--border);
}
.thumb-link.current { border-color: var(--accent); }

/* The large photo is a link to the file, so it is a block and not a line of text. */
.gallery-open { display: block; line-height: 0; cursor: zoom-in; }

/* The full-size view. It fills the window and the photo is fitted inside it, so
   every picture is shown whole whatever shape it has; the page behind is held still
   while it is open. The colours here are deliberately not the theme's: this sits on
   top of a photograph, where a light scheme would leave white controls on a white
   wall. */
html.lightbox-open { overflow: hidden; }

.lightbox {
  width: 100vw;
  height: 100vh;
  height: 100dvh; /* the second wins where it is understood: vh lies under a phone's toolbar */
  max-width: 100vw;
  max-height: 100dvh;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  overflow: hidden;
}
/* Only while it is open, or this display would override the one the browser uses to
   keep a closed dialog closed, and the photo would sit on the page for ever. */
.lightbox[open] {
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox::backdrop { background: rgba(0, 0, 0, 0.92); }

/* The picture takes its own shape and stops at the edges of the window. It is sized
   rather than stretched on purpose: the space beside a portrait photo then belongs to
   the dialog, so tapping there closes it — with an image filling the whole box that
   space is part of the picture, and the only way out on a phone would be the one
   small button in the corner. */
.lightbox-image {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
}

.lightbox-button {
  position: absolute;
  /* Big enough to hit with a thumb without looking. */
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  line-height: 1;
  color: #fff;
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 50%;
  cursor: pointer;
}
.lightbox-button:hover { background: rgba(0, 0, 0, 0.75); }
.lightbox-close { top: 0.75rem; right: 0.75rem; }
.lightbox-prev { left: 0.75rem; top: 50%; transform: translateY(-50%); }
.lightbox-next { right: 0.75rem; top: 50%; transform: translateY(-50%); }

.lightbox-count {
  position: absolute;
  left: 50%;
  bottom: 0.75rem;
  transform: translateX(-50%);
  margin: 0;
  padding: 0.2rem 0.6rem;
  font-size: 0.8125rem;
  color: #fff;
  background: rgba(0, 0, 0, 0.55);
  border-radius: 999px;
}

.callout-muted { border-left-color: var(--muted); color: var(--muted); }
.callout-binding { border-left-color: var(--danger); margin-top: 0; }

/* The rules are lists of short statements, not prose. */
ul.rules { padding-left: 1.2rem; }
ul.rules li { margin-bottom: 0.35rem; }

/* The admin gallery keeps its row of small pictures with a remove button each. */
.admin-columns .gallery {
  flex-direction: row;
  flex-wrap: wrap;
  gap: 0.75rem;
}
.admin-columns .gallery img {
  width: 10rem;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 8px;
  border: 1px solid var(--border);
}

.price-panel {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  justify-content: space-between;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem;
  margin: 1.5rem 0;
}

.price-main { display: flex; flex-direction: column; gap: 0.15rem; }
.price-label { color: var(--muted); font-size: 0.875rem; }
.price-value { font-size: 1.9rem; font-weight: 650; letter-spacing: -0.02em; }
.price-note { color: var(--muted); font-size: 0.875rem; }

.price-side {
  display: grid;
  grid-template-columns: max-content max-content;
  gap: 0.25rem 1.25rem;
  margin: 0;
  font-size: 0.9375rem;
}
.price-side dt { color: var(--muted); }
.price-side dd { margin: 0; }

.countdown { font-variant-numeric: tabular-nums; }
.ending-soon { color: var(--danger); font-weight: 600; }

/* ---------------------------------------------------------------- callouts */

.callout {
  border: 1px solid var(--border);
  border-left-width: 4px;
  border-radius: 8px;
  padding: 0.9rem 1.1rem;
  margin: 1.25rem 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.callout-draw { border-left-color: var(--accent); }
.callout-cancelled { border-left-color: var(--danger); }
.callout-result { border-left-color: #2e7d32; }
/* The warning before a step that cannot be walked back by the application itself. It
   borrows the danger colour rather than a new one: the page has exactly one of these,
   and a second warning shade would only compete with it. */
.callout-warning { border-left-color: var(--danger); }
.draw-pool { font-size: 0.9375rem; color: var(--muted); }

.alert {
  border-radius: 8px;
  padding: 0.75rem 1rem;
  margin: 0 0 1.25rem;
  border: 1px solid var(--border);
}
.alert-ok { border-left: 4px solid #2e7d32; }
.alert-error { border-left: 4px solid var(--danger); }
.alert-warning { border-left: 4px solid #ed6c02; }

.hint { color: var(--accent); }

/* ---------------------------------------------------------------- forms */

.bid-form,
.question-form {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem;
  margin-bottom: 1.25rem;
}
.bid-form label,
.question-form label { font-weight: 600; }

.amount-input {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.amount-input input {
  flex: 1;
  /* A form control carries an intrinsic width — an input is about twenty characters
     wide by default — and a flex item does not shrink below that on its own. In the
     narrow bid card the field therefore kept its size and pushed the euro sign out of
     the box. min-width lets it give way. */
  min-width: 0;
  font: inherit;
  font-size: 1.2rem;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  color: var(--text);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.amount-input .currency { color: var(--muted); font-size: 1.2rem; }

textarea {
  font: inherit;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  color: var(--text);
  resize: vertical;
}

.checkbox { display: flex; align-items: center; gap: 0.5rem; font-weight: 400; }

button.danger { background: var(--danger); }
/* The way out of a page that asks for a decision: present, but not competing with the
   button that does the thing. */
.button-quiet {
  background: transparent;
  border-color: var(--border);
  color: var(--text);
}

.item-side .card + .card { margin-top: 1.25rem; }
.item-side h3 { margin: 0 0 0.5rem; font-size: 1rem; }

ul.plain { list-style: none; margin: 0 0 0.75rem; padding: 0; }
ul.plain li { padding: 0.15rem 0; }

/* ---------------------------------------------------------------- tables */

table.bids,
table.tickets {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
  margin-bottom: 0.75rem;
}
table.bids th,
table.bids td,
table.tickets th,
table.tickets td {
  text-align: left;
  padding: 0.45rem 0.6rem;
  border-bottom: 1px solid var(--border);
}
table.bids th,
table.tickets th { color: var(--muted); font-weight: 600; }
.num { text-align: right !important; font-variant-numeric: tabular-nums; }

tr.inactive td { color: var(--muted); text-decoration: line-through; }
tr.inactive td:last-child { text-decoration: none; }
tr.winner { font-weight: 600; }

.tag {
  display: inline-block;
  padding: 0.05rem 0.45rem;
  border-radius: 999px;
  background: var(--bg);
  border: 1px solid var(--border);
  font-size: 0.75rem;
  color: var(--muted);
  text-decoration: none;
}
.tag-max { border-color: var(--accent); color: var(--accent); }
.tag-leading { border-color: #2e7d32; color: #2e7d32; }
.tag-won { border-color: #2e7d32; color: #2e7d32; font-weight: 600; }

/* ---------------------------------------------------------------- draw & rules */

.draw-record,
.nerd-box {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem 1.25rem;
  margin: 1.5rem 0;
}
.draw-record summary,
.nerd-box summary {
  cursor: pointer;
  font-weight: 600;
}
.draw-record[open] summary,
.nerd-box[open] summary { margin-bottom: 0.75rem; }

.nerd-box h3 { font-size: 1rem; margin: 1.25rem 0 0.4rem; }

code, pre {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  font-size: 0.875em;
}
pre {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.75rem 0.9rem;
  overflow-x: auto;
}
code.hash { word-break: break-all; color: var(--muted); }
.commitment { margin: 1rem 0; }

/* ---------------------------------------------------------------- questions */

ul.qa { list-style: none; margin: 0 0 1.5rem; padding: 0; }
ul.qa > li {
  border-left: 3px solid var(--border);
  padding: 0 0 0 1rem;
  margin-bottom: 1.25rem;
}
.question, .answer { margin: 0 0 0.35rem; white-space: pre-wrap; }
.answer { color: var(--text); }

.small { font-size: 0.875rem; }

.footer > span:last-child {
  display: inline-flex;
  gap: 1rem;
  align-items: baseline;
}

.footer .version {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.8125rem;
  color: var(--muted);
}

/* ---------------------------------------------------------------- admin */

.admin-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  padding: 0.6rem 0;
  margin-bottom: 1.5rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.9375rem;
}
.admin-nav a { color: var(--muted); text-decoration: none; }
.admin-nav a.active { color: var(--text); font-weight: 600; }

.stat-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  list-style: none;
  padding: 0;
  margin: 0 0 1.5rem;
  color: var(--muted);
}
.stat-number {
  display: block;
  font-size: 1.6rem;
  font-weight: 650;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.admin-columns {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(18rem, 2fr);
  gap: 2rem;
  align-items: start;
}
@media (max-width: 60rem) {
  .admin-columns { grid-template-columns: 1fr; }
}

.admin-form {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem;
  margin-bottom: 1.5rem;
}
.admin-form h3 { margin: 0 0 0.25rem; font-size: 1rem; }
.admin-form label { font-weight: 600; font-size: 0.9375rem; }
/* Selected by exclusion rather than by listing the types that get styled: the
   justification field was type="url" and fell through a list that named text,
   datetime-local and file, so it rendered as a narrow browser default beside its
   label while every field around it was full width. A list like that is a trap for
   the next type somebody reaches for — email, number, search. */
.admin-form input:not([type="hidden"]):not([type="submit"]):not([type="checkbox"]):not([type="radio"]),
.admin-form select,
.field {
  font: inherit;
  padding: 0.4rem 0.55rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  color: var(--text);
  width: 100%;
}
.admin-form input:disabled { opacity: 0.6; }

.form-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}
.form-row > div { flex: 1 1 10rem; }

.form-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 0.5rem;
}

.answer-form {
  display: flex;
  gap: 0.5rem;
  align-items: flex-start;
  margin: 0.5rem 0;
}
/* Same trap: a textarea is twenty columns wide until it is told it may shrink. */
.answer-form textarea { flex: 1; min-width: 0; }

.inline-form { display: inline; }
.inline-form input:not([type="hidden"]) {
  font: inherit;
  font-size: 0.875rem;
  padding: 0.15rem 0.35rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
}

td.actions { white-space: nowrap; }
td.actions form + form { margin-left: 0.75rem; }

.details-cell { font-size: 0.8125rem; color: var(--muted); }
.kv { white-space: nowrap; margin-right: 0.5rem; }

.gallery figure { margin: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.gallery figcaption { font-size: 0.8125rem; }

table.wide { margin-bottom: 1.5rem; }

/* ---------------------------------------------------------------- pager */

.pager {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  margin: 0 0 1.5rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
  font-size: 0.9375rem;
}

.pager-link { text-decoration: none; }
.pager-link.disabled { color: var(--muted); opacity: 0.5; }
.pager-position { color: var(--muted); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------- pickup slip */

.slip {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  max-width: 34rem;
  margin: 0 auto 2rem;
}
.slip-head {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: flex-start;
  justify-content: space-between;
}
.slip-head h1 { margin: 0; font-size: 1.3rem; }
.slip-number { font-size: 1.6rem; font-weight: 650; margin: 0.25rem 0 0; }
.slip-title { margin: 1rem 0; font-size: 1.1rem; }
.slip-done {
  border: 1px solid var(--danger);
  border-left-width: 4px;
  border-radius: 8px;
  padding: 0.6rem 0.9rem;
  margin: 1rem 0 0;
}

.slip-note { margin-top: 1.25rem; }
/* The one line the person fetching the item actually reads. */
.slip-location { font-weight: 650; }
.slip-link { overflow-wrap: anywhere; }
.qr { flex: none; background: #fff; border-radius: 6px; }

/* Printed, the slip is the whole page: the chrome of a website on a piece of paper
   is noise, and the ink is somebody else's. */
@media print {
  .topbar,
  .footer { display: none; }
  body { background: #fff; }
  .container { padding: 0; max-width: none; }
  .slip {
    border: none;
    padding: 0;
    max-width: none;
    background: #fff;
  }
  a[href] { text-decoration: none; color: inherit; }
}

/* ------------------------------------------------------------ narrow screens */

/* Everything below is for phones. It is one block at the end rather than a media
   query next to each rule, so that "how does this look on a phone" is a question
   with one place to look.

   Inputs are the exception that has to be stated up front: Safari zooms the page
   when a focused field has text under 16px, and leaves it zoomed. Every field is
   therefore at least 16px on every screen, not only on small ones. */
input,
textarea,
select { font-size: max(1rem, 16px); }

/* A wide table scrolls inside its own box. Without this the table makes the whole
   document wider than the screen, which drags every other section sideways with it
   — the layout looks broken everywhere except where the actual problem is. */
.scroll-x {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin-bottom: 1.5rem;
}
.scroll-x > table { margin-bottom: 0.25rem; }

@media (max-width: 40rem) {
  .container { padding: 1.25rem 1rem 3rem; }
  h1 { font-size: 1.4rem; }
  h2 { font-size: 1.1rem; margin-top: 1.5rem; }

  /* The brand keeps its line and the navigation gets the next one, rather than the
     six links of an admin pushing the page wider than the screen. */
  .topbar { align-items: flex-start; }
  .topbar nav { width: 100%; gap: 0.35rem 1rem; }

  /* The price and the facts beside it each take the full width; a long "ends at" date
     next to its label would otherwise be the widest thing on the page. */
  .price-panel { gap: 1rem; padding: 1rem; }
  .price-side { width: 100%; grid-template-columns: max-content minmax(0, 1fr); }

  /* Thumbnails scroll in one row instead of stacking into four. */
  .thumbs {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 0.25rem;
  }
  .thumb-link { flex: 0 0 auto; }
  /* Fifteen rem of a laptop is a stamp. The picture is the thing being judged, so it
     gets most of the screen before anybody has to open it. */
  .gallery-main { max-height: 60vh; }

  /* A table with five columns cannot be read on a phone, so each row becomes a card
     and every value is labelled with the column heading it lost. The labels come from
     data-label in the markup; a cell without one simply has no label. */
  table.cards thead { display: none; }
  table.cards,
  table.cards tbody,
  table.cards tr,
  table.cards td { display: block; width: 100%; }
  table.cards tr {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.5rem 0.75rem;
    margin-bottom: 0.6rem;
  }
  table.cards td {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 0.15rem 0.75rem;
    border: 0;
    padding: 0.2rem 0;
    /* A 64-character ticket has nowhere to break on its own. */
    overflow-wrap: anywhere;
  }
  table.cards td::before {
    content: attr(data-label);
    color: var(--muted);
    font-size: 0.875rem;
    flex: 0 0 auto;
  }
  table.cards td:not([data-label])::before { content: none; }
  table.cards td.num { text-align: left !important; }
  /* A row that is only struck through reads as a mistake; the label stays legible. */
  table.cards tr.inactive td { text-decoration: none; }
  table.cards tr.inactive { opacity: 0.7; }

  /* The pager sits under its cards, not spread across the full width. */
  .pager { justify-content: flex-start; }

  /* The one button of a bid or a question fills the width — a small button under a
     full-width field is a small target for no reason. Forms with several actions wrap
     instead, so two buttons do not end up as two halves of a line. */
  .bid-form > button,
  .question-form > button,
  .item-side form > button { width: 100%; }
  .form-actions { flex-wrap: wrap; }
  .footer { flex-direction: column; }
}

/* The install offer. It is built by install.js and is absent from every page the
   script did not add it to, so nothing here has a counterpart in a template.

   Fixed to the foot of the window rather than the document: on a phone the page is
   long and an offer that scrolled away at the top would never be seen. The safe-area
   inset keeps it clear of the home indicator on an iPhone, where the bar would
   otherwise sit under it. */
.install-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.85rem 1rem calc(0.85rem + env(safe-area-inset-bottom));
  background: var(--surface);
  border-top: 1px solid var(--border);
  box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.12);
}

.install-bar p {
  margin: 0;
  /* min-width: 0 so a long sentence wraps inside the flex row instead of widening
     it — the same trap the bid amount field fell into. */
  min-width: 0;
  flex: 1 1 14rem;
  font-size: 0.95rem;
}

.install-actions {
  display: flex;
  gap: 0.5rem;
  flex-shrink: 0;
}

.install-bar button {
  font: inherit;
  padding: 0.55rem 0.9rem;
  border-radius: var(--radius, 10px);
  border: 1px solid var(--border);
  cursor: pointer;
}

.install-action {
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
}

.install-dismiss {
  background: transparent;
  color: var(--muted);
}

/* An installed app never needs to be offered again. The script checks this too; the
   rule is here because a browser can enter standalone mode without a reload. */
@media (display-mode: standalone) {
  .install-bar { display: none; }
}

/* Room for the bar, added by the script only while the bar is there. The content
   security policy allows no inline style, so the class is how a script changes
   anything about the layout. */
.has-install-bar { padding-bottom: 6rem; }

/* The confirmation step. It replaces a window.confirm(), so it appears where the
   buttons were and takes their place while it is open — two live ways to send the
   same form is how a double submit starts. Built by app.js; no markup for it exists
   in any template. */
.confirm-step {
  margin-top: 0.75rem;
  padding: 0.85rem 1rem;
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--bg);
}

.confirm-step p {
  margin: 0 0 0.75rem;
  font-size: 0.95rem;
}

.confirm-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.confirm-no {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
}

/* The hidden attribute has to beat the display rules on .bid-actions and
   .form-actions, which are flex and would otherwise keep showing them: a class
   selector and an attribute selector have the same weight, and the author stylesheet
   always outranks the browser's own. */
[hidden] { display: none !important; }
