@charset "utf-8";

/*
 * Folio — a Shopclass public theme.
 * Copyright (c) 2026 Navjot Tomer (Mindstellar) and contributors
 * SPDX-License-Identifier: GPL-3.0-or-later
 *
 * One stylesheet. No build step, no framework, no reset beyond what the cascade
 * needs — the markup is native HTML5 and most of it is already styled correctly
 * by the browser. What is here is a type scale, a rule colour, and a spine.
 *
 * Logical properties throughout: the site ships RTL locales and there is no
 * separate RTL sheet.
 */

/* ---------------------------------------------------------------- tokens -- */

:root {
  color-scheme: light dark;

  /* Ink navy carries the brand at region scale: the masthead band and the
     closing band are the colour, the reading surface is not. It is not a state
     colour, so it never collides with the success/danger/notice vocabulary. */
  --navy:        #1b2c5e;
  --navy-deep:   #142145;
  --navy-line:   #35477e;
  --navy-tint:   light-dark(#e6eaf5, #202a49);
  --navy-ink:    light-dark(#1b2c5e, #b9c6ec);

  --ground:      light-dark(#fbfbfa, #10131b);
  --surface:     light-dark(#ffffff, #171b26);
  --sunk:        light-dark(#f1f1ef, #1d2230);
  --wash:        light-dark(#f4f4f1, #191e2a);

  --ink:         light-dark(#14161c, #eceef4);
  --ink-soft:    light-dark(#3c414c, #c4cad6);
  --ink-muted:   light-dark(#585c66, #a7adbb);
  /* The navy band and colophon stay navy in both schemes, so anything painted on
     them is fixed too: an adaptive token resolves to its dark value there and
     paints dark on dark. */
  --ink-onnavy:  #ffffff;
  --ink-onnavy-muted: #c3cbe2;
  --surface-onnavy: #e6eaf5;

  --rule:        light-dark(#e2e2df, #262c3a);
  --rule-mid:    light-dark(#cfcfcb, #333a4b);
  /* The border a form control is identified by, so it answers to the 3:1 the
     spec asks of a component boundary and not to the 4.5:1 asked of text. The
     dark value was #5c6474, which is 2.89:1 against a card. */
  --rule-strong: light-dark(#8a8d95, #6d7689);
  /* A filled navy button on a dark card is navy-on-near-navy: 1.28:1, so its
     edge -- the thing that says "this is a control" -- disappears. The fill and
     the label stay exactly as they are; only the edge is lit, which keeps the
     brand colour itself unchanged in both schemes.
     Mixed from --navy rather than written as a hex, so a site that sets its own
     brand colour gets an edge of that colour and not a blue one. */
  --navy-edge:   light-dark(var(--navy), color-mix(in srgb, var(--navy) 45%, white));

  --link:        light-dark(#1b2c5e, #9db2ee);
  --link-hover:  light-dark(#0f1a3c, #c3d0f5);

  --ok:          light-dark(#1d6b38, #7bd39a);
  --ok-tint:     light-dark(#e4f5e9, #16301f);
  --warn:        light-dark(#75560f, #e8c463);
  --warn-tint:   light-dark(#fbf1d5, #33290d);
  --stop:        light-dark(#9d2018, #f6a99f);
  --stop-tint:   light-dark(#fdeae7, #351713);

  --measure: 66ch;
  --plate: 8rem;
  --gutter: clamp(1.125rem, 4vw, 2.5rem);
  --spine: 1120px;

  --step--1: 0.8125rem;   /* labels and metadata only — never body text */
  --step-0:  1rem;
  --step-1:  1.0625rem;
  --step-2:  1.3125rem;
  --step-3:  1.625rem;
  --step-4:  2.0625rem;
  --step-5:  2.625rem;

  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  --radius: 3px;
  --tap: 2.75rem;
}

/* ------------------------------------------------------------- structure -- */

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

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

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: var(--step-0)/1.55 var(--sans);
  /* Grades the numerals in the index: prices and dates align in a column. */
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;
  min-block-size: 100vh;
}

/* An auto inline margin on a column flex item cancels the cross-axis stretch,
   so the width has to be asked for explicitly or every page shrink-wraps. */
body > * { inline-size: 100%; }

main { flex: 1 0 auto; min-inline-size: 0; padding-block: 2.5rem 3.5rem; }
main > * { min-inline-size: 0; }

.spine {
  inline-size: 100%;
  max-inline-size: var(--spine);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Everything that reads as prose is capped; tables and indexes are not. */
.prose { max-inline-size: var(--measure); }
.prose > :last-child { margin-block-end: 0; }

/* A bounded form block. Wide enough for a long translated label, narrow enough
   that a single column of fields still reads as one object. */
.sheet { max-inline-size: 30rem; }
.sheet-wide { max-inline-size: 42rem; }

/* ------------------------------------------------------------ typography -- */

/* overflow-wrap because headings carry user text -- a search term, a listing
   title, a member's name -- and one long token otherwise pushes the page
   wider than the viewport. */
h1, h2, h3 {
  line-height: 1.18; text-wrap: balance; margin: 0 0 .5em; font-weight: 620;
  overflow-wrap: anywhere;
}
h1 { font-size: var(--step-4); letter-spacing: -.021em; }
h2 { font-size: var(--step-2); letter-spacing: -.012em; }
h3 { font-size: var(--step-1); letter-spacing: -.006em; }
p  { margin: 0 0 1em; text-wrap: pretty; }
* + h2 { margin-block-start: 2.5rem; }
* + .ruled { margin-block-start: 2.5rem; }
* + h3 { margin-block-start: 1.75rem; }

a { color: var(--link); text-underline-offset: .18em; text-decoration-thickness: from-font; }
a:hover { color: var(--link-hover); }

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--link);
  outline-offset: 2px;
  border-radius: var(--radius);
}
/* The page focus ring is navy; on the navy bands it would be invisible. */
.masthead :focus-visible,
.colophon :focus-visible { outline-color: #fff; }

.muted { color: var(--ink-muted); }
.small { font-size: var(--step--1); }

/* The catalogue's class mark: what shelf a record sits on. Set in the label
   size because that is what it is, and tracked out so it never reads as a
   heading competing with the title under it. */
.classmark {
  margin: 0 0 .2rem;
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.classmark a { color: inherit; text-decoration: none; }
.classmark a:hover { color: var(--ink); text-decoration: underline; }

/* A rule that closes a section heading, the way a catalogue closes a column
   head. Draws under the whole block, not under the words. */
.ruled {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .35rem 1.25rem;
  padding-block-end: .75rem;
  border-block-end: 2px solid var(--ink);
  margin-block-end: 0;
}
.ruled > :is(h1, h2, h3) { margin-block-end: 0; }
.ruled > .push { margin-inline-start: auto; }
/* The count is the one fact that orients a visitor in a result set, so it is set
   at reading size beside the heading rather than as muted trim under it. */
.tally { margin: 0; color: var(--ink-muted); font-weight: 560; white-space: nowrap; }

/* What is currently applied to the set, and the way to change it. Sits directly
   under the ruled head because that is where the eye already is after a reload,
   and because on a narrow screen the facet column it summarises is three screens
   further down. */
.applied {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .25rem 1rem;
  margin: .75rem 0 0;
  font-size: var(--step--1);
}
.applied-terms { color: var(--ink-soft); font-weight: 600; }
.applied-more { margin-inline-start: auto; font-weight: 560; }

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

.masthead {
  background: var(--navy);
  color: var(--ink-onnavy);
}
.masthead a { color: var(--ink-onnavy); }

.masthead-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem 1.75rem;
  padding-block: 1.125rem;
}
.masthead-band { border-block-start: 1px solid var(--navy-line); }

.wordmark {
  font-size: var(--step-2);
  font-weight: 640;
  letter-spacing: -.024em;
  text-decoration: none;
  margin-inline-end: auto;
  padding-block: .125rem;
}
.wordmark:hover { color: #fff; text-decoration: underline; text-underline-offset: .22em; }
/* Sized by height, not by the file's own pixels: a site-supplied logo can be any
   aspect ratio, so only the band height it has to fit is fixed. */
.wordmark img { display: block; block-size: 2rem; inline-size: auto; max-inline-size: 12rem; }

.masthead nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .35rem 1.5rem;
  font-size: 0.9375rem;
}
.masthead nav a {
  text-decoration: none;
  color: var(--ink-onnavy-muted);
  padding-block: .375rem;
}
.masthead nav a:hover { color: #fff; text-decoration: underline; text-underline-offset: .25em; }
.masthead nav a[aria-current] {
  color: #fff;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: .3em;
}

/* The publish action sits in the nav but is not a nav item: it is the one thing
   a seller came to do, so it takes an outline and holds it. */
.nav-action {
  border: 1px solid var(--navy-line);
  border-radius: var(--radius);
  padding: .375rem .875rem;
  color: #fff;
  background: color-mix(in srgb, #fff 8%, transparent);
}
.masthead nav a.nav-action { color: #fff; }
.masthead nav a.nav-action:hover { background: #fff; color: var(--navy); text-decoration: none; }

.colophon {
  background: var(--navy-deep);
  color: var(--ink-onnavy-muted);
  margin-block-start: auto;
  padding-block: 2.5rem 2rem;
  font-size: 0.9375rem;
}
.colophon a { color: var(--ink-onnavy-muted); text-decoration-color: var(--navy-line); }
.colophon a:hover { color: #fff; text-decoration-color: currentColor; }
.colophon h2 {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: #97a4c9;
  margin: 0 0 .875rem;
}
.colophon-grid {
  display: grid;
  gap: 2rem;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  padding-block-end: 1.75rem;
  border-block-end: 1px solid var(--navy-line);
}
.colophon-mark { font-size: var(--step-1); font-weight: 620; color: #fff; margin: 0 0 .4rem; letter-spacing: -.015em; }
.colophon nav { display: grid; gap: .5rem; justify-items: start; }
.colophon-note { margin: 0 0 .75rem; font-size: var(--step--1); }
.colophon-foot { display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; padding-block-start: 1.25rem; font-size: var(--step--1); }

/* Language switcher. Plain links, so it works before any script does; the
   current language is marked rather than removed, to keep the row from
   reflowing as it changes. */
/* Outranks `.colophon nav`, which lays the footer's link columns out as a grid. */
.colophon .colophon-langs { display: flex; flex-wrap: wrap; gap: .25rem 1rem; margin-inline-start: auto; flex: none; }
.colophon .colophon-langs a[aria-current] { color: var(--ink-onnavy); text-decoration: none; cursor: default; }

/* Widget zones. The markup inside is written in the admin, not here, so each
   zone only sets its own spacing and hands back neutral flow — the masthead's
   nav geometry must not reach into a widget that happens to contain a <nav>. */
.masthead-widgets { border-block-start: 1px solid var(--navy-line); padding-block: 1rem; }
.masthead-widgets nav { display: block; gap: 0; font-size: inherit; }
.masthead-widgets nav a { padding-block: 0; }
.colophon-widgets { padding-block-start: 1.5rem; }

/* ------------------------------------------------------------ tap targets -- */
/* A thumb is not a cursor. On a narrow screen the primary navigation takes the
   full --tap height the form controls already take; the denser link lists -- the
   colophon, the breadcrumb, the pager, the seller record -- grow their hit area
   with padding that is cancelled by an equal negative margin, so the target gets
   bigger and nothing on the page moves. */
@media (max-width: 40rem) {
  .masthead nav a { display: inline-flex; align-items: center; min-block-size: var(--tap); padding-block: 0; }
  .masthead .nav-action { min-block-size: var(--tap); }
  .wordmark { display: inline-flex; align-items: center; min-block-size: var(--tap); }
  .wordmark img { block-size: 1.75rem; }

  .colophon nav a { padding-block: .4375rem; margin-block: -.4375rem; }
  .colophon .colophon-langs { gap: .25rem 1.25rem; }
  .colophon-langs a,
  .crumbs a,
  .pager a,
  .attrib dd a,
  .empty a,
  .applied-more,
  .ruled > .push { padding-block: .5rem; margin-block: -.5rem; display: inline-block; }

  .facets a { padding-block: .5625rem; }
}

.skip { position: absolute; inset-inline-start: -9999px; }
.skip:focus {
  inset-inline-start: var(--gutter);
  inset-block-start: .5rem;
  z-index: 10;
  background: var(--surface);
  color: var(--ink);
  padding: .625rem 1rem;
  border-radius: var(--radius);
}

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

label { display: block; font-size: var(--step--1); font-weight: 600; margin-block-end: .375rem; color: var(--ink-soft); }

input, select, textarea {
  font: inherit;
  inline-size: 100%;
  min-block-size: var(--tap);
  padding: .5rem .75rem;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
}
textarea { min-block-size: 6rem; line-height: 1.55; }
/* Width and tap height belong to text fields; these three size themselves. */
input:is([type="checkbox"], [type="radio"]) {
  inline-size: 1.125rem;
  block-size: 1.125rem;
  min-block-size: 0;
  padding: 0;
  accent-color: var(--navy);
  flex: none;
}
input[type="file"] { min-block-size: 0; padding: .5rem; }
input::placeholder, textarea::placeholder { color: var(--ink-muted); opacity: 1; }
input:focus, select:focus, textarea:focus { border-color: var(--navy); }

/* The browser already flags these; we only give the flag a colour. */
input:user-invalid, textarea:user-invalid, select:user-invalid { border-color: var(--stop); }
input:user-invalid + .hint { color: var(--stop); }

.field { margin-block-end: 1.125rem; }
.field-check {
  display: flex;
  gap: .625rem;
  align-items: center;
  font-size: var(--step-0);
  font-weight: 400;
  color: var(--ink);
  margin-block-end: 1.125rem;
}
/* Core stacks a currency select under the price input; they are one control. */
.field > :is(input, select) + :is(input, select) { margin-block-start: .5rem; }
.hint { display: block; font-size: var(--step--1); color: var(--ink-muted); margin-block-start: .375rem; }

.actions { display: flex; flex-wrap: wrap; gap: .875rem; align-items: center; margin-block-start: 1.5rem; }

/* -- the search control ----------------------------------------------------- */
/* Labels sit above their fields, so the row aligns on the bottom edge. */

.searchbar { display: flex; flex-wrap: wrap; gap: .625rem .75rem; align-items: end; }
.searchbar .field { margin: 0; }
.searchbar .field-wide { flex: 1 1 20rem; }
.searchbar .field-narrow { flex: 1 1 12rem; }
.searchbar input { min-block-size: 3rem; font-size: var(--step-1); }
.searchbar .btn { min-block-size: 3rem; padding-inline: 1.75rem; }

/* On the navy band the control is the one lit object in the region. */
.searchbar-band { padding-block: 1.375rem 1.75rem; }
.searchbar-band label { color: var(--ink-onnavy-muted); }
/* In light the field is white on navy and needs no edge — the fill is the whole
   signal. In dark the fill is --surface, which is darker than the band it sits
   on (1.28:1), so the field stops being an object at all; the edge carries it
   instead, in the same fixed on-navy ink the labels beside it use. */
.searchbar-band input { border-color: light-dark(transparent, var(--ink-onnavy-muted)); }
.searchbar-band input:focus { border-color: #fff; }
.searchbar-band .btn { background: #fff; color: var(--navy); border-color: #fff; }
.searchbar-band .btn:hover { background: var(--surface-onnavy); color: var(--navy); border-color: var(--surface-onnavy); }
.searchbar-band :focus-visible { outline-color: #fff; }

/* Core renders the comments block (.oe-comment*); its submit is a plain <button>,
   so it joins the theme's button rather than growing a second one. `.oe-btn` is
   the class core puts on its own buttons wherever it emits one -- including
   inside a theme view, like the save-this-search form in the facet column, as
   well as on the pages core renders whole. One class covers both: core ships its
   page stylesheet inside `@layer shopclass`, and an unlayered rule outranks a
   layered one whatever its specificity, so this does not have to know or mirror
   the selector core happened to write. Requires Shopclass 6.3.0; on an older
   core the account pages keep core's own button until it is updated. */
.btn,
.oe-btn,
.oe-comment-form button {
  font: inherit;
  font-weight: 560;
  inline-size: auto;
  min-block-size: var(--tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: .5rem 1.25rem;
  border: 1px solid var(--navy-edge);
  border-radius: var(--radius);
  background: var(--navy);
  color: #fff;
  text-decoration: none;
  cursor: pointer;
}
.btn:hover,
.oe-btn:hover,
.oe-comment-form button:hover { background: var(--navy-deep); border-color: var(--navy-edge); color: #fff; }
.btn-quiet,
.oe-btn.oe-secondary { background: var(--surface); color: var(--ink); border-color: var(--rule-strong); }
.btn-quiet:hover,
.oe-btn.oe-secondary:hover { background: var(--sunk); color: var(--ink); border-color: var(--rule-strong); }
.btn-stop,
.oe-btn-danger { background: var(--stop); border-color: var(--stop); color: #fff; }
.btn-block { inline-size: 100%; }
.btn:disabled { opacity: .45; cursor: not-allowed; }

/* ----------------------------------------------------------------- index -- */
/* The listing index: a ruled column, not a grid of cards. Each record is one
   entry — plate, class mark, title, place, price — on a shared spine. */

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

.record {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) minmax(0, auto);
  gap: 0 1.5rem;
  align-items: start;
  padding: 1.125rem .875rem;
  margin-inline: -.875rem;
  /* The separator is the whole device -- this index refuses card borders, so the
     rule between records is what does their work. At --rule it was 1.25:1 against
     the ground and the eye was actually reading the plate rhythm instead. */
  border-block-end: 1px solid var(--rule-mid);
  border-radius: var(--radius);
  transition: background-color .12s ease-out;
}
.record:hover, .record:focus-within { background: var(--wash); }
.record > .record-body { min-inline-size: 0; }

.record h2, .record h3 {
  overflow-wrap: anywhere;
  font-size: var(--step-1);
  font-weight: 600;
  letter-spacing: -.008em;
  line-height: 1.3;
  margin: 0;
  text-wrap: pretty;
}
/* Ink, not link blue. Forty saturated blue titles down a page flatten it; the
   underline on hover and the row wash carry the affordance instead. */
.record :is(h2, h3) a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: transparent;
  transition: text-decoration-color .12s ease-out;
}
.record :is(h2, h3) a:hover { color: var(--navy-ink); text-decoration-color: currentColor; }
/* The title's box is stretched over the whole text block, so the place, the date
   and the gaps between them are all part of the same target. The block has to be
   positioned for its text to ride above that box -- without it the box swallows
   mousedown and a visitor cannot select a title -- and that positioning is also
   what bounds the box to this column. The photograph therefore cannot be reached
   from here at all; .plate-link is the link that covers it. */
.record .record-body { position: relative; z-index: 1; }
.record :is(h2, h3) a::after { content: ""; position: absolute; inset: 0; }
.record .price { position: relative; z-index: 1; }
/* Wrapper only: the box is the plate's, and the plate sets its own size. It
   carries no link styling of its own -- the "No photo" stand-in is text, and
   inside a bare <a> it came out underlined and in link ink. */
.record .plate-link { display: block; color: inherit; text-decoration: none; }

.record .meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .25rem .5rem;
  font-size: 0.875rem;
  color: var(--ink-muted);
  margin: .3rem 0 0;
}
.record .meta .sep { color: var(--rule-mid); }
.record .price { grid-column: 3; text-align: end; margin: 0; padding-block-start: .1rem; max-inline-size: 9.5rem; }

/* The presentational width/height attributes stay on the img for layout
   stability, so the box has to be asked for in both axes to override them. */
.plate {
  display: block;
  inline-size: var(--plate);
  block-size: auto;
  aspect-ratio: 6 / 5;
  object-fit: cover;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.plate-empty {
  display: grid;
  place-items: center;
  background: var(--sunk);
  border-style: dashed;
  border-color: var(--rule-mid);
  color: var(--ink-muted);
  font-size: var(--step--1);
  letter-spacing: .05em;
  text-transform: uppercase;
  text-align: center;
  padding: .5rem;
}

/* A price is a figure: the number carries the weight, the currency recedes to
   the label size so a long currency name cannot outshout it. */
.price .amount { font-size: var(--step-2); font-weight: 640; letter-spacing: -.015em; line-height: 1.2; }
.price .cur { font-size: var(--step--1); font-weight: 500; color: var(--ink-muted); letter-spacing: 0; }
/* "Check with seller" and "Free" are answers, not blanks. Set at the body size in
   the soft ink, a record without a figure still reads as a record with a price. */
.price-note { font-size: var(--step-0); font-weight: 560; color: var(--ink-soft); }

@media (max-width: 40rem) {
  .record { grid-template-columns: auto minmax(0, 1fr); gap: 0 1rem; }
  .record > .price { grid-column: 2; text-align: start; padding-block-start: .5rem; }
  .plate { --plate: 5.5rem; }
  /* The price keeps the figure size it has on a wide screen. Dropping it a step
     put it at the weight of the date beside it, and a record whose price reads
     as metadata is the one thing this index is not allowed to be. It sits under
     the title rather than beside it, so it costs a line, not a column. */
}

/* ------------------------------------------------------------------ flags -- */
/* Tint, shape and word — never colour on its own. The shape is a real glyph in
   the flow, so it survives greyscale and a stylesheet that fails to load. */

.flag {
  display: inline-flex;
  align-items: center;
  gap: .3em;
  padding: .1rem .5rem;
  border-radius: 999px;
  font-size: var(--step--1);
  font-weight: 600;
  white-space: nowrap;
  line-height: 1.5;
}
.flag-brand { background: var(--navy-tint); color: var(--navy-ink); }
.flag-ok   { background: var(--ok-tint);   color: var(--ok); }
.flag-warn { background: var(--warn-tint); color: var(--warn); }
.flag-stop { background: var(--stop-tint); color: var(--stop); }

/* -------------------------------------------------------- catalogue entry -- */
/* The item page. Plates, then the description, then the specification list,
   then the seller record beside it. */

.record-sheet {
  display: grid;
  gap: 2rem 3.5rem;
  grid-template-columns: minmax(0, 1fr) minmax(17rem, 20rem);
  align-items: start;
}
.record-sheet > * { min-inline-size: 0; }

/* The item page's three blocks are placed explicitly rather than in flow, so the
   aside can sit beside both of them on a wide screen while staying between them
   in the source. That is what lets the narrow layout put the price and the
   contact action directly under the photographs without an `order` -- which
   would move the eye and leave the tab order and the screen reader behind. */
.record-sheet > .entry-lead { grid-column: 1; grid-row: 1; }
.record-sheet > .entry-body { grid-column: 1; grid-row: 2; }
.record-sheet > .aside      { grid-column: 2; grid-row: 1 / span 2; }
/* The last thing in the lead carries its own margin; the grid gap adds the rest.
   A <dialog> is exempt: it is out of flow and has no trailing margin to trim,
   but it does centre itself in the window by resolving `margin: auto` -- so
   pinning its bottom margin to zero drops the panel against the bottom edge of
   the screen. Written at the end of the block, it is the last child. */
.entry-lead > :last-child:not(dialog) { margin-block-end: 0; }

@media (max-width: 60rem) {
  .record-sheet { grid-template-columns: minmax(0, 1fr); gap: 2.5rem; }
  .record-sheet > :is(.entry-lead, .entry-body, .aside) { grid-column: 1; grid-row: auto; }
  /* One column: the aside is a block in the reading order, not a rail beside it,
     so pinning it would park the decision over the description. */
  .aside { position: static; }
}

.crumbs { font-size: var(--step--1); color: var(--ink-muted); margin: 0 0 .75rem; }
.crumbs ol { display: flex; flex-wrap: wrap; gap: .25rem .5rem; list-style: none; margin: 0; padding: 0; }
.crumbs a { color: var(--ink-muted); }
.crumbs a:hover { color: var(--ink); }

.entry-head { margin-block-end: 1.75rem; }
.entry-head h1 { margin-block-end: .5rem; }
.entry-meta { display: flex; flex-wrap: wrap; gap: .25rem .625rem; color: var(--ink-muted); font-size: 0.9375rem; margin: 0; }

/* One image fills the plate; several become a snap-scrolled strip that keeps
   the same plate proportions, so the page height does not change with the
   number of photographs. */
.plates {
  display: grid;
  grid-auto-flow: column;
  /* Not a clean half. Two plates filling the strip exactly is how a third one
     becomes invisible: nothing overlaps the edge, so nothing suggests the strip
     scrolls. At 42% the next plate is cut by the boundary, which is the oldest
     signal there is that a row continues. */
  grid-auto-columns: min(100%, calc(42% - .375rem));
  gap: .75rem;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scroll-snap-type: x mandatory;
  margin: 0 0 2rem;
  padding: 0 0 .5rem;
  list-style: none;
  scrollbar-width: thin;
}
.plates li { scroll-snap-align: start; min-inline-size: 0; }
.plates a { display: block; }
.plates img {
  inline-size: 100%;
  aspect-ratio: 4 / 3;
  object-fit: contain;
  background: var(--sunk);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.plates-single { grid-auto-columns: 100%; }
@media (max-width: 45rem) { .plates { grid-auto-columns: min(100%, 88%); } }

/* How many photographs there are, stated rather than implied. A visitor judging
   a used item judges it on the evidence they can see, so the count of what they
   cannot see yet belongs on the page and not in the scrollbar. */
.plate-count {
  margin: -1.25rem 0 2rem;
  font-size: var(--step--1);
  color: var(--ink-muted);
  letter-spacing: .04em;
  text-transform: uppercase;
}

/* A specification list is a table of two columns; ruled rows are what makes it
   scannable, and they fill a column that otherwise trails off. */
.spec { margin: 0; display: grid; grid-template-columns: minmax(6rem, 11rem) minmax(0, 1fr); }
.spec dt, .spec dd { padding-block: .6875rem; border-block-start: 1px solid var(--rule); margin: 0; }
.spec dt { font-size: var(--step--1); font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-muted); padding-inline-end: 1rem; }
.spec dd { color: var(--ink); overflow-wrap: anywhere; }
.spec > :nth-last-child(-n + 2) { border-block-end: 1px solid var(--rule); }

.aside {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 1.5rem;
  position: sticky;
  inset-block-start: 1.5rem;
}
.aside > :last-child { margin-block-end: 0; }
.figure-price {
  font-size: var(--step-5);
  font-weight: 660;
  letter-spacing: -.03em;
  line-height: 1.05;
  margin: 0 0 1.25rem;
  text-wrap: balance;
}
.figure-price .amount { font-size: inherit; font-weight: inherit; letter-spacing: inherit; }
.figure-price .cur { font-size: var(--step-0); font-weight: 500; color: var(--ink-muted); letter-spacing: 0; }
.figure-price .price-note { font-size: var(--step-2); font-weight: 600; color: var(--ink-soft); }

/* The seller record inside the aside: the same two-column reading as the
   specification list, at the aside's smaller measure. */
.attrib { margin: 0 0 1.25rem; display: grid; grid-template-columns: auto minmax(0, 1fr); gap: .5rem .875rem; font-size: 0.9375rem; }
.attrib dt { font-size: var(--step--1); font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-muted); padding-block-start: .15rem; }
.attrib dd { margin: 0; overflow-wrap: anywhere; }

.aside-actions { display: grid; gap: .625rem; margin-block-end: 1.25rem; }
.safety {
  font-size: var(--step--1);
  color: var(--ink-muted);
  border-block-start: 1px solid var(--rule);
  padding-block-start: 1rem;
  margin: 0;
}

/* The two facts that say who a seller is, under their name and in the label size
   because that is what they are -- not a claim the seller makes, a record the
   site keeps. */
.attrib-note { display: block; font-size: var(--step--1); color: var(--ink-muted); margin-block-start: .15rem; }

/* The last resort on the page. Shut, quiet, and set apart from the actions above
   it -- a visitor looking for it will find it, and nobody else is invited. */
.report { margin-block-start: 1.25rem; border-block-start: 1px solid var(--rule); }
.report > summary {
  cursor: pointer;
  list-style: none;
  padding-block: .75rem;
  font-size: var(--step--1);
  color: var(--ink-muted);
}
.report > summary::-webkit-details-marker { display: none; }
.report > summary:hover { color: var(--ink); text-decoration: underline; }
.report .field { margin-block-end: .75rem; }
/* The same line at the head of a form rather than the foot of an aside: it closes
   nothing, it opens something, so the rule moves under it. */
.safety-lead {
  border-block-start: 0;
  border-block-end: 1px solid var(--rule);
  padding-block: 0 1rem;
  margin-block-end: 1.25rem;
}

/* --------------------------------------------------------------- facets -- */

.facets { display: grid; gap: 0; align-content: start; }
.facets > h2 {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 .25rem;
  padding-block-end: .625rem;
  border-block-end: 2px solid var(--ink);
}
.facets details { border-block-end: 1px solid var(--rule); }
.facets summary {
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  font-size: var(--step-0);
  font-weight: 600;
  padding: .75rem .375rem;
  color: var(--ink);
  list-style: none;
}
.facets summary::-webkit-details-marker { display: none; }
.facets summary::after {
  content: "";
  inline-size: .5rem;
  block-size: .5rem;
  flex: none;
  /* Physical sides on purpose. This mark points down when closed and up when
     open, and a vertical arrow means the same in both directions -- it must not
     mirror. The borders were logical and the rotation was not, so in RTL the
     sides swapped, the angle did not, and the arrow came out pointing sideways. */
  border-right: 2px solid var(--ink-muted);
  border-bottom: 2px solid var(--ink-muted);
  transform: rotate(45deg) translate(-.1em, -.1em);
  transition: transform .15s ease-out;
}
.facets details[open] > summary::after { transform: rotate(-135deg) translate(-.1em, -.1em); }
.facets summary:hover { color: var(--navy-ink); }
.facets summary:hover::after { border-color: var(--navy-ink); }
.facets ul { list-style: none; margin: 0 0 .875rem; padding: 0; display: grid; gap: .125rem; }
.facets a {
  display: block;
  padding: .375rem .375rem;
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--ink-soft);
}
.facets a:hover { background: var(--wash); color: var(--navy-ink); text-decoration: underline; }
/* A facet row that carries a count is read the way the home page's shelf list is
   read: name on the left, figure on the right, in the tabular numerals the body
   already sets, so the column of counts stacks. */
.facets a:has(.count) { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.facets a:hover .name { text-decoration: underline; }
.facets a:has(.count):hover { text-decoration: none; }
.facets .count { flex: none; font-size: var(--step--1); color: var(--ink-muted); }
.facets [aria-current] .count { color: inherit; }
/* The sections inside the shelf the visitor is standing in. Indented against a
   rule rather than bulleted: the line says "these belong to the row above" and
   costs nothing at any width. */
.facet-sub {
  list-style: none;
  margin: .125rem 0 .375rem;
  padding-inline-start: .875rem;
  border-inline-start: 1px solid var(--rule-mid);
  display: grid;
  gap: .125rem;
  /* A shelf with twenty sections made the filter column three times taller than
     the results it was filtering, so the page read as a failure on a search that
     had worked. The sections scroll inside their own box instead. */
  max-block-size: 17rem;
  overflow-y: auto;
  overscroll-behavior-block: contain;
  scrollbar-width: thin;
}
.facet-sub [aria-current] { scroll-margin-block: 3rem; }
.facets .facet-sub a { font-size: 0.9375rem; }
.facets form { padding-block-end: 1rem; }
.facets .field { margin-block-end: .75rem; }

/* An account sidebar is the same object: a ruled list of destinations. */
.facets > ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .125rem; }
.facets [aria-current] { background: var(--navy-tint); color: var(--navy-ink); font-weight: 600; }

/* ------------------------------------------------------- category index -- */
/* The shelf list. Ruled rows in columns, with the count set in the tabular
   figures the body already carries so the numbers stack. */

.classmarks {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0 2.5rem;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}
/* The rule and the hover wash sit on the same box, the way a record's do. Split
   across the <li> and the <a>, the wash was 8px wider than the rule at each end
   and overhung it. */
.classmarks a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: .75rem .5rem;
  margin-inline: -.5rem;
  border-block-end: 1px solid var(--rule);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--ink);
}
.classmarks a:hover { background: var(--wash); color: var(--navy-ink); }
.classmarks a:hover .name { text-decoration: underline; }
.classmarks .count { font-size: var(--step--1); color: var(--ink-muted); }
/* Nothing on this shelf yet. Still a page, still a link -- just not one the eye
   should stop on while it is looking for somewhere with listings in it. Applies
   wherever a shelf is listed: the home index and the facet column both. */
.classmarks a.empty-shelf .name,
.facets a.empty-shelf .name { color: var(--ink-muted); }

/* --------------------------------------------------------------- dialogs -- */

dialog {
  border: 1px solid var(--rule-mid);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  padding: 1.75rem;
  inline-size: min(34rem, calc(100vw - 2rem));
  max-block-size: calc(100dvh - 4rem);
  overflow: hidden;
}
/* The fields scroll; the buttons do not. On a laptop 600px tall the panel used to
   cut mid-textarea with the submit below the fold, so a form that was not
   finished looked finished.
   Scoped to [open]: `display` on the bare element overrides the user agent's
   `display: none` for a closed dialog, which lays the panel out off the side of
   the page and gives the document a horizontal scrollbar. */
dialog[open] { display: flex; flex-direction: column; }
dialog > form {
  display: flex;
  flex-direction: column;
  min-block-size: 0;
  overflow-y: auto;
  /* The action bar below is sticky and opaque, so the browser's own scroll-into-
     view for a focused field has to be told that the last stretch of the
     scrollport is spoken for. Without it a field whose top is technically inside
     the box is considered visible, nothing scrolls, and on a short window the
     required message textarea takes focus with none of it on screen. */
  scroll-padding-block-end: 5rem;
}
dialog > form > .actions {
  position: sticky;
  inset-block-end: 0;
  margin-block-end: 0;
  padding-block: 1rem;
  background: var(--surface);
  border-block-start: 1px solid var(--rule);
}
dialog h2 { margin-block-start: 0; }
/* What the visitor is about to write about: the listing they were looking at, in
   the record's own vocabulary. The dialog covers the page that carried both. */
.dialog-subject {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .25rem .75rem;
  padding-block-end: .875rem;
  margin-block-end: 1.25rem;
  border-block-end: 1px solid var(--rule);
}
.dialog-subject .name { font-weight: 600; overflow-wrap: anywhere; }
.dialog-subject .price { margin: 0; margin-inline-start: auto; }
.dialog-subject .price .amount { font-size: var(--step-1); }
dialog::backdrop { background: rgb(10 14 26 / .55); }

/* A photograph at the size the page cannot give it. The panel is only a frame,
   so it is wider than a form dialog and carries almost no padding. The picture
   is bounded in both axes -- a tall photograph must not push the close button
   off the bottom of the window. */
/* The frame takes the photograph's width too, so a portrait picture is not
   stranded in the middle of a landscape panel. Floored so the button row below
   it still has somewhere to sit. */
.plate-view {
  inline-size: fit-content;
  min-inline-size: min(18rem, calc(100vw - 2rem));
  max-inline-size: min(60rem, calc(100vw - 2rem));
  padding: .75rem;
}
/* The panel is a frame around the photograph and takes its size, so a small
   picture does not sit in a large grey box. The photograph opts out of the flex
   column's stretch -- left in it, a small one was pulled to the full width of
   the panel and grew taller than the window with it -- and is allowed to shrink
   when the window is short. Bounded in both axes; a replaced element keeps its
   own ratio between them. The reserve is the panel's padding, the button row,
   and the gap the dialog keeps from the edge of the window. */
.plate-view img {
  align-self: center;
  flex: 0 1 auto;
  min-block-size: 0;
  max-inline-size: 100%;
  max-block-size: calc(100dvh - 12rem);
  inline-size: auto;
  block-size: auto;
  border-radius: calc(var(--radius) - 2px);
  background: var(--sunk);
}
.plate-view > form {
  display: block;
  overflow: visible;
  text-align: end;
  padding-block-start: .75rem;
}

/* ---------------------------------------------------------------- notice -- */

.notice {
  border: 1px solid var(--rule-mid);
  border-radius: var(--radius);
  padding: .875rem 1.125rem;
  margin-block-end: 1.5rem;
  background: var(--surface);
}
.notice-ok   { background: var(--ok-tint);   border-color: var(--ok);   color: var(--ok); }
.notice-stop { background: var(--stop-tint); border-color: var(--stop); color: var(--stop); }
.notice-warn { background: var(--warn-tint); border-color: var(--warn); color: var(--warn); }
.notice > :last-child { margin-block-end: 0; }

/* Core's flash messages. The class names are core's and frozen, so they are
   styled here rather than swapped: the element is a <div>, not the list this
   sheet used to assume, and every one of them rendered unstyled. */
.flashmessage {
  border: 1px solid var(--rule-mid);
  border-radius: var(--radius);
  padding: .875rem 1.125rem;
  margin-block-end: 1.5rem;
  background: var(--surface);
}
.flashmessage > :last-child { margin-block-end: 0; }
.flashmessage-ok      { background: var(--ok-tint);   border-color: var(--ok);   color: var(--ok); }
.flashmessage-error   { background: var(--stop-tint); border-color: var(--stop); color: var(--stop); }
.flashmessage-warning { background: var(--warn-tint); border-color: var(--warn); color: var(--warn); }
.flashmessage-info    { background: var(--navy-tint); border-color: var(--navy-line); color: var(--navy-ink); }
.flashmessage a { color: inherit; }
/* Core prints the dismiss control as an <a> with no href, no role and no
   tabindex, styled by whatever the theme makes of `.btn .ico-close`. It cannot
   be operated without script, so it stays hidden until the footer script gives
   it a role -- which is also what makes it a real control rather than a button
   shape that swallows clicks. The `.btn` shorthands have to be undone here:
   the classes are core's and the anchor is not a page button. */
.flashmessage .ico-close { display: none; }
.flashmessage .ico-close[role="button"] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  float: inline-end;
  inline-size: var(--tap);
  block-size: var(--tap);
  min-block-size: 0;
  /* A 44px target next to a 24px line. The block margins pull it back so the
     glyph sits on the first line instead of hanging below it, and so the
     control adds no height to a one-line message. */
  margin-block: -.625rem;
  margin-inline: .75rem -.4375rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius);
  background: none;
  color: inherit;
  font-size: 1rem;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
}
.flashmessage .ico-close[role="button"]:hover {
  background: color-mix(in srgb, currentColor 12%, transparent);
  color: inherit;
}

.empty {
  border: 1px dashed var(--rule-mid);
  border-radius: var(--radius);
  padding: 3rem 1.5rem;
  text-align: center;
  color: var(--ink-muted);
  background: var(--sunk);
}
.empty > :last-child { margin-block-end: 0; }
.empty strong { display: block; color: var(--ink); font-size: var(--step-1); margin-block-end: .35rem; }

/* ------------------------------------------------------------- utilities -- */

.stack > * + * { margin-block-start: var(--stack-gap, 2.5rem); }
.row { display: flex; flex-wrap: wrap; gap: .5rem 1rem; align-items: baseline; }
.row > :is(h1, h2) { margin-block-end: 0; }
.push { margin-inline-start: auto; }

.pager {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 1rem;
  align-items: center;
  margin-block-start: 2rem;
  padding-block-start: 1.25rem;
  border-block-start: 1px solid var(--rule);
}
/* Three tracks, so the counter is centred on the column whether or not there is a
   previous page to link to. Auto margins cannot do this: on page one they divide
   the free space between the counter and the next link instead of holding the
   middle, and an empty <span> to reserve the slot is markup doing layout's job. */
.pager > .prev  { grid-column: 1; justify-self: start; }
.pager > .tally { grid-column: 2; justify-self: center; }
.pager > .next  { grid-column: 3; justify-self: end; }
.pager a { text-decoration: none; font-weight: 560; }
.pager a:hover { text-decoration: underline; }

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

@media (max-width: 40rem) {
  .masthead-bar { padding-block: .875rem; gap: .375rem 1.25rem; }
  .searchbar-band { padding-block: 1rem 1.25rem; }
  /* A lone right-aligned link under a heading reads as an orphan once the row
     wraps; on one column it belongs at the start like everything else. */
  .ruled > .push { margin-inline-start: 0; }
  .aside { padding: 1.25rem; }
  .spec { grid-template-columns: minmax(0, 1fr); }
  .spec dt { padding-block-end: 0; border-block-end: 0; }
  .spec dd { padding-block-start: .125rem; border-block-start: 0; }
  .spec > :nth-last-child(-n + 2) { border-block-end: 0; }
  .spec dd { border-block-end: 1px solid var(--rule); padding-block-end: .6875rem; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
}

/* ------------------------------------------------------- core-owned pages -- */
/* The account section and the sign-in pages are core's markup, rendered between
   this theme's header and footer. Core publishes the class names below as a
   contract (docs: "Account pages"); this block is the whole of what makes those
   pages folio rather than generic. No PHP, and nothing renamed.
   Written against the same objects the catalogue already has: the account nav is
   a facet column, a listing row is a record, a form is a sheet. */

/* Core bounds its own column; inside a theme the theme's spine already does, so
   the second constraint only makes the page narrower than every other page. */
.oe-page .oe-doc { max-inline-size: none; margin: 0; padding: 0; }
.oe-page .oe-h1 { font-size: var(--step-4); font-weight: 620; letter-spacing: -.021em; margin: 0 0 1.5rem; }
.oe-page h2 { font-size: var(--step-2); font-weight: 620; letter-spacing: -.012em; }
.oe-page .oe-muted { color: var(--ink-muted); }
/* Core sets its own heading margins at a specificity this sheet's `* + h2`
   cannot reach, so the space above a heading has to be asked for again here. */
.oe-page * + h2 { margin-block-start: 2.5rem; }
.oe-page * + .oe-panel { margin-block-start: 1.5rem; }

/* Content beside a facet column — the same object as .record-sheet. */
.oe-page .oe-account {
  display: grid;
  gap: 2rem 3.5rem;
  grid-template-columns: minmax(0, 1fr) minmax(15rem, 18rem);
  align-items: start;
}
@media (max-width: 60rem) {
  .oe-page .oe-account { grid-template-columns: minmax(0, 1fr); gap: 2.5rem; }
}

/* The account nav is a facet list: a tracked-out column head over a ruled list. */
.oe-page .oe-account-nav > h2 {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 .25rem;
  padding-block-end: .625rem;
  border-block-end: 2px solid var(--ink);
}
.oe-page .oe-account-nav ul { display: grid; gap: .125rem; }
.oe-page .oe-account-nav a {
  padding: .375rem .375rem;
  margin-inline: 0;
  color: var(--ink-soft);
  border-radius: var(--radius);
}
.oe-page .oe-account-nav a:hover { background: var(--wash); color: var(--navy-ink); text-decoration: underline; }
.oe-page .oe-account-nav [aria-current] { background: var(--navy-tint); color: var(--navy-ink); font-weight: 600; }

/* A listing in an account list is the same record as one in the index: a ruled
   row on a shared spine, with the plate, the title and the price in a line. */
.oe-page .oe-list-item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) minmax(0, auto);
  gap: 0 1.5rem;
  align-items: start;
  padding: 1.125rem .875rem;
  margin-inline: -.875rem;
  border-block-end: 1px solid var(--rule);
  border-radius: var(--radius);
  transition: background-color .12s ease-out;
}
.oe-page .oe-list-item:hover, .oe-page .oe-list-item:focus-within { background: var(--wash); }
.oe-page .oe-list-item h3 {
  font-size: var(--step-1);
  font-weight: 600;
  letter-spacing: -.008em;
  line-height: 1.3;
  margin: 0;
}
/* Ink, not link blue: a column of saturated blue titles flattens the page. The
   underline on hover and the row wash carry the affordance instead. */
.oe-page .oe-list-item h3 a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: transparent;
  transition: text-decoration-color .12s ease-out;
}
.oe-page .oe-list-item h3 a:hover { color: var(--navy-ink); text-decoration-color: currentColor; }

.oe-page .oe-thumb {
  inline-size: var(--plate);
  block-size: auto;
  aspect-ratio: 6 / 5;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.oe-page .oe-thumb-empty {
  background: var(--sunk);
  border-style: dashed;
  border-color: var(--rule-mid);
  color: var(--ink-muted);
  font-size: var(--step--1);
  letter-spacing: .05em;
  text-transform: uppercase;
}

.oe-page .oe-meta { font-size: 0.875rem; color: var(--ink-muted); gap: .25rem .5rem; margin: .3rem 0 0; }
/* Core renders one formatted string ("21000 Dollar US$") rather than the split
   figure the index uses, and the currency name can be long — so this is set a
   step down and given room, instead of being squeezed onto two lines. */
.oe-page .oe-price {
  grid-column: 3;
  font-size: var(--step-1);
  font-weight: 640;
  letter-spacing: -.015em;
  line-height: 1.3;
  max-inline-size: 11rem;
  padding-block-start: .15rem;
}

@media (max-width: 40rem) {
  .oe-page .oe-list-item { grid-template-columns: auto minmax(0, 1fr); gap: 0 1rem; }
  .oe-page .oe-price { grid-column: 2; text-align: start; padding-block-start: .5rem; }
  .oe-page .oe-thumb { --plate: 5.5rem; }
  .oe-page .oe-price { font-size: var(--step-1); }
}

/* A status flag: tint, shape and word, never colour on its own. Core's badge
   modifiers are named for the billing states they were written for; they map
   onto this theme's three signals. */
.oe-page .oe-badge {
  padding: .1rem .5rem;
  border-radius: 999px;
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 1.5;
}
.oe-page .oe-badge.paid      { background: var(--ok-tint);   color: var(--ok); }
.oe-page .oe-badge.pending   { background: var(--warn-tint); color: var(--warn); }
.oe-page .oe-badge.failed,
.oe-page .oe-badge.cancelled { background: var(--stop-tint); color: var(--stop); }
.oe-page .oe-badge.refunded  { background: var(--sunk);      color: var(--ink-muted); }

/* Forms. The controls carry no class of core's own, so they are reached through
   the field wrapper; core's own defaults are declared at zero specificity for
   exactly this. */
.oe-page .oe-form-page { max-inline-size: 30rem; }
/* A column of fields as wide as the content column stops reading as one object.
   The same bound the theme's own forms use. */
.oe-page .oe-account-main form { max-inline-size: 32rem; }
.oe-page .oe-label {
  font-size: var(--step--1);
  font-weight: 600;
  margin-block-end: .375rem;
  color: var(--ink-soft);
}
.oe-page .oe-field { margin-block-end: 1.125rem; }
.oe-page .oe-field :is(input, select, textarea) {
  font: inherit;
  inline-size: 100%;
  max-inline-size: none;
  min-block-size: var(--tap);
  padding: .5rem .75rem;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
}
.oe-page .oe-field textarea { block-size: 9rem; min-block-size: 6rem; line-height: 1.55; }
.oe-page .oe-field input[type="file"] { min-block-size: 0; }
.oe-page .oe-field :is(input, select, textarea):focus { border-color: var(--navy); }
.oe-page .oe-field :is(input, select, textarea):focus-visible { outline: 2px solid var(--link); outline-offset: 2px; }
.oe-page .oe-field :is(input, select):user-invalid { border-color: var(--stop); }
/* Core stacks the region select under the country one; they read as one control. */
.oe-page .oe-field > :is(input, select) + :is(input, select) { margin-block-start: .5rem; }
.oe-page .oe-hint { font-size: var(--step--1); color: var(--ink-muted); margin-block-start: .375rem; }
.oe-page .oe-check { gap: .625rem; margin-block-end: 1.125rem; font-size: var(--step-0); }
/* The checkbox's own label is a sentence, not a field label. */
.oe-page .oe-check, .oe-page .oe-check span { font-weight: 400; color: var(--ink); }
.oe-page .oe-check input {
  inline-size: 1.125rem;
  block-size: 1.125rem;
  min-block-size: 0;
  padding: 0;
  accent-color: var(--navy);
}
.oe-page .oe-actions { gap: .875rem; margin-block-start: 1.5rem; align-items: center; }

/* `.oe-btn` itself is styled up with `.btn` near the top of this sheet. Core ships
   its page stylesheet in `@layer shopclass`, and an unlayered rule beats a layered
   one at any specificity, so one class is enough and this file does not have to
   mirror core's selector shape. Only the anchors core styles through `.oe-actions`
   need naming here, because they carry no `.oe-btn` of their own. */
.oe-actions a.oe-primary {
  font: inherit;
  font-weight: 560;
  min-block-size: var(--tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: .5rem 1.25rem;
  border: 1px solid var(--navy-edge);
  border-radius: var(--radius);
  background: var(--navy);
  color: #fff;
}
.oe-actions a.oe-primary:hover { background: var(--navy-deep); border-color: var(--navy-edge); color: #fff; }
.oe-actions a.oe-secondary {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--rule-strong);
}
.oe-actions a.oe-secondary:hover { background: var(--sunk); color: var(--ink); border-color: var(--rule-strong); }
.oe-btn-danger:hover { background: var(--stop); border-color: var(--stop); filter: brightness(.92); }

/* Panels, empty states and paging, in the theme's own weights. */
.oe-page .oe-panel {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 1.5rem;
  margin-block-end: 1.5rem;
}
.oe-page .oe-panel > :last-child { margin-block-end: 0; }
/* Core scopes its own rules under .oe-page, which outranks a bare class here —
   so a flash message rendered inside core's block needs the tints repeated. */
.oe-page .flashmessage { background: var(--surface); border-color: var(--rule-mid); }
.oe-page .flashmessage-ok      { background: var(--ok-tint);   border-color: var(--ok);   color: var(--ok); }
.oe-page .flashmessage-error   { background: var(--stop-tint); border-color: var(--stop); color: var(--stop); }
.oe-page .flashmessage-warning { background: var(--warn-tint); border-color: var(--warn); color: var(--warn); }
.oe-page .flashmessage-info    { background: var(--navy-tint); border-color: var(--navy-line); color: var(--navy-ink); }
.oe-page .oe-empty {
  border: 1px dashed var(--rule-mid);
  border-radius: var(--radius);
  padding: 3rem 1.5rem;
  color: var(--ink-muted);
  background: var(--sunk);
}
.oe-page .oe-pager {
  margin-block-start: 2rem;
  padding-block-start: 1.25rem;
  border-block-start: 1px solid var(--rule);
  font-size: var(--step-0);
  gap: 1rem;
  justify-content: center;
}
.oe-page .oe-pager a { text-decoration: none; font-weight: 560; }
.oe-page .oe-pager a:hover { text-decoration: underline; }
/* Core's paginator marks the current page with .selected on the <li>. */
.oe-page .oe-pager ul { display: flex; flex-wrap: wrap; gap: .25rem .75rem; list-style: none; margin: 0; padding: 0; }

/* The register form's error box is core's #error_list, filled by core's own
   validator; it has no class, and it is the same object as a stop notice. */
.oe-page #error_list:not(:empty) {
  background: var(--stop-tint);
  border-color: var(--stop);
  color: var(--stop);
  border-radius: var(--radius);
}
