/* ---------------------------------------------------------------------------
   Auction Calendar — blacksoxfan.com
   Visual language taken from the site banner: near-black ground, off-white
   serif wordmark, high-contrast B&W photography, hairline rules. Serif for
   headings, system sans for data. One restrained accent (aged gold) for the
   thing that matters most: the close date.
   No external fonts, no build step.

   Breakpoints — three designed states, no gaps:
     <= 640px            phone: tables become stacked cards
     641px .. 1023px     tight table: no relative dates, trimmed padding
     >= 900px            consign column appears; filter tree goes two-up
     >= 1024px           roomy table: relative dates ("in 3d") return
--------------------------------------------------------------------------- */

:root {
  --bg:        #0a0a0b;
  --panel:     #121214;
  --panel-2:   #191a1d;
  --rule:      rgba(255,255,255,0.13);
  --rule-soft: rgba(255,255,255,0.07);

  --ink:       #ece8e0;
  --ink-dim:   #a8a49c;
  /* 5.46:1 on --bg, 4.80:1 on --panel-2 — clears WCAG AA for normal text
     everywhere it is used. Was #706d67 (3.84:1), which did not. */
  --ink-faint: #8a867e;
  --ink-past:  #6f6c66;   /* de-emphasis only, never carries text meaning alone */

  --gold:      #d8b76a;   /* close  — primary axis */
  --blue:      #7ea8bd;   /* start  — secondary    */
  --rust:      #c58a63;   /* consign — tertiary    */
  --today:     #f0e6cf;

  --serif: Georgia, "Iowan Old Style", "Times New Roman", Times, serif;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --wrap: 1180px;
  --r: 3px;

  /* The banner's own intrinsic width — the only number in this sheet that
     knows anything about banner.jpg, and it exists solely to stop the image
     being upscaled past 1:1. Height is never stated: the <img> carries
     width/height attributes, so the box is reserved at the right ratio before
     the bytes land and a differently-proportioned replacement just works. */
  --banner-w: 710px;
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.45;
}

h1, h2, h3 { font-family: var(--serif); font-weight: 400; letter-spacing: 0.01em; }

a { color: var(--ink); text-decoration-color: rgba(216,183,106,0.6); text-underline-offset: 2px; }
a:hover { color: var(--gold); }

code { font-family: var(--mono); font-size: 0.92em; color: var(--gold); }

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

/* The UA sheet's [hidden] { display: none } loses to any class that sets a
   display. Every element this site hides with the attribute and styles with a
   display needs its own rule — these are those elements. */
.controls[hidden],
.masthead-tools[hidden],
.table-strip[hidden],
.marker-toggles[hidden],
.table-section[hidden],
#filters[hidden],
.section-panel[hidden],
.undo-strip[hidden],
.types[hidden] { display: none !important; }

/* --- masthead ----------------------------------------------------------- */

.masthead { border-bottom: 1px solid var(--rule); background: #000; }

/* Constrained by width only, never by height: the aspect ratio comes from the
   file's own width/height attributes, so the box is the right shape before the
   image arrives and a replacement of any proportion drops straight in. */
.banner {
  display: block;
  width: 100%;
  max-width: var(--banner-w);   /* never upscaled past 1:1 */
  height: auto;
  margin: 0 auto;
}

/* Row one of two: the wordmark, and Settings hard right. Nothing else. */
.masthead-bar {
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 8px 16px 10px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 16px;
  border-top: 1px solid var(--rule-soft);
}

.masthead-bar h1 {
  margin: 0;
  font-size: 24px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.masthead-tools {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}
/* Reset used to sit here beside Settings and needed borderless treatment to
   match it. It has moved into the Settings panel, next to the saved view it
   is the counterweight to, so the masthead now holds the gear alone. */

/* the JS-has-not-run warning gets its own line when it is there at all, and
   takes none when app.js hides it */
.masthead-bar .js-check { flex: 1 0 100%; }

.meta { margin: 0; color: var(--ink-faint); font-size: 12.5px; }
.meta a { color: var(--ink-dim); }

.js-check { color: var(--rust); }

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

main {
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 16px 48px;
}

section { margin-top: 26px; scroll-margin-top: calc(var(--stick, 55px) + 8px); }

.notice {
  border: 1px solid var(--rust);
  background: rgba(197,138,99,0.08);
  border-radius: var(--r);
  padding: 14px 16px;
}
.notice h2 { margin: 0 0 6px; font-size: 18px; }
.notice p { margin: 6px 0 0; color: var(--ink-dim); font-size: 14px; }
.notice-hint { border-top: 1px solid var(--rule-soft); padding-top: 8px; }
.notice-actions { padding-top: 10px; }
.notice-warn { border-color: var(--gold); background: rgba(216,183,106,0.07); }

/* --- buttons, chips ----------------------------------------------------- */

.btn {
  font-family: var(--sans);
  font-size: 13px;
  color: var(--ink);
  background: var(--panel-2);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  padding: 7px 12px;
  cursor: pointer;
  min-height: 34px;
}
.btn:hover { border-color: rgba(255,255,255,0.32); }
.btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }

.btn-primary { background: var(--gold); color: #16130b; border-color: var(--gold); font-weight: 600; }
.btn-primary:hover { background: #e5c67d; }

/* Settings is borderless on purpose: it sits among bare toggles, and a box
   around the only one of them made it the odd element out. It earns its
   prominence from position — hard right, alone — not from a rule. A background
   appears on hover and focus so it still reads as something you can press.
     desktop  ⚙ Settings · N hidden
     phone    ⚙ •            (the word costs room the 390px bar hasn't got;
                              the button's aria-label carries the full meaning
                              at every width, so nothing is lost non-visually) */
.settings-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
  background: transparent;
  border: 0;
  color: var(--ink);
  padding: 6px 8px;
  border-radius: var(--r);
}
.settings-btn:hover { background: rgba(255,255,255,0.07); color: var(--ink); border-color: transparent; }
.settings-btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; background: rgba(255,255,255,0.07); }
.ico-gear { width: 1.15em; height: 1.15em; flex: 0 0 auto; color: var(--gold); }

.btn-badge { display: inline-flex; align-items: center; }
.btn-badge[hidden] { display: none; }
/* no pill here — the count is a plain continuation of the label, hung off a
   middot so it reads as one phrase: "Settings · 112 hidden" */
.badge-count { font-size: 13px; color: var(--ink-dim); }
.badge-count::before { content: "· "; }
/* 3ch holds the widest possible count (522), so 112 -> 9 does not shuffle the bar */
.badge-n { display: inline-block; min-width: 3ch; text-align: right; font-variant-numeric: tabular-nums; }
.badge-dot {
  display: none; width: 8px; height: 8px; border-radius: 50%;
  background: var(--gold);
}
.btn-quiet { background: transparent; color: var(--ink-dim); }
.btn-quiet:hover { color: var(--ink); }
.btn-nav { min-width: 38px; font-size: 18px; line-height: 1; padding: 4px 10px; }
.btn-seg { background: transparent; color: var(--ink-dim); border-radius: 0; }
.btn-seg.is-on { background: var(--panel-2); color: var(--ink); box-shadow: inset 0 -2px 0 var(--gold); }
.org-toggle { display: inline-flex; border: 1px solid var(--rule); border-radius: var(--r); overflow: hidden; }
.org-toggle .btn-seg { border: 0; border-right: 1px solid var(--rule); }
.org-toggle .btn-seg:last-child { border-right: 0; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  color: var(--ink-dim);
  border: 1px solid var(--rule);
  border-left-width: 3px;
  border-left-color: var(--rule);
  border-radius: var(--r);
  padding: 6px 11px;
  cursor: pointer;
  user-select: none;
  min-height: 34px;
}
.chip:hover { color: var(--ink); }
/* A real checkbox with no box of its own. It keeps the native checked state for
   a screen reader and stays in the tab order; the swatch beside it is the only
   thing drawn, so the state is stated once rather than twice. Not display:none,
   which would take it out of the tab order and off the a11y tree. */
.chip input {
  position: absolute; width: 1px; height: 1px; margin: 0; padding: 0;
  opacity: 0; pointer-events: none;
}
/* the focus ring belongs to the chip now — the input has no visible box to put
   one on, and a keyboard user must still be able to see where they are */
.chip:has(input:focus-visible) { outline: 2px solid var(--gold); outline-offset: 1px; }
.chip-end   { border-left-color: var(--gold); }
.chip-start { border-left-color: var(--blue); }
.chip-consign { border-left-color: var(--rust); }
.chip-past  { border-left-color: var(--ink-faint); }
.chip:has(input:checked) { color: var(--ink); background: rgba(255,255,255,0.04); }

/* The swatch is now the toggle's state as well as its key: FILLED = on,
   HOLLOW = off. Fill against outline is a difference in value and shape, not
   hue, so it survives colour-blindness and a greyscale print alike — and the
   accent stripe still says which of the four this is. Consign keeps its dashed
   stripe in both states, exactly as the marker does in the grid. */
.swatch {
  --sw: var(--ink-faint);
  width: 20px; height: 13px; flex: 0 0 auto;
  border-left: 3px solid var(--sw);
  border-radius: 0 2px 2px 0;
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--rule);
}
.chip:has(input:checked) .swatch { background: var(--sw); box-shadow: none; }
.sw-start   { --sw: var(--blue); }
.sw-end     { --sw: var(--gold); }
.sw-consign { --sw: var(--rust); border-left-style: dashed; }
.sw-past    { --sw: var(--ink-dim); }

.count { font-size: 13px; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.c-short { display: none; }
.control-label { font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-faint); }

select {
  font-family: var(--sans);
  font-size: 13px;
  color: var(--ink);
  background: var(--panel-2);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  padding: 7px 8px;
  min-height: 34px;
}

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

.controls {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  align-items: center;
  justify-content: space-between;
  padding: 8px 0;
  margin-top: 0;
  background: var(--bg);
  border-bottom: 1px solid var(--rule);
}
.control-group { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }

/* the three that change how much detail the grid draws, kept together */
.marker-toggles { display: inline-flex; flex-wrap: wrap; gap: 8px; align-items: center; }

/* Show past changes the dataset, not the drawing — same row, its own side of a
   hairline, so it never reads as a fourth marker type. */
.ctrl-sep { width: 1px; align-self: stretch; min-height: 26px; background: var(--rule); }

/* --- filter panel ------------------------------------------------------- */

.panel {
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--panel);
  margin-top: 14px;
}
.panel-head {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  padding: 12px 14px;
  border-bottom: 1px solid var(--rule);
}
.panel-head h2 { margin: 0; font-size: 18px; flex: 0 0 auto; }
.panel-done { margin-left: auto; }

/* the counts live here now, not in the browsing bar */
.panel-counts {
  margin: 0; padding: 10px 14px; font-size: 13px; color: var(--ink-dim);
  font-variant-numeric: tabular-nums; border-bottom: 1px solid var(--rule);
}

/* set-once display preferences, together and out of the browsing chrome */
.panel-prefs {
  display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center;
  padding: 10px 14px; border-bottom: 1px solid var(--rule);
}

/* Reset sits hard left, the saved-view pair hard right: with one button in the
   slot it reads as the far end of the row, with two they group together, and
   the two states differ by a button rather than by a re-layout. */
.panel-views {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  padding: 10px 14px; border-bottom: 1px solid var(--rule);
}
.view-slot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-left: auto; }
#view-msg { flex: 1 1 100%; }
#view-msg:empty { display: none; }        /* no empty line while it has nothing to say */

.panel-bulk {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  padding: 10px 14px;
  border-bottom: 1px solid var(--rule);
}

/* A label and the control it names travel together. Loose in a wrapping flex
   row they wrapped separately, so at phone widths "Dates" ended the Organise
   line and "Groups" ended the Everything line — each naming the thing to its
   left instead of the thing it belongs to. */
.pref-item, .bulk-item {
  display: flex; align-items: center; gap: 8px;
  flex: 0 1 auto; min-width: 0;
}
.bulk-btns { display: flex; gap: 8px; flex-wrap: wrap; }
.bulk-sep { width: 1px; height: 20px; background: var(--rule); margin: 0 4px; }
.group-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.gchip {
  font-size: 12px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-dim);
  padding: 5px 11px;
  cursor: pointer;
  min-height: 30px;
}
.gchip[data-state="all"]  { color: #16130b; background: var(--gold); border-color: var(--gold); }
.gchip[data-state="some"] { color: var(--gold); border-color: var(--gold); }
.gchip:hover { border-color: rgba(255,255,255,0.35); }

/* The search leads the panel and spans it, with a magnifier drawn in. Nothing
   has to tell you it is there — it is the first and widest thing in the panel.
   The icon is an inline data URI: no request, no build step. */
.panel-search { padding: 10px 14px; border-bottom: 1px solid var(--rule); }

.tree-search {
  font-family: var(--sans); font-size: 14px; color: var(--ink);
  background: var(--panel-2) no-repeat 11px 50%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%238a867e' stroke-width='1.6'%3E%3Ccircle cx='7' cy='7' r='4.6'/%3E%3Cpath d='M10.4 10.4 14 14' stroke-linecap='round'/%3E%3C/svg%3E");
  background-size: 16px 16px;
  border: 1px solid var(--rule); border-radius: var(--r);
  padding: 8px 10px 8px 34px; min-height: 38px; width: 100%;
}
.tree-search::placeholder { color: var(--ink-faint); }
.tree-search:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }

.tree { padding: 6px 14px 14px; column-gap: 28px; }
/* NB: multi-column must not be combined with a scroll container — it overflows
   sideways. The scrolling panel below 900px and columns above it never meet. */
@media (min-width: 900px) { .tree { columns: 2; } }

.tree-house, .tree-group { break-inside: avoid; padding: 10px 0; border-bottom: 1px solid var(--rule-soft); }
.tree-group > .tree-house { border-bottom: 0; padding-bottom: 0; }

.tree-group-head {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--serif); font-size: 16px; padding-bottom: 4px;
}
.tree-group-head .count { margin-left: auto; }

.house-row { display: flex; align-items: center; gap: 9px; }
.house-row label { display: flex; align-items: center; gap: 9px; cursor: pointer; font-weight: 600; font-size: 14px; }
.house-row input { accent-color: var(--gold); width: 16px; height: 16px; }
.badge {
  font-size: 11px; color: var(--ink-faint); border: 1px solid var(--rule);
  border-radius: 999px; padding: 1px 7px; font-variant-numeric: tabular-nums;
}
.badge.is-off { color: var(--rust); border-color: rgba(197,138,99,0.5); }
.badge-toggle {
  font-family: var(--sans); background: transparent; cursor: pointer;
  min-height: 26px; padding: 2px 8px; line-height: 1.3;
}
.badge-toggle::after { content: " \25BE"; }
.badge-toggle[aria-expanded="false"]::after { content: " \25B8"; }
.badge-toggle:hover { color: var(--ink); border-color: rgba(255,255,255,0.35); }
.badge-toggle:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }

/* click-out to the house's own site, outside the checkbox label */
.house-link {
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; line-height: 1; text-decoration: none;
  color: var(--ink-faint); border: 1px solid var(--rule-soft); border-radius: var(--r);
  padding: 0 6px; min-width: 26px; min-height: 26px; flex: 0 0 auto;
}
.house-link:hover { color: var(--gold); border-color: var(--rule); background: var(--panel-2); }
.house-link:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }

.house-row .count { margin-left: auto; }
.house-sub { font-size: 13px; color: var(--ink-dim); display: flex; align-items: center; gap: 8px; }

.types { margin: 4px 0 0 26px; display: flex; flex-direction: column; }
.type-row {
  display: flex; align-items: center; gap: 9px;
  padding: 5px 0; font-size: 13.5px; color: var(--ink-dim); cursor: pointer;
  min-height: 30px;
}
.type-row:hover { color: var(--ink); }
.type-row input { accent-color: var(--gold); width: 15px; height: 15px; }
.type-row.is-blocked { opacity: 0.42; cursor: not-allowed; }
.type-row .count { margin-left: auto; }
.gtag {
  font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-faint); border: 1px solid var(--rule-soft); border-radius: 999px; padding: 0 6px;
}

/* --- panel foot: what's new, and moving settings between devices --------- */

/* Both are subordinate to the tree above them: a rule, smaller type, and the
   transfer block folded away until asked for. Neither is a headline. */
.panel-foot { border-top: 1px solid var(--rule); padding: 10px 14px 14px; }

.new-types {
  border: 1px solid var(--gold);
  background: rgba(216,183,106,0.07);
  border-radius: var(--r);
  padding: 9px 11px;
  margin: 0 0 10px;
}
.new-types-head { display: flex; align-items: center; gap: 10px; }
.new-types-head h3 { margin: 0; font-size: 14px; flex: 1 1 auto; min-width: 0; }
.new-types-head .btn-quiet { flex: 0 0 auto; min-height: 28px; padding: 3px 8px; }
.new-types-list { list-style: none; margin: 7px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.new-type-btn {
  display: flex; align-items: baseline; gap: 8px; width: 100%;
  font: inherit; font-size: 13px; text-align: left;
  background: transparent; border: 1px solid var(--rule-soft); border-radius: var(--r);
  color: var(--ink-dim); padding: 5px 8px; cursor: pointer; min-height: 30px;
}
.new-type-btn:hover { color: var(--ink); border-color: var(--rule); background: var(--panel-2); }
.new-type-btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.new-type-name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
/* "hidden" is the whole point of the notice, so it is a word and not a shade */
.new-type-where { flex: 0 0 auto; font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); }
.new-type-where.is-hidden { color: var(--gold); }

.transfer { font-size: 13px; }
.transfer > summary {
  cursor: pointer; color: var(--ink-dim); padding: 4px 0; min-height: 28px;
  list-style-position: outside;
}
.transfer > summary:hover { color: var(--ink); }
.transfer > summary:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.transfer-body { display: flex; flex-direction: column; gap: 8px; padding: 8px 0 0; }

/* The instructional copy. --ink-dim on --panel is 7.53:1, so this reads as a
   quiet note without dropping under AA. Deliberately not flex-laid-out: an <ol>
   inside a flex container is one browser quirk away from losing its numbers,
   and the numbers ARE the instruction. */
.transfer-copy { margin: 0; color: var(--ink-dim); font-size: 12.5px; line-height: 1.55; }
.transfer-steps {
  list-style: decimal outside;
  margin: 0; padding: 0 0 0 20px;
  color: var(--ink-dim); font-size: 12.5px; line-height: 1.55;
}
.transfer-steps li { margin: 0 0 4px; }
.transfer-steps li:last-child { margin-bottom: 0; }
.transfer-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.transfer-text {
  font-family: var(--mono); font-size: 12px; color: var(--ink);
  background: var(--panel-2); border: 1px solid var(--rule); border-radius: var(--r);
  padding: 7px 9px; width: 100%; resize: vertical;
}
.transfer-text::placeholder { color: var(--ink-faint); }
.transfer-text:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.transfer-text[hidden] { display: none; }
.transfer-msg { font-size: 12.5px; color: var(--ink-dim); min-width: 0; overflow-wrap: anywhere; }
.transfer-msg.is-bad { color: var(--rust); }
.transfer-msg.is-good { color: var(--gold); }

/* --- calendar ----------------------------------------------------------- */

/* the month navigation lives in the controls row now — see .controls */
.cal-nav { display: flex; align-items: center; gap: 6px; }
.cal-nav h2 { margin: 0; font-size: 19px; min-width: 148px; text-align: center; }

/* the grid is the first thing in its section now, so the section's own
   margin-top is the only gap above it */
.calendar-section { margin-top: 18px; }

.cal-dow {
  display: grid; grid-template-columns: repeat(7, 1fr);
  font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-faint); border-bottom: 1px solid var(--rule);
}
.cal-dow span { padding: 5px 6px; }

.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  border-left: 1px solid var(--rule-soft);
  border-top: 1px solid var(--rule-soft);
}

.day {
  border-right: 1px solid var(--rule-soft);
  border-bottom: 1px solid var(--rule-soft);
  min-height: 104px;
  padding: 4px 5px 6px;
  background: transparent;
  text-align: left;
  font: inherit;
  color: inherit;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
/* adjacent-month cells carry real events — dimmed, like Google and Outlook,
   but never blank and never unclickable */
.day.is-out { background-color: rgba(255,255,255,0.018); }
.day.is-out .day-num { color: var(--ink-faint); }
.day.is-out .marker { opacity: 0.78; }

/* a day that has already been — hatched, so "past" reads without colour */
.day.is-past {
  background-image: repeating-linear-gradient(-45deg,
    rgba(255,255,255,0.032) 0 1px, transparent 1px 7px);
}
.day.is-past .day-num { color: var(--ink-faint); }

.day.has-events { cursor: pointer; }
.day.has-events:hover { background-color: rgba(255,255,255,0.045); }
.day:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }

.day-num { font-size: 12px; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.day.is-today .day-num {
  color: #16130b; background: var(--today); border-radius: 999px;
  padding: 0 6px; font-weight: 700; align-self: flex-start;
}
.day.is-today { box-shadow: inset 0 0 0 1px var(--gold); }

.marker {
  --accent: var(--ink-faint);
  font-size: 11.5px;
  line-height: 1.25;
  padding: 2px 4px 2px 5px;
  border-left: 3px solid var(--accent);
  background: rgba(255,255,255,0.05);
  border-radius: 0 2px 2px 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.marker-end     { --accent: var(--gold); color: var(--ink); font-weight: 600; background: rgba(216,183,106,0.13); }
.marker-start   { --accent: var(--blue); color: var(--ink-dim); background: rgba(126,168,189,0.09); }
.marker-consign { --accent: var(--rust); color: var(--ink-dim); background: transparent; border-left-style: dashed; font-style: italic; }
.m-kind { color: var(--ink-faint); font-style: normal; font-weight: 400; }

/* a marker whose own date has passed: colour drained, fill removed. Only ever
   seen with "Show past" ticked — by default these are not drawn at all. */
.marker.is-past {
  --accent: var(--ink-past);
  color: var(--ink-faint);
  background: transparent;
  font-weight: 400;
  font-style: normal;
  border-left-style: solid;
}

.day-more {
  font-size: 11px; color: var(--gold); text-align: left; margin-top: auto;
  background: none; border: 0; padding: 2px 0 0; cursor: pointer; font-family: var(--sans);
}

/* No legend under the grid: the toggle swatches above it are the key. */
.cal-empty { color: var(--ink-faint); font-size: 13px; padding: 10px 2px 0; margin: 0; }

/* --- calendar: list view ------------------------------------------------ */

/* The same month, arranged down the page instead of across it. A day gets the
   full width, so an auction can be named rather than reduced to a colour bar —
   which is the one thing the 7-column grid cannot do at 390px. Days with
   nothing on them are not printed: a list of empty rows is not a calendar. */
.cal-list { display: block; }
.cal-list[hidden], .cal-grid[hidden], .cal-dow[hidden] { display: none; }

.list-day { border-bottom: 1px solid var(--rule-soft); padding: 8px 0 10px; }
.list-day.is-past { opacity: 0.72; }
.list-date {
  margin: 0 0 6px;
  font-family: var(--sans);
  font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-faint);
  display: flex; align-items: baseline; gap: 8px;
}
.list-day.is-today .list-date { color: var(--today); }
.list-today {
  font-size: 10px; letter-spacing: 0.08em;
  color: #16130b; background: var(--today); border-radius: 999px; padding: 0 7px;
}

.list-item {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding: 7px 8px 7px 9px;
  margin-top: 4px;
  border-left: 3px solid var(--accent, var(--ink-faint));
  border-radius: 0 2px 2px 0;
  background: rgba(255,255,255,0.05);
  color: inherit; text-decoration: none;
}
.list-item.kind-end     { --accent: var(--gold); font-weight: 600; background: rgba(216,183,106,0.13); }
.list-item.kind-start   { --accent: var(--blue); background: rgba(126,168,189,0.09); }
.list-item.kind-consign { --accent: var(--rust); background: transparent; border-left-style: dashed; }
.list-item.is-past {
  --accent: var(--ink-past);
  background: transparent; font-weight: 400; border-left-style: solid; color: var(--ink-dim);
}
.list-name { min-width: 0; overflow-wrap: anywhere; }
.list-item.is-link { cursor: pointer; }
.list-item.is-link:hover { background: rgba(255,255,255,0.09); color: var(--gold); }
.list-item.is-link:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }

/* --- collapsible sections ----------------------------------------------- */

/* A strip above both table sections, carrying the second Settings entry point.
   It is a sibling of the sections rather than a child, so collapsing both of
   them leaves it exactly where it was — Settings stays reachable down here. */
/* It is a bar, not a floating icon. Before, the button hung on its own above
   the Scheduled Auctions heading with nothing around it and read as a stray. A ground, a
   rule and a label on the left give it somewhere to be, and say what it opens.
   Sort deliberately does NOT live here: this strip sits above both tables and
   sort applies to one of them. */
.table-strip {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px;
  margin-top: 26px;
  padding: 4px 10px;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--r);
}
.strip-label {
  font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-faint); min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap;
}
.table-strip ~ .table-section { margin-top: 16px; }
.filters-slot:empty { display: none; }

.table-head {
  display: flex; flex-wrap: wrap; gap: 10px 16px;
  align-items: center; justify-content: space-between;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 10px;
}
.table-head h2 { margin: 0; font-size: 20px; }
.section-h { flex: 1 1 300px; min-width: 0; }

/* a bordered bar, so it reads as a control without needing a hover */
.section-toggle {
  display: flex; align-items: center; gap: 10px;
  width: 100%; min-height: 42px;
  font: inherit; color: inherit; text-align: left;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  padding: 6px 12px;
  cursor: pointer;
}
.section-toggle:hover { background: var(--panel-2); border-color: rgba(255,255,255,0.32); }
.section-toggle:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.section-name { flex: 0 1 auto; }
.section-toggle .count { margin-left: auto; text-align: right; }
.chev {
  flex: 0 0 auto; width: 0; height: 0;
  border-left: 6px solid transparent; border-right: 6px solid transparent;
  border-top: 7px solid var(--gold);
}
.section-toggle[aria-expanded="false"] .chev { transform: rotate(-90deg); }

/* The Sort dropdown is a phone-only control: everywhere else the column heads
   are the sort, and two ways to do one thing is one too many. display:none
   also takes it out of the tab order, which is the point. */
.table-tools { display: none; }
@media (max-width: 640px) { .table-tools { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; } }

/* Width-dependent button wording. Exactly one is ever displayed, so the
   accessible name is whatever is on screen — no aria-label needed, and voice
   control matches what the user can actually read. */
.lbl-narrow { display: none; }
@media (max-width: 640px) {
  .lbl-wide   { display: none; }
  .lbl-narrow { display: inline; }
}
.sort-label { font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-faint); }

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

.data-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.data-table th {
  text-align: left;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 500;
  border-bottom: 1px solid var(--rule);
  padding: 0;
}
/* --stick is the live height of the sticky controls bar, set by app.js, so the
   table head parks directly beneath it at any width or zoom */
.data-table thead th { position: sticky; top: var(--stick, 54px); background: var(--bg); z-index: 10; }
.th-sort {
  font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit;
  background: none; border: 0; padding: 9px 10px 9px 0; cursor: pointer; width: 100%; text-align: left;
}
.th-sort:hover { color: var(--ink); }
.th-sort:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }
/* the sorted column is the only sort control now, so it says so loudly: gold,
   bold, an arrow for the direction, and a rule under the head it belongs to */
th[aria-sort] .th-sort { color: var(--gold); font-weight: 700; }
th[aria-sort] { box-shadow: inset 0 -2px 0 var(--gold); }
th[aria-sort="ascending"] .th-sort::after { content: " \2191"; }
th[aria-sort="descending"] .th-sort::after { content: " \2193"; }

.data-table td { padding: 9px 10px 9px 0; border-bottom: 1px solid var(--rule-soft); vertical-align: top; }
.data-table tbody tr:hover { background: rgba(255,255,255,0.035); }
.data-table tbody tr.is-past { color: var(--ink-faint); }
.data-table tbody tr.is-past .cell-house { color: var(--ink-faint); }
.data-table tbody tr.is-past .row-link { color: var(--ink-faint); }

/* the whole row is a link to that auction */
.data-table tbody tr.is-clickable { cursor: pointer; }
.row-link { text-decoration: none; color: inherit; }
.data-table tbody tr.is-clickable:hover .row-link { color: var(--gold); text-decoration: underline; }
.row-link:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

.cell-house { font-weight: 600; }
/* both halves of a date cell ride together — the cell never breaks between
   "Aug 19, 2026" and "in 3d", so every row in a table is the same height */
.col-start, .col-end, .col-consign { white-space: nowrap; }
.cell-date { font-variant-numeric: tabular-nums; white-space: nowrap; }
.col-end .cell-date { color: var(--gold); }
.rel { color: var(--ink-faint); font-size: 12px; margin-left: 6px; white-space: nowrap; }
.rel.is-soon { color: var(--gold); }
.dash { color: var(--ink-faint); }

/* A consign deadline that has gone by. Two facts collapse to one appearance:
   the source printed "Past", OR the date itself is now behind us — auctionreport
   is slow to update that column, so 23 live rows carry a lapsed date. Struck
   through and tagged, never just a dimmer grey (that would fail contrast). */
.consign-gone { color: var(--ink-faint); font-style: italic; }
.consign-gone .cell-date {
  color: var(--ink-faint);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}
.gone-tag {
  font-style: normal; font-size: 10.5px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ink-faint);
  border: 1px solid var(--rule-soft); border-radius: 999px;
  padding: 0 6px; margin-left: 6px; white-space: nowrap;
}

.col-hide { width: 1%; white-space: nowrap; }
.btn-hide {
  font-size: 12px; padding: 4px 10px; min-height: 28px;
  background: transparent; color: var(--ink-dim);
}
.btn-hide:hover { color: var(--ink); background: var(--panel-2); border-color: rgba(255,255,255,0.35); }

/* Consign is a desktop-only column — see the >= 900px block. It is a
   responsive rule, never a user-facing toggle. */
.data-table .col-consign { display: none; }

.empty-note { color: var(--ink-faint); font-size: 13.5px; padding: 14px 0; margin: 0; }

.site-foot {
  max-width: var(--wrap); margin: 0 auto; padding: 18px 16px 40px;
  border-top: 1px solid var(--rule-soft); color: var(--ink-faint); font-size: 12px;
}
.site-foot p { margin: 0; }

/* --- undo strip --------------------------------------------------------- */

.undo-wrap {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
  display: flex; justify-content: center;
  padding: 0 12px 14px; pointer-events: none;
}
.undo-strip {
  pointer-events: auto;
  display: flex; align-items: center; gap: 10px;
  background: var(--panel-2);
  border: 1px solid var(--rule); border-left: 3px solid var(--gold);
  border-radius: var(--r);
  padding: 8px 10px 8px 13px;
  box-shadow: 0 12px 34px rgba(0,0,0,0.7);
  font-size: 13.5px; max-width: 100%;
}
.btn-undo { background: transparent; color: var(--gold); border-color: rgba(216,183,106,0.55); font-weight: 600; }
.btn-undo:hover { background: rgba(216,183,106,0.12); }
.undo-x { min-height: 28px; padding: 2px 8px; font-size: 16px; line-height: 1; }

/* --- day sheet ---------------------------------------------------------- */

.sheet-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.72); z-index: 90; }
.sheet {
  position: fixed;
  z-index: 100;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(560px, calc(100vw - 24px));
  max-height: min(78vh, 720px);
  overflow: auto;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  box-shadow: 0 24px 60px rgba(0,0,0,0.75);
}
.sheet-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 14px; border-bottom: 1px solid var(--rule);
  position: sticky; top: 0; background: var(--panel);
}
.sheet-head h2 { margin: 0; font-size: 19px; }
.sheet-body { padding: 6px 14px 16px; }

/* The whole entry is the link, not just the name: three lines of target instead
   of one word of it, which on a phone is the difference between tapping and
   aiming. Nothing else inside an entry is interactive, so there is nothing to
   nest. */
.sheet-item { padding: 11px 10px 11px 10px; border-bottom: 1px solid var(--rule-soft); border-left: 3px solid var(--ink-faint); margin-top: 8px; }
.sheet-item.kind-end { border-left-color: var(--gold); }
.sheet-item.kind-start { border-left-color: var(--blue); }
.sheet-item.kind-consign { border-left-color: var(--rust); border-left-style: dashed; }
.sheet-item.is-past { border-left-color: var(--ink-past); }
.sheet-item h3 { margin: 0; font-size: 15.5px; font-family: var(--sans); font-weight: 600; min-width: 0; }
/* line one: the name and the kind, side by side */
.sheet-name { display: flex; align-items: baseline; gap: 10px; justify-content: space-between; }

.sheet-item.is-link {
  display: block; color: inherit; text-decoration: none; cursor: pointer;
  border-radius: 0 var(--r) var(--r) 0;
}
.sheet-item.is-link:hover { background: rgba(255,255,255,0.045); }
.sheet-item.is-link:hover h3 { color: var(--gold); text-decoration: underline; }
.sheet-item.is-link:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }
/* the kind, as a pill — the same treatment as the "passed" tag, which is the
   one the user liked. It is a word, not a colour: Closes and Starts on the
   same day have to be tellable apart without seeing hue. */
.kind-pill {
  flex: 0 0 auto;
  font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-dim);
  border: 1px solid var(--rule); border-radius: 999px;
  padding: 1px 8px; white-space: nowrap;
}
.kind-pill.is-past { color: var(--ink-faint); border-color: var(--rule-soft); }
.sheet-dates { font-size: 13px; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.sheet-consign { color: var(--ink-faint); }

/* --- tight table band: 641px .. 1023px ---------------------------------- */
/* The band the user browses in at 175% zoom. Relative dates go — their length
   varies per row ("in 3d" vs "yesterday"), which is what made the column
   ragged — and padding tightens so the columns fit without wrapping. */

@media (min-width: 641px) and (max-width: 1023px) {
  .data-table { font-size: 13.5px; }
  .data-table td, .th-sort { padding-right: 8px; }
  .rel { display: none; }
  .cal-nav h2 { min-width: 150px; }
}

/* --- desktop: the consign column and the two-up filter tree ------------- */

@media (min-width: 900px) {
  .data-table .col-consign { display: table-cell; }
}

/* --- filter panel scrolls internally below the two-column breakpoint ----- */

/* Below the two-column breakpoint the whole panel is capped to the screen and
   the tree is the part that scrolls — head, counts, preferences, search and
   bulk actions stay pinned. The tree flexes into whatever is left over, so
   adding another row above it can never push the panel past the viewport
   again. --stick is the live height of the sticky bar the panel sits under.
   NB: multi-column and a scroll container must never meet (they overflow
   sideways) — columns are >= 900px, this is <= 899px. */
@media (max-width: 899px) {
  #filters {
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - var(--stick, 60px) - 28px);
  }
  #filters > *:not(#filter-tree) { flex: 0 0 auto; }
  #filter-tree {
    flex: 1 1 auto;
    min-height: 140px;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}

/* --- phone -------------------------------------------------------------- */

@media (max-width: 640px) {
  body { font-size: 15px; }
  .masthead-bar { padding: 7px 12px 9px; gap: 4px 10px; }
  .masthead-bar h1 { font-size: 19px; }
  main { padding: 0 12px 40px; }

  /* Not sticky here. Pinned, this bar held about four rows of a 844px screen
     for the whole scroll — a third of the phone permanently spent on controls
     the visitor set once. It scrolls away with everything else; measureStick()
     reads the computed position and publishes --stick: 0 to match. */
  .controls { position: static; gap: 6px; padding: 7px 0; }
  .controls .control-label { display: none; }

  /* gear + dot: the word and the count are what the aria-label is for here */
  .settings-btn .btn-word { display: none; }
  .settings-btn { gap: 6px; padding: 6px; }
  .badge-count { display: none; }
  .badge-dot { display: inline-block; }
  .c-long { display: none; }
  .c-short { display: inline; }

  /* Four toggles, 2x2. In one row they overflowed and "Show past" — the longest
     label — was pushed onto a line of its own, right-aligned, looking like a
     mistake. Two even rows of two: Starts/Closes, then Consign/Show past.
     display:contents lets the three markers and Show past share one grid while
     the marker group keeps its role and its label. */
  .controls .control-group:not(.control-group-right) {
    display: grid;
    grid-template-columns: 1fr 1fr;
    width: 100%;
    gap: 6px;
  }
  .marker-toggles { display: contents; }
  .ctrl-sep { display: none; }
  .chip-past { margin-left: 0; }
  .controls .chip { padding: 6px 9px; gap: 8px; font-size: 13px; min-width: 0; }
  .controls .chip-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* the calendar's own row: view first, then where you are in it */
  .control-group-right { width: 100%; flex-wrap: wrap; gap: 6px; }
  .cal-nav { flex: 1 1 100%; min-width: 0; order: -1; }
  .cal-nav h2 { font-size: 17px; min-width: 0; flex: 1; }
  .view-toggle { flex: 0 0 auto; }
  #month-jump { flex: 1 1 auto; min-width: 0; }
  #today-btn { flex: 0 0 auto; }

  .table-strip { margin-top: 18px; }

  .cal-dow { font-size: 10px; letter-spacing: 0.04em; }
  .cal-dow span { padding: 4px 2px; }

  .day { min-height: 62px; padding: 3px 3px 4px; gap: 2px; }
  .day-num { font-size: 11px; }
  /* labels can't fit in a 50px column — markers collapse to colour bars and
     the day opens the full list on tap */
  .marker { height: 6px; padding: 0; border-left: 0; border-radius: 2px; background: var(--accent); font-size: 0; }
  .marker-consign { background: transparent; box-shadow: inset 0 0 0 1px var(--rust); }
  .marker.is-past { background: var(--ink-past); box-shadow: none; }
  .day-more { font-size: 10px; }

  /* the swatches track the markers into their phone form — solid colour bars —
     while keeping the one rule that matters: filled means on, hollow means off */
  .swatch { border-left: 0; border-radius: 2px; height: 9px; width: 22px; }
  .sw-consign { border-left: 0; }

  /* the header is decorative here — hiding it also takes its four sort
     buttons out of the tab order, where they were focusable off-screen */
  .data-table thead { display: none; }

  /* Tables become stacked cards — no horizontal scrolling one-handed — and each
     card is TWO lines, not six:

         Collector Investor Auctions              [Hide]
         Main · 08.06.26 → 08.16.26 · today

     The labels are gone. HOUSE above a house name earned nothing, and neither
     did TYPE / START / CLOSE; the shape of a date says what it is. At 67
     upcoming that is ~400 lines of scrolling turned into ~140. The row is a
     flex box with a full-width pseudo-element forcing the break, so line one is
     name + Hide and line two is everything else, in reading order. */
  .data-table, .data-table tbody, .data-table td { display: block; }
  .data-table tr {
    display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 6px;
    border: 1px solid var(--rule); border-left-width: 3px; border-left-color: var(--gold);
    border-radius: var(--r); padding: 7px 10px; margin-bottom: 6px;
  }
  .data-table tr.is-past { border-left-color: var(--ink-past); }
  .data-table td { border: 0; padding: 0; min-width: 0; }
  .data-table td::before { content: none; }          /* the labels, gone */
  .data-table tr::after { content: ""; flex: 0 0 100%; height: 0; order: 3; }

  .data-table td.cell-house-td { order: 1; flex: 1 1 auto; font-size: 15.5px; overflow-wrap: anywhere; }
  .data-table td.col-hide { order: 2; flex: 0 0 auto; width: auto; padding: 0; }

  /* line two, in one voice: dimmer, smaller, separators drawn in rather than
     spelt out. The relative word sits immediately after the close date it
     describes — opposite it, "today" took the right edge and shoved the date
     into the middle of the card, the one row that did not align. */
  .data-table td.col-type,
  .data-table td.col-start,
  .data-table td.col-end,
  .data-table td.col-cadence { font-size: 13px; color: var(--ink-dim); }
  .data-table td.col-type { order: 4; }
  .data-table td.col-start { order: 5; }
  .data-table td.col-end { order: 6; }
  .data-table td.col-cadence { order: 5; }
  .data-table td.col-start::before,
  .data-table td.col-cadence::before { content: "· "; color: var(--ink-faint); }
  .data-table td.col-end::before { content: "→ "; color: var(--ink-faint); }
  .data-table td.col-end .rel { margin-left: 5px; }
  .data-table td.col-end .rel::before { content: "· "; color: var(--ink-faint); }

  /* consign costs a whole line per card and is reachable in the day sheet */
  .data-table .col-consign { display: none; }

  /* Sort rides the Scheduled Auctions heading row rather than dropping to a line of its
     own — it belongs to this table and it has to look like it. flex:1 1 300px
     on the heading was what forced the wrap at 390px; here the heading takes
     what is left and ellipsises, and the direction button sheds its word
     (the aria-label still states the direction in full). */
  .table-head { flex-wrap: nowrap; gap: 8px; }
  .section-h { flex: 1 1 auto; min-width: 0; }
  .section-toggle { padding: 6px 9px; gap: 7px; }
  .section-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .section-toggle .count { flex: 0 0 auto; white-space: nowrap; }
  .table-tools { flex: 0 0 auto; flex-wrap: nowrap; gap: 6px; }
  .sort-label {
    position: absolute; width: 1px; height: 1px; margin: -1px;
    padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  #sort-key { padding: 6px 4px; max-width: 108px; }
  #sort-dir { padding: 6px 7px; }
  #sort-dir .dir-word { display: none; }

  /* Settings is the screen, not a 687px card floating in an 844px one. Two
     nested scroll surfaces become one: the panel scrolls, the page behind it
     does not, and the head with the Done button stays put at the top. */
  /* A label sits ABOVE the control it names here, not beside it: at 390px a
     side-by-side pair leaves the control a stub of the width, and the group
     chips wrap into a block the label then floats against. Stacked, each label
     is unambiguously the heading of the thing under it. */
  .pref-item, .bulk-item {
    flex-direction: column; align-items: flex-start; gap: 5px; flex: 1 1 100%;
  }
  .panel-prefs, .panel-bulk { gap: 12px; }
  .panel-prefs .control-label, .panel-bulk .control-label { display: block; }
  .bulk-sep { display: none; }

  body.filters-open { overflow: hidden; }
  #filters {
    position: fixed; inset: 0; z-index: 95;
    display: block;
    width: auto; height: auto; max-height: none;
    margin: 0; border: 0; border-radius: 0;
    background: var(--panel);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  #filter-tree { flex: none; min-height: 0; overflow-y: visible; }
  .panel-head {
    position: sticky; top: 0; z-index: 2;
    background: var(--panel); flex-wrap: nowrap;
  }
  .panel-foot { padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px)); }

  .sheet { top: auto; bottom: 0; left: 0; transform: none; width: 100vw; max-height: 82vh; border-radius: var(--r) var(--r) 0 0; }
  .tree { padding-bottom: 10px; }

  .undo-wrap { padding: 0 8px 10px; }
  .undo-strip { width: 100%; }
}

@media (prefers-reduced-motion: no-preference) {
  .day, .btn, .chip { transition: background-color 90ms linear, border-color 90ms linear, color 90ms linear; }
  .chev { transition: transform 120ms ease; }
}
