/* PARKSIDE manuals — page styles.
   No build step: plain CSS with custom properties for the values that
   repeat. Class names stay semantic (.card, .chip, .badge) so the rules
   read as what they style rather than as how they look. */

/* Three themes. The default is plain black and white, the one this page
   was built in; the other two are PARKSIDE's own design tokens, lifted
   from parkside-diy.com. A theme is only paint -- it says nothing about
   which products are shown, unlike the Series filter. */

:root {
  /* brand */
  --brand: #171717;
  --brand-strong: #000;
  --spark: #e30613;
  --alert: #e30613;          /* destructive hover and failure */

  /* surfaces */
  --paper: #fff;             /* cards, chips */
  --page: #f3f3f3;           /* the page behind them */
  --wash: #fafafa;           /* hover fill */
  --thumb: #fff;             /* covers are white-backed: match them */

  /* ink is the high-contrast colour: dark on a light theme, light on a
     dark one. `strong` is the filled surface it paints. */
  --ink: #171717;
  --strong: #171717;
  --on-strong: #fff;

  /* greys, lightest last */
  --muted: #777;
  --dim: #888;
  --faint: #999;
  --ghost: #aaa;
  --line: #ddd;              /* card and header rules */
  --edge: #dcdcdc;           /* control borders */

  /* meaning: a link, a good state, an uncertain one, a confirmed one */
  --link: #3a6ea5;
  --link-bg: #eef4fb;
  --link-edge: #cfd9e6;
  /* A link in the page's own text follows the theme, unlike `--link`,
     which also paints the kind tag and the IANs badge -- those are
     labels, and the brand red would read as a warning on them. */
  --link-ink: var(--ink);
  --ok: #0a6b3d;
  --ok-bg: #e6f4ec;
  --warn: #7a5c00;
  --warn-bg: #fdf3d8;
  --warn-edge: #d6b13a;
  --confirmed: #0a4b7d;
  --confirmed-bg: #e4eef7;

  /* series and group badges. Their ink is fixed rather than themed:
     every one of these is a mid-tone, dark enough for white in either
     theme and never light enough for the dark theme's own surface. */
  --on-series: #fff;
  --series-performance: #0a4b7d;
  --series-toy: #7a5c00;
  --series-garden: #2f6b3a;
  --series-washer: #1f5f70;
  --series-compressor: #4a5568;

  /* The header is its own surface: on the Parkside theme it carries the
     brand green, so everything sitting on it needs its own contrast. */
  --header-bg: var(--paper);
  --header-ink: var(--ink);
  --header-muted: var(--muted);
  --header-faint: var(--faint);
  --header-line: var(--line);

  /* a chip sits on the header but keeps its own light surface */
  --chip-ink: var(--ink);
  --chip-on: var(--strong);
  --on-chip-on: var(--on-strong);

  --logo-filter: none;
  --logo-filter-on: invert(1);
}

/* PARKSIDE: their primary green on their light grey. */
[data-theme="parkside"] {
  --brand: #1e4b32;
  --brand-strong: #143c28;
  --spark: #00ff99;          /* power green, used as a highlight only */
  --alert: #cd2828;

  --page: #f2f3f4;
  --ink: #1c1c1c;
  --strong: #1e4b32;

  --muted: #626262;
  --dim: #7c8489;
  --faint: #98a0a5;
  --ghost: #b0b7bb;
  --line: #dee2e4;
  --edge: #cccfd1;

  --link: #1e4b32;
  --link-bg: #e7efea;
  --link-edge: #c4d5cb;
  --link-ink: #1e4b32;
  --ok: #1e4b32;
  --ok-bg: #e7efea;

  --series-performance: #1c1c1c;

  /* the whole top of the page is the brand green */
  --header-bg: #1e4b32;
  --header-ink: #fff;
  --header-muted: rgba(255, 255, 255, .82);
  --header-faint: rgba(255, 255, 255, .62);
  --header-line: #143c28;

  /* selected chips take the power green, which is what that colour is
     for -- brand green on brand green would vanish */
  --chip-on: #00ff99;
  --on-chip-on: #143c28;
}

/* PERFORMANCE: charcoal and red. Their dark-grey base is the page, the
   secondary grey the card surface, and red does the work green does. */
[data-theme="performance"] {
  --brand: #cd2828;
  --brand-strong: #a81f1f;
  --spark: #eb4545;
  --alert: #eb4545;

  --paper: #232d32;
  --page: #1c1c1c;
  --wash: #2b363c;
  --thumb: #fff;             /* covers are white-backed, even here */

  --ink: #f2f3f4;
  --strong: #cd2828;
  --on-strong: #fff;

  --muted: #a6afb4;
  --dim: #98a2a8;
  --faint: #8a949a;
  --ghost: #78838a;
  --line: #33414a;
  --edge: #3f4d55;

  --link: #7bb0f0;
  --link-bg: #2a3a46;
  --link-edge: #3d5566;
  /* The brand red #cd2828 reaches only 2.6:1 on this charcoal, so the
     link takes a lighter tint of it rather than the brand value. */
  --link-ink: #f2807c;
  --ok: #6fdca6;
  --ok-bg: #26372f;
  --warn: #e0bb5c;
  --warn-bg: #38321f;
  --warn-edge: #6b5a2a;
  --confirmed: #8ec5f0;
  --confirmed-bg: #253746;

  --series-performance: #cd2828;
  --series-toy: #8a6a10;
  --series-compressor: #55606d;

  --header-bg: #232d32;
  --header-ink: #f2f3f4;
  --header-muted: #a6afb4;
  --header-faint: #8a949a;
  --header-line: #33414a;
  --chip-ink: #f2f3f4;

  /* the wordmarks are black artwork, so they need inverting on a dark
     page -- and not inverting again on a selected chip */
  --logo-filter: invert(1);
  --logo-filter-on: none;
}

/* ---------------------------------------------------------------- frame */

* { box-sizing: border-box }

body {
  margin: 0;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  background: var(--page);
  color: var(--ink);
}

header {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--header-bg);
  color: var(--header-ink);
  border-bottom: 1px solid var(--header-line);
  padding: 16px;
}

header > div, main { max-width: 1400px; margin: auto }
main { padding: 22px 16px 50px }
h1 { margin: 0 0 12px; font-size: 24px }

/* The manufacturer's own mark, inline so the wordmark can take the
   header's colour while the three bars keep the brand red. */
.brand { display: flex; align-items: baseline; gap: 9px; margin: 0 0 12px; cursor: pointer }
.brand .wordmark { height: 21px; width: auto; align-self: center }
.wordmark-bars { fill: #cd2828 }
.wordmark-text { fill: var(--header-ink) }
.brand-word { font-size: 21px; font-weight: 400; color: var(--header-ink); opacity: .92 }

.controls { display: flex; gap: 10px; flex-wrap: wrap }

/* the interface-language switch, kept out of the filter rows so it is
   not mistaken for the catalogue-language filter */
.topline { display: flex; align-items: center; justify-content: space-between; gap: 12px }

/* the switches live in the open on a wide screen, behind this on a phone */
.options-toggle { display: none }
.switches { display: flex; gap: 8px; align-items: center; margin-bottom: 12px }


.uilang { display: flex; gap: 4px }
.uilang button {
  font: inherit;
  font-size: 12px;
  line-height: 1;
  padding: 6px 10px;
  border: 1px solid var(--edge);
  border-radius: 6px;
  background: var(--paper);
  color: var(--chip-ink);
  cursor: pointer;
}
.uilang button:hover { border-color: var(--muted); background: var(--wash) }
.uilang button.on { background: var(--chip-on); border-color: var(--chip-on); color: var(--on-chip-on); cursor: default }

input, select {
  font: inherit;
  padding: 10px 12px;
  border: 1px solid var(--edge);
  border-radius: 7px;
  background: var(--paper);
  color: var(--ink);
}

#search { flex: 1; min-width: 240px; max-width: 460px }

/* The clear button a search field grows once something is typed. The
   browser draws a grey X of its own; this is the same shape in the
   theme's colour, so the field matches everything around it. */
#search::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  margin-left: 6px;
  cursor: pointer;
  background: var(--brand);
  opacity: .55;
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cpath%20d%3D%22M3.5%203.5l9%209M12.5%203.5l-9%209%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cpath%20d%3D%22M3.5%203.5l9%209M12.5%203.5l-9%209%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
}
#search::-webkit-search-cancel-button:hover { opacity: 1 }

/* A focused field takes the theme's own highlight -- the same colour a
   selected chip uses -- so the page says which theme it is even while
   you are typing. */
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none;
  border-color: var(--chip-on);
  box-shadow: 0 0 0 2px var(--chip-on);
}

/* The sort control. The native arrow sits hard against the right edge,
   and the box is as wide as its longest option ("Eniten IAN-numeroita"),
   so the gap between the two is large. Drawing the arrow ourselves puts
   it where the text ends up, and a leading glyph says what the control
   is without spending a row on a label. */
/* The sort control. The border and the icon belong to the wrapper, not
   to the select: the operating system aligns its popup to the select's
   own box, so keeping that box where the text starts brings the open
   list under the words rather than under the icon. The arrow is drawn
   here too -- the browser's sits hard against the edge of a box as wide
   as its longest option. */
.select {
  position: relative;
  display: inline-flex;
  align-items: center;
  /* sized by its longest option, like the native control it wraps */
  flex: none;
  gap: 8px;
  padding: 0 30px 0 11px;
  border: 1px solid var(--edge);
  border-radius: 7px;
  background: var(--paper);
  color: var(--chip-ink);
}
.select::before {
  content: "";
  flex: none;
  width: 13px;
  height: 13px;
  background: currentColor;
  opacity: .5;
  pointer-events: none;
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cpath%20d%3D%22M2%203.4h12v1.7H2zM2%207.15h8.5v1.7H2zM2%2010.9h5v1.7H2z%22%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cpath%20d%3D%22M2%203.4h12v1.7H2zM2%207.15h8.5v1.7H2zM2%2010.9h5v1.7H2z%22%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
}
.select select {
  appearance: none;
  /* The popup follows the select's box, so the box is nudged down while
     the padding is trimmed by the same amount -- the text stays exactly
     where it was, and the list opens five pixels lower. */
  margin-top: 5px;
  padding: 5px 0 10px;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
}
.select select:focus-visible { outline: none; box-shadow: none }
.select:focus-within { border-color: var(--chip-on); box-shadow: 0 0 0 2px var(--chip-on) }
.select::after {
  content: "";
  position: absolute;
  right: 13px;
  top: 50%;
  margin-top: -2px;
  border: 4px solid transparent;
  border-top-color: currentColor;
  opacity: .6;
  pointer-events: none;
}

.summary { color: var(--muted); margin-bottom: 16px }

.linkish {
  font: inherit;
  padding: 0;
  border: 0;
  background: none;
  color: var(--link-ink);
  text-decoration: underline;
  cursor: pointer;
}
.linkish:hover { color: var(--ink) }

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

/* -------------------------------------------------------------- filters */

/* A label column keeps every row's chips on the same left edge, instead
   of each row starting wherever its label happens to end. */
.facets {
  position: relative;
  margin-top: 14px;
  border-top: 1px solid var(--header-line);
  padding-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.facet {
  display: grid;
  /* wide enough for the longest label in either language --
     "KÄYTTÖVOIMA" is half again as long as "POWER" */
  grid-template-columns: 96px 1fr;
  gap: 12px;
  align-items: baseline;
}
/* the filter row names, and the label on the theme buttons: the same
   voice, so they read as one kind of thing */
.facet-name, .switch-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--header-faint);
  text-transform: uppercase;
  letter-spacing: .06em;
}
.facet-name { padding-top: 6px }
/* min-width: 0 is what lets the chips wrap: a grid item defaults to
   min-width: auto, so a row of nowrap chips widens the column past 1fr
   and pushes the page sideways instead of wrapping. */
.facet-chips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; min-width: 0 }
.facet > * { min-width: 0 }
.facet-sep { width: 1px; align-self: stretch; background: var(--header-line); margin: 2px 4px }

#advanced { display: flex; flex-direction: column; gap: 10px; margin-top: 10px }
/* an author display rule beats the hidden attribute, so say it again */
#advanced[hidden] { display: none }

/* The native box is redundant once the chip itself shows the state. */
.chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  line-height: 1;
  padding: 7px 12px;
  border: 1px solid var(--edge);
  border-radius: 7px;
  background: var(--paper);
  color: var(--chip-ink);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  transition: background .12s, border-color .12s, color .12s;
}
.chip input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none }
.chip:hover { border-color: var(--muted); background: var(--wash) }
.chip.on { background: var(--chip-on); border-color: var(--chip-on); color: var(--on-chip-on) }
.chip.on:hover { filter: brightness(.92) }
.chip .n { font-size: 12px; color: var(--faint); font-variant-numeric: tabular-nums }

/* Compressor, pressure washing and garden name groups that also badge a
   card, so a selected chip becomes that badge, ink and all. Only the
   selected state is coloured: unselected, the row reads as one set of
   options, and colouring three of them would say they are a different
   kind of filter rather than a filter that is currently on. The tint is
   held in a custom property so the states are written once. */
.chip.tinted.on {
  background: var(--tint);
  /* The header is brand green on the Parkside theme, and garden green
     against it is nearly the vanishing act the power-green rule was
     written to avoid. The badge colour still fills the chip -- that is
     the point -- so the paper-coloured edge is what says "selected".
     On a light header it lands on its own background and disappears,
     which is exactly where it is not needed. */
  border-color: var(--paper);
  color: var(--on-series);
}
.chip.tinted.on .n { color: var(--on-series) }
.chip.toy { --tint: var(--series-toy) }
.chip.garden { --tint: var(--series-garden) }
.chip.washer { --tint: var(--series-washer) }
.chip.compressor { --tint: var(--series-compressor) }
.chip.on .n { color: currentColor; opacity: .7 }
/* Fading the whole chip lets the header colour through it, which on the
   green theme reads as a different kind of chip. Fade the content. */
.chip.zero { background: var(--wash); color: var(--ghost); border-color: var(--edge) }
.chip.zero .n { color: var(--ghost) }
.chip.review { border-style: dashed; border-color: var(--warn-edge); color: var(--warn) }
.chip.review.on {
  background: var(--warn);
  border-color: var(--warn);
  border-style: solid;
  color: var(--paper);
}

/* Clear filters shares a row with the Filters toggle rather than living
   inside the panel: on a phone the panel folds away, and the button that
   undoes a filter has to outlive the rows it undoes. */
.filter-bar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 12px;
  margin-bottom: -6px;
}

.clear {
  font: inherit;
  font-size: 13px;
  line-height: 1;
  padding: 7px 12px;
  border: 1px solid var(--edge);
  border-radius: 7px;
  background: var(--paper);
  color: var(--chip-ink);
  cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
.clear:hover { border-color: var(--alert); color: var(--alert) }
.clear[disabled] {
  opacity: .45;
  cursor: default;
  border-color: var(--edge);
  background: var(--paper);
  color: var(--muted);
}
.clear[disabled]:hover { border-color: var(--edge); color: var(--muted) }

/* What this build is, behind the logo. */
.about {
  /* A modal dialog is centred by the browser's own margin: auto. Pin the
     top instead, so it opens near the filters rather than halfway down a
     page the reader is not looking at. */
  margin-top: 96px;
  margin-bottom: auto;
  border: 1px solid var(--edge);
  border-radius: 10px;
  padding: 22px 24px 18px;
  background: var(--paper);
  color: var(--ink);
  min-width: 260px;
  max-width: min(420px, 92vw);
}
.about::backdrop { background: rgba(0, 0, 0, .45) }
.about-mark { display: block; height: 26px; width: auto; margin-bottom: 14px }
/* the dialog is a light panel, not the header */
.about .wordmark-text { fill: var(--ink) }
.about h2 { margin: 0 0 12px; font-size: 15px; letter-spacing: .01em }
.about dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 14px;
  margin: 0 0 16px;
  font-size: 13px;
}
.about dt { color: var(--muted) }
.about dd { margin: 0; font-variant-numeric: tabular-nums }
.about dd code { font-size: 12px; letter-spacing: .04em }
.about .about-dev { grid-column: 1 / -1; color: var(--muted); line-height: 1.4 }

/* The disclaimers. Quiet, but above the Close button rather than under
   it: they are the last thing read, not a footnote to the counts. */
.about-note-title {
  margin: 0 0 6px;
  font-size: 12px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}
.about-note {
  margin: 0 0 12px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--muted);
}

/* Closing takes the theme's highlight, like a selected chip: the panel
   is the theme's own surface, and a white button on the dark theme was
   the same colour as the panel behind it. */
.about .clear {
  background: var(--chip-on);
  border-color: var(--chip-on);
  color: var(--on-chip-on);
}
.about .clear:hover { filter: brightness(.92); border-color: var(--chip-on); color: var(--on-chip-on) }

/* Back to top. A phone has no Home key and the grid runs long; it
   appears once there is enough behind you to make it worth a tap. */
.to-top {
  position: fixed;
  right: 14px;
  bottom: 14px;
  z-index: 20;
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--edge);
  border-radius: 50%;
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 2px 10px rgba(0, 0, 0, .18);
  cursor: pointer;
}
.to-top svg { width: 20px; height: 20px; display: block }
.to-top:hover { border-color: var(--brand); color: var(--brand) }

@media (max-width: 760px) {
  .to-top:not([hidden]) { display: inline-flex }
  .about { margin-top: 56px }
}

/* ---------------------------------------------------------- grid & card */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 16px;
}

.card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
  display: flex;
  flex-direction: column;

  /* Skip the cards nobody is looking at. The grid runs to 2936 cards
     and 90 000 elements, and every one of them had to be restyled to
     change the theme -- 180-212 ms of style recalculation for a change
     that is paint and nothing else. Off-screen cards are now skipped
     until they are scrolled to, which takes that to 16-25 ms.
     The browser still knows they are there: it lays out an estimate,
     and `auto` remembers each card's real height once it has been
     rendered, so the scrollbar does not move under the reader (the page
     measured 83 462 px before and 83 337 px after). scrollIntoView and
     find-in-page reveal a skipped card the same way scrolling does. */
  content-visibility: auto;
  /* Measured, not guessed: card heights run p25 422, median 436,
     p75 441, p95 463. */
  contain-intrinsic-size: auto 430px;
}
.card.nopdf { opacity: .72 }

/* A limited edition is a keepsake entry: it has a photograph and no
   manual, which would otherwise read as a broken record. The accent
   says it is meant to be like that, and cancels the fading that marks
   an entry whose PDF has gone missing upstream. */
/* A limited edition has a photograph and no manual, which would read as
   a broken record; the accent says it is meant to be that way, and
   cancels the fading that marks a PDF gone missing upstream. */
.card.limited-card { opacity: 1; border-color: #c8a02e }

/* A model line with nothing in it: the placeholder should read as an
   absence, not as a code. */
.model.none { color: var(--ghost); font-style: italic }

/* What an accessory attaches to, or what attaches to a tool: the same
   shape as the IANs badge, since it is the same kind of pointer. */
.ians.for { font-weight: 400 }
/* a card showing a revision other than the one the filter matched */
.card.swapped { box-shadow: 0 0 0 2px var(--ink) }

.shot { position: relative; display: block }

/* The cover's frame, so the loading mark can sit behind the image and be
   clipped by the same box. */
.shot-frame {
  position: relative;
  display: block;
  background: var(--thumb);
  overflow: hidden;
  /* The divider belongs to the panel, not to the picture inside it: on
     the image it left a line with white still below, and it took the
     card's own rule colour -- dark slate on the dark theme, which read
     as a black line across every cover. Tied to the panel it is always
     a soft edge, whatever the theme. */
  border-bottom: 1px solid rgba(0, 0, 0, .1);
}

/* The logo's three stripes, rising through the frame and leaving at the
   top. Only shown while the cover is still loading. */
.stripes {
  position: absolute;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  gap: 5px;
  pointer-events: none;
}
.shot-frame.loading .stripes { display: flex }

.stripes i {
  width: 7px;
  height: 26px;
  background: var(--lidl-stripe, #cd2828);
  transform: skewX(-18deg) translateY(150%);
  animation: stripe-rise 1.6s cubic-bezier(.33, 0, .2, 1) infinite;
}
.stripes i:nth-child(2) { animation-delay: .13s }
.stripes i:nth-child(3) { animation-delay: .26s }

@keyframes stripe-rise {
  0%   { transform: skewX(-18deg) translateY(150%); opacity: 0 }
  22%  { transform: skewX(-18deg) translateY(0);    opacity: 1 }
  55%  { transform: skewX(-18deg) translateY(0);    opacity: 1 }
  82%  { transform: skewX(-18deg) translateY(-150%); opacity: 0 }
  100% { transform: skewX(-18deg) translateY(-150%); opacity: 0 }
}

/* A cover that is still loading should not show as a white gap. */
.shot-frame.loading .thumb { opacity: 0 }
.shot-frame .thumb { transition: opacity .18s }

@media (prefers-reduced-motion: reduce) {
  .stripes i { animation: none; transform: skewX(-18deg); opacity: .35 }
}

/* The same three stripes at page size, over everything, while the data
   is on the wire. It paints the page's own background rather than a
   scrim: the grid behind it is empty at that point, and a translucent
   overlay would only show that. */
.splash {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  background: var(--page);
  transition: opacity .42s ease;
}
.splash.gone { opacity: 0; pointer-events: none }
.splash .stripes { display: flex; gap: clamp(16px, 4.8vmin, 48px) }
/* vmin, so the mark is as big on a phone in landscape as in portrait,
   and the gap keeps the logo's own ratio to the bar width (~0.45). */
.splash .stripes i {
  width: clamp(34px, 10.5vmin, 104px);
  height: clamp(130px, 39vmin, 390px);
}

@media (prefers-reduced-motion: reduce) {
  /* No rise, but still the mark rather than three faded ghosts: this is
     the whole page, not a thumbnail waiting behind a card. */
  .splash .stripes i { opacity: 1 }
}

.thumb {
  width: 100%;
  height: 190px;
  object-fit: contain;
  background: var(--thumb);
  border: 0;
  cursor: pointer;
}
.missing {
  position: relative;
  border-bottom: 1px solid rgba(0, 0, 0, .1);
  height: 190px;
  display: grid;
  place-items: center;
  color: var(--dim);
  background: var(--wash);
  font-size: 13px;
  overflow: hidden;
}
.missing span { position: relative; z-index: 1 }

/* the same mark as the loading state, held still and faint */
.stripes.still { display: flex }
.stripes.still i {
  animation: none;
  transform: skewX(-18deg);
  opacity: .13;
  height: 46px;
  width: 12px;
}
/* a cover-less card still opens its manual */
.missing.openable { cursor: pointer }
.card:hover .missing.openable { color: var(--ink) }

.info { padding: 12px; flex: 1 }
.ian { font-size: 13px; font-weight: 700; margin-bottom: 5px }
/* the article number copies itself; a button so it is reachable by
   keyboard, painted as the plain text it replaced */
.ian-code {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  border-radius: 3px;
}
.ian-code:hover .ian-label { text-decoration: underline; text-underline-offset: 2px }
.ian-code.copied { color: var(--ok) }
/* the hold that was refused: a word, not an error -- the number still
   reached the clipboard */
.ian-code.denied { color: var(--warn) }
/* and the card that just left the comparison: quieter than the tick,
   since nothing was gained -- something was put back */
.ian-code.released { color: var(--muted) }

/* the icon appears with the card's other hover controls, and holds its
   width while hidden so the date beside it does not shift */
.copy-icon {
  width: 15px;
  height: 15px;
  margin-left: 6px;
  vertical-align: -3px;
  opacity: 0;
  transition: opacity .12s;
}
.card:hover .copy-icon,
.ian-code:focus-visible .copy-icon,
.ian-code.copied .copy-icon { opacity: 1 }
.ian-code:hover .copy-icon { opacity: 1 }

.copy-icon .glyph-done,
.copy-icon .glyph-held { display: none }
.ian-code.copied .glyph-copy,
.ian-code.held .glyph-copy { display: none }
.ian-code.copied .glyph-done { display: inline }

/* held for comparison: the mark stays on show without a hover, since a
   card in the comparison must not look like one that is merely near the
   pointer. The flash of "Copied" outranks it for its second and a bit. */
.ian-code.held .copy-icon { opacity: 1; color: var(--link) }
.ian-code.held .glyph-held { display: inline }
/* Hovering a held mark empties the box: the tick is what the click will
   take away, so showing it gone is the clearest way to say so. The whole
   label is the click target, not just the icon, so the whole label is
   what arms it. */
.ian-code.held:hover .glyph-held path { display: none }
.ian-code.copied .glyph-held { display: none }
.ian-code.copied .copy-icon { color: inherit }

/* the badge that runs the comparison, sized like the "N IANs" one it
   stands beside -- it does the same thing with a different list */
.compare-ian {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-weight: 700;
}
.compare-icon { width: 15px; height: 15px }
.model {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: .02em;
  margin-bottom: 3px;
}
/* a set lists its parts; the one the card is grouped by leads */
.model-set { line-height: 1.45 }
.model-part { white-space: nowrap }
.model-part:not(.lead) { font-weight: 500; opacity: .8 }

.name { font-size: 14px; line-height: 1.35 }
.date { float: right; font-weight: 400; color: var(--dim) }

.actions { padding: 0 12px 12px }
.langs { display: flex; gap: 6px }
.langs .button { flex: 1 }

.button {
  display: block;
  padding: 9px;
  text-align: center;
  text-decoration: none;
  color: var(--paper);
  background: var(--brand);
  border-radius: 6px;
  font-size: 13px;
}
.button:hover { background: var(--brand-strong) }
.button.disabled { background: var(--edge); color: var(--muted); cursor: default }
.button.disabled:hover { background: var(--edge) }


/* what the box contains, and what powers it */
.kind-row { margin-top: 6px; display: flex; align-items: center; gap: 2px }
.kindtag {
  font-size: 12px;
  color: var(--link);
  background: var(--link-bg);
  border-radius: 4px;
  padding: 2px 7px;
  display: inline-block;
}
.kindtag.none { color: var(--ghost); background: var(--wash) }
.kind-row .change { opacity: 0; transition: opacity .12s }
.card:hover .kind-row .change { opacity: 1 }

.power {
  margin-top: 7px;
  font-size: 12px;
  color: var(--ok);
  background: var(--ok-bg);
  border-radius: 4px;
  padding: 2px 7px;
  display: inline-block;
}
.power.unsure { color: var(--warn); background: var(--warn-bg) }

/* Capacity, built exactly like the power tag beside it, and shaded by
   size: a bigger pack is a warmer badge, so the range of a family reads
   at a glance without anyone having to hunt for the number. */
.ah {
  margin-left: 5px;
  display: inline-block;
  font-size: 12px;
  padding: 2px 7px;
  border-radius: 4px;
  color: #4a5568;
  background: #eef1f4;
  white-space: nowrap;
}
.ah[data-ah="2"]   { color: #4a5568; background: #eef1f4 }
.ah[data-ah="2.5"] { color: #1f5f70; background: #e2eff2 }
.ah[data-ah="3"]   { color: #1f5f70; background: #e2eff2 }
.ah[data-ah="4"]   { color: #1e4b32; background: #dfeee5 }
.ah[data-ah="5"]   { color: #5c5a10; background: #f2f0d4 }
.ah[data-ah="6"]   { color: #7a5c00; background: #fdf3d8 }
.ah[data-ah="8"]   { color: #8a4b10; background: #fbe7d5 }
.ah[data-ah="10"]  { color: #99401a; background: #fbdfd2 }
.ah[data-ah="12"]  { color: #8a2020; background: #f7dcdc }
.power.confirmed { color: var(--confirmed); background: var(--confirmed-bg) }

/* revision lineage */
.revs { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 4px; align-items: center }
.revs-label {
  font-size: 10px;
  color: var(--ghost);
  text-transform: uppercase;
  letter-spacing: .05em;
  margin-right: 2px;
}
.rev {
  font: inherit;
  font-size: 11px;
  line-height: 1;
  padding: 3px 6px;
  border: 1px solid var(--edge);
  border-radius: 4px;
  background: var(--paper);
  color: var(--muted);
  cursor: pointer;
}
.rev:hover { border-color: var(--ink); color: var(--ink) }
.rev.here { background: var(--strong); border-color: var(--strong); color: var(--on-strong); cursor: default }
/* exists, but not in the language you filtered to */
.rev.elsewhere { border-style: dashed; color: var(--ghost) }
.rev.elsewhere:hover { border-color: var(--muted); color: var(--ink) }

/* ------------------------------------------------ badges and series marks */

.badge {
  display: inline-block;
  margin-left: 0;
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--alert);
  color: var(--paper);
  font-size: 11px;
  vertical-align: 1px;
}
/* Gold, because that is what the finish is -- fixed rather than themed,
   since the colour is the whole point. Written after `.badge` and with
   its class, because equal specificity is decided by order and the
   plain badge would otherwise paint over it. */
.badge.limited-badge {
  background: linear-gradient(180deg, #e6c65e, #b8912a);
  color: #2a2000;
  border-color: #a5801f;
  text-shadow: 0 1px 0 rgba(255, 255, 255, .35);
}

/* The series colours are mid-tone in both themes, so their ink is fixed
   white rather than --paper: on the dark theme --paper is the charcoal
   surface, which left a compressor badge at 2.2:1 against its own fill.
   The chips that carry these colours read from the same token. */
.badge.series { background: var(--brand); color: var(--on-series) }
.badge.series.performance { background: var(--series-performance) }
.badge.series.toy { background: var(--series-toy) }
.badge.series.garden { background: var(--series-garden) }
.badge.series.washer { background: var(--series-washer) }
.badge.series.compressor { background: var(--series-compressor) }

/* the wordmarks, lifted as vector art from manual covers */
.logo { height: 9px; width: auto; vertical-align: 1px; margin-left: 7px; filter: var(--logo-filter) }
.chip-logo { height: 9px; margin-left: 0; vertical-align: middle; filter: var(--logo-filter) }
/* the mark is black artwork, so invert it on a selected (dark) chip */
.chip.on .chip-logo { filter: var(--logo-filter-on) }

/* Marks get a row of their own: three of them plus their add-buttons no
   longer fit beside the IAN. The row keeps its height whether or not the
   add-buttons are showing, so revealing them on hover shifts nothing. */
.series-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 3px;
  margin: 0 0 5px;
  min-height: 17px;
}
.series-controls > * { margin-left: 0 }

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

.verdict { margin-left: 6px; white-space: nowrap; display: inline-block }

.change {
  margin-left: 4px;
  vertical-align: 2px;
  font: inherit;
  font-size: 12px;
  line-height: 1;
  padding: 3px 7px;
  border: 1px solid var(--edge);
  border-radius: 5px;
  background: var(--paper);
  color: var(--dim);
  cursor: pointer;
}
.change:hover { border-color: var(--ink); color: var(--ink) }
.change.failed { border-color: var(--alert); color: var(--alert) }
/* a set typed with something that is not a model code */
.namefield.failed { border-color: var(--alert); color: var(--alert) }

.confirm {
  margin-left: 0;
  vertical-align: 2px;
  font: inherit;
  font-size: 12px;
  line-height: 1;
  padding: 3px 8px;
  border: 1px solid var(--edge);
  border-radius: 5px;
  background: var(--paper);
  color: var(--ok);
  cursor: pointer;
}
.confirm:hover { background: var(--ok); border-color: var(--ok); color: var(--paper) }
.confirm.failed { border-color: var(--alert); color: var(--alert) }

.picker {
  font: inherit;
  font-size: 12px;
  padding: 2px 4px;
  border: 1px solid var(--ink);
  border-radius: 5px;
  max-width: 190px;
}

.rename { margin-left: 5px; vertical-align: 1px; opacity: 0; transition: opacity .12s }
.card:hover .rename { opacity: .7 }
.card:hover .rename:hover { opacity: 1 }
.namefield {
  font: inherit;
  font-size: 14px;
  width: 100%;
  padding: 3px 5px;
  border: 1px solid var(--ink);
  border-radius: 5px;
}

.unseries {
  margin-left: 4px;
  font: inherit;
  font-size: 11px;
  line-height: 1;
  padding: 2px 5px;
  border: 1px solid var(--edge);
  border-radius: 4px;
  background: var(--paper);
  color: var(--faint);
  cursor: pointer;
  vertical-align: 1px;
}
.unseries:hover { border-color: var(--alert); color: var(--alert) }
/* the add control stays out of the way until the card is hovered */
.unseries.add { display: none }
.card:hover .unseries.add { display: inline-block; opacity: .75; transition: opacity .12s }
.card:hover .unseries.add:hover { opacity: 1 }
.unseries.split { opacity: 0; transition: opacity .12s }
.card:hover .unseries.split { opacity: .8 }
.card:hover .unseries.split:hover { opacity: 1; border-color: var(--alert); color: var(--alert) }
.unseries.undo { color: var(--muted) }
.unseries.undo:hover { border-color: var(--ink); color: var(--ink) }
.unseries.failed { border-color: var(--alert); color: var(--alert) }

.ians {
  /* spaced by a text space, not a margin: a margin would indent it when
     the IAN line wraps and the badge starts the next one */
  margin-left: 0;
  font: inherit;
  /* the same size and padding as the tags below it -- .kindtag and
     .power -- so the card's small type reads as one family */
  font-size: 12px;
  line-height: 1;
  padding: 2px 7px;
  border: 1px solid var(--link-edge);
  border-radius: 4px;
  background: var(--link-bg);
  color: var(--link);
  cursor: pointer;
  vertical-align: 1px;
}
.ians:hover { border-color: var(--link) }

/* the cover page/region steppers, revealed on hover */
.page {
  position: absolute;
  right: 6px;
  bottom: 6px;
  font: inherit;
  font-size: 11px;
  line-height: 1;
  padding: 3px 7px;
  border: 1px solid var(--edge);
  border-radius: 5px;
  background: var(--paper);
  color: var(--muted);
  cursor: pointer;
  opacity: 0;
  transition: opacity .12s;
}
.card:hover .page { opacity: 1 }
.page:hover { border-color: var(--ink); color: var(--ink) }
.page.crop { right: 44px }
.page.failed { border-color: var(--alert); color: var(--alert) }

/* ----------------------------------------------------------- responsive */

/* Narrow screens. Two things dominate: the filter rows are a two-column
   grid that has no room for its label column, and the header is sticky --
   at full height it covered 750 px of an 813 px phone screen. */
/* Only a phone gets the disclosure; on a wide screen the filters are
   always open and the button never exists. */
.filter-toggle { display: none }

@media (max-width: 760px) {
  .options-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    line-height: 1;
    padding: 0;
    border: 1px solid var(--edge);
    border-radius: 7px;
    background: var(--paper);
    color: var(--chip-ink);
    cursor: pointer;
    margin-bottom: 8px;
  }
  .options-toggle svg { width: 17px; height: 17px; display: block }
  .options-toggle.on { background: var(--chip-on); border-color: var(--chip-on); color: var(--on-chip-on) }
  .switches { display: none; width: 100% }
  /* A row per switch rather than one wrapping line: theme and language
     are separate choices, and left to wrap they broke wherever the
     translated labels happened to run out of width -- "Teema" and
     "Kieli" fit on one line where "Theme" and "Language" did not. Two
     columns keep the labels aligned down the left. */
  header.options-open .switches {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 8px 10px;
  }

  .filter-toggle { display: inline-flex }
  .filter-bar { justify-content: space-between; margin-top: 10px; margin-bottom: 0 }
  .facets { display: none }
  header.filters-open .facets { display: flex }

  header {
    /* let the filters scroll away instead of pinning them over the grid */
    position: static;
    padding: 12px;
  }

  .topline { flex-wrap: wrap; gap: 8px }
  .brand { margin-bottom: 8px }
  .brand .wordmark { height: 18px }
  .brand-word { font-size: 18px }
  .switches { margin-bottom: 8px }

  .controls { gap: 8px }
  #search { min-width: 0; max-width: none; flex: 1 1 100% }
  /* the sort control keeps its own width; only the search box grows */
  input, select { padding: 9px 10px }

  /* the label goes above its chips rather than beside them */
  .facet { grid-template-columns: 1fr; gap: 4px }
  .facet-name { padding-top: 0 }
  .facets { margin-top: 10px; padding-top: 10px; gap: 12px }
  /* no room to float it on a phone: let it sit under the rows */
  #clearHost { position: static; margin-top: -4px }

  main { padding: 16px 12px 40px }
}

@media (max-width: 600px) {
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px }
  .thumb, .missing { height: 150px }
}

/* Phones: one column reads better than two 150 px-wide cards. */
@media (max-width: 420px) {
  .grid { grid-template-columns: 1fr }
  .thumb, .missing { height: 200px }
}
