/* ---------------------------------------------------------------------------
   catalogue.css - a ROUTE stylesheet, not part of the always-loaded core.

   Browsing furniture: the search field, the filter sheet and the theme tiles.

   Loaded by: /search, /minifigs, /parts, /themes, /themes/{ThemeId} - and by /dev/gallery, which renders everything.
   Linked from those pages' own markup (<RouteStylesheets Sheets="..." />), so
   it arrives AFTER tokens/reset/app/components: at equal specificity a rule
   here beats the same selector in the core sheet, which reverses the order
   these rules had inside components.css. Why the split exists, and what a page
   that links the wrong set breaks, is in
   docs/architecture/performance-budgets.md.
   --------------------------------------------------------------------------- */

/* =========================================================================
   SearchField (§5).
   ========================================================================= */

.bd-search-form {
  margin: var(--bd-space-4) 0;
}

.bd-search-field {
  display: flex;
  align-items: center;
  gap: var(--bd-space-2);
  min-height: 44px;
  padding-inline: var(--bd-space-3);
  border-radius: var(--bd-radius-md);
  background: var(--bd-color-surface-raised);
  border: 1px solid var(--bd-color-border);
}

.bd-search-field:focus-within {
  border-color: var(--bd-color-focus-ring);
}

.bd-search-field__icon {
  width: 20px;
  height: 20px;
  flex: none;
  color: var(--bd-color-muted);
}

.bd-search-field__input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  border: none;
  background: transparent;
  font-family: var(--bd-font-body);
  font-size: var(--bd-type-body-size);
  line-height: var(--bd-type-body-line);
  color: var(--bd-color-on-surface);
}

.bd-search-field__input::placeholder {
  color: var(--bd-color-muted);
}

.bd-search-field__input:focus-visible {
  outline: 3px solid var(--bd-color-focus-ring);
  outline-offset: 2px;
}

.bd-search-field__filter {
  position: relative;
}

.bd-search-field__filter-count {
  position: absolute;
  inset: 0 0 auto auto;
  min-width: 16px;
  padding: 2px;
  border-radius: var(--bd-radius-pill);
  background: var(--bd-color-primary);
  color: var(--bd-color-on-primary);
  font-size: var(--bd-type-label-size);
  line-height: var(--bd-type-label-line);
}
.bd-filter-sheet__title,
.bd-theme-tile__name {
  font-family: var(--bd-font-display);
  font-weight: 600;
  font-size: var(--bd-type-title-size);
  line-height: var(--bd-type-title-line);
}

/* =========================================================================
   FilterSheet (§5) — issue #88's browse-and-filter chrome. Bottom sheet
   (--bd-radius-xl top corners) on compact/sm, side sheet on md+; grouped
   filter chips (the caller's ChildContent, built from the same
   .bd-toggle-chip-group pattern as ConditionEditor's flags) plus a sticky
   "Apply (N results)" primary button. Never applies filters live - only
   the Apply tap fires OnApply; the scrim or the close button fires
   OnDismiss instead, discarding whatever was toggled inside.

   Solid --bd-color-surface-overlay fill, matching ConfirmSheet's own
   choice immediately above rather than <GlassSurface> - one sheet
   pattern, one fill, and only one component to re-verify against
   TokenContrastTests if that ever changes.
   ========================================================================= */

.bd-filter-sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--bd-z-modal);
  display: flex;
  flex-direction: column;
  gap: var(--bd-space-3);
  max-height: min(80vh, 640px);
  padding: var(--bd-space-3) var(--bd-space-5)
           calc(var(--bd-space-5) + var(--bd-safe-area-bottom));
  border-radius: var(--bd-radius-xl) var(--bd-radius-xl) 0 0;
  background: var(--bd-color-surface-overlay);
  box-shadow: var(--bd-shadow-4);
  animation: bd-filter-sheet-in-bottom var(--bd-duration-medium) var(--bd-ease-emphasized);
}

.bd-filter-sheet__handle {
  align-self: center;
  width: 36px;
  height: 4px;
  flex: none;
  border-radius: var(--bd-radius-pill);
  background: var(--bd-color-border-strong);
}

.bd-filter-sheet__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--bd-space-2);
}

.bd-filter-sheet__body {
  display: flex;
  flex-direction: column;
  gap: var(--bd-space-4);
  overflow-y: auto;
}

.bd-filter-sheet__footer {
  flex: none;
}

.bd-filter-sheet__apply {
  width: 100%;
}

@media (min-width: 600px) {
  .bd-filter-sheet {
    left: auto;
    border-radius: var(--bd-radius-xl) var(--bd-radius-xl) 0 0;
  }}

/* md+ (§4): a side sheet rather than a bottom sheet - full height, anchored
   to the trailing edge, rounded only on the leading corners so it reads as
   a panel sliding in rather than a bottom sheet stretched tall. */
@media (min-width: 905px) {
  .bd-filter-sheet {
    top: 0;
    left: auto;
    right: 0;
    bottom: 0;
    max-height: none;
    width: min(400px, 100%);
    padding: var(--bd-space-5);
    border-radius: var(--bd-radius-xl) 0 0 var(--bd-radius-xl);
    animation: bd-filter-sheet-in-side var(--bd-duration-medium) var(--bd-ease-emphasized);
  }

  .bd-filter-sheet__handle {
    display: none;
  }}

@media (prefers-reduced-motion: reduce) {
  .bd-filter-sheet {
    animation: bd-toast-in-reduced var(--bd-duration-instant) linear;
  }}
.bd-theme-tile__meta {
  font-family: var(--bd-font-body);
  font-size: var(--bd-type-caption-size);
  line-height: var(--bd-type-caption-line);
  color: var(--bd-color-muted);
}

.bd-theme-tile {
  display: flex;
  flex-direction: column;
  border-radius: var(--bd-radius-lg);
  background: var(--bd-color-card-surface);
  box-shadow: var(--bd-shadow-1);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
}

/* The studded brick. Two studs rather than BrickRow's one, because this one
   is wide: the silhouette has to read as a brick at a glance rather than as
   a coloured bar with a bump. They are drawn with box-shadow on a single
   pseudo-element so the tile needs no extra markup — a screen reader meets
   the name and the counts and nothing else. */
.bd-theme-tile__brick {
  position: relative;
  display: block;
  height: 28px;
  margin: var(--bd-space-3) var(--bd-space-3) var(--bd-space-2);
  border-radius: var(--bd-radius-sm);
  background: var(--bd-color-border-strong);
}

.bd-theme-tile__brick::before {
  content: "";
  position: absolute;
  top: -5px;
  left: 22%;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: inherit;
  box-shadow: 34px 0 0 0 currentcolor;
}

.bd-theme-tile__body {
  display: flex;
  flex-direction: column;
  gap: var(--bd-space-1);
  padding: 0 var(--bd-space-4) var(--bd-space-4);
}

/* `color` as well as `background`, so the second stud's box-shadow above
   (which reads currentcolor) tracks the hue without six more rules. The
   inherited text colour is restored on the body, which is the only part
   that carries any. */
.bd-theme-tile--red .bd-theme-tile__brick { background: var(--bd-accent-red); color: var(--bd-accent-red); }
.bd-theme-tile--orange .bd-theme-tile__brick { background: var(--bd-accent-orange); color: var(--bd-accent-orange); }
.bd-theme-tile--yellow .bd-theme-tile__brick { background: var(--bd-accent-yellow); color: var(--bd-accent-yellow); }
.bd-theme-tile--green .bd-theme-tile__brick { background: var(--bd-accent-green); color: var(--bd-accent-green); }
.bd-theme-tile--blue .bd-theme-tile__brick { background: var(--bd-accent-blue); color: var(--bd-accent-blue); }
.bd-theme-tile--purple .bd-theme-tile__brick { background: var(--bd-accent-purple); color: var(--bd-accent-purple); }

/* =========================================================================
   The browse surface (ui-refresh components.md §4) — the region /search,
   /minifigs and a theme's set list lay their results out in.

   EVERY RULE BELOW IS WRITTEN FROM THE OUTSIDE IN, and that is the whole
   reason this section is in a route sheet at all. `.bd-catalogue
   .bd-card-grid` is a rule *about* the catalogue, so StylesheetMap's
   leftmost-block reducer assigns it here and `bd-card-grid` stays in the
   always-loaded core, where the collection and the home launcher go on
   reading the version they always did. Spelt the other way round — a
   `.bd-card-grid--browse` modifier, or these declarations pushed into
   components.css — the catalogue's density decisions would download on
   every page in the application, and ComponentBlockIsRenderedTests would
   start demanding that Home link this sheet. Guard:
   Design.CatalogueBrowseLayoutTests.
   ========================================================================= */

.bd-catalogue {
  display: flex;
  flex-direction: column;
  gap: var(--bd-space-4);
}

/* components.md §4.4: the count on the left, and (once the catalogue API can
   answer per-facet counts) the Filters button and the sort select on the
   right. It wraps rather than scrolling because German runs about 30% longer
   and a control scrolled out of view is a control nobody uses. */
.bd-catalogue__toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--bd-space-3);
  padding-bottom: var(--bd-space-3);
  border-bottom: 1px solid var(--bd-color-border);
}

.bd-catalogue__count {
  margin: 0;
  color: var(--bd-color-muted);
  font-variant-numeric: tabular-nums;
}

/* THE COLUMN COUNT IS ARITHMETIC, NOT A BREAKPOINT LADDER. A browse grid is
   read by scanning, so the phone wants two cards abreast rather than one
   tall one — but two 168px tracks plus the gap do not fit inside the shell's
   content box at the 360px viewport NoHorizontalScrollTests drives in
   German, and a `repeat(2, …)` there would fit them by overflowing. auto-fill
   answers both cases from the same declaration: one column below ~380px, two
   above, three and four as the space appears. `min(100%, 168px)` rather than
   a bare 168px because a track wider than its container is still a track
   wider than its container. components.md §3 specifies exactly this. */
.bd-catalogue .bd-card-grid {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 168px), 1fr));
  gap: var(--bd-space-3);
}

@media (min-width: 905px) {
  .bd-catalogue .bd-card-grid {
    gap: var(--bd-space-4);
  }}

/* Browse density. The same card carries a collection row and a scan result at
   full width elsewhere; at 168px it needs less padding and a title one step
   down the scale. min-width on the card itself because a grid track floored
   at min(100%, 168px) is not floored at zero, so a long unbroken word inside
   a flex column could still push the track wider than its share. */
.bd-catalogue .bd-set-card {
  min-width: 0;
}

.bd-catalogue .bd-set-card__body {
  padding: var(--bd-space-3);
}

.bd-catalogue .bd-set-card__title {
  font-size: var(--bd-type-body-size);
  line-height: var(--bd-type-body-line);
}

.bd-catalogue .bd-set-card__meta {
  gap: var(--bd-space-2);
  font-size: var(--bd-type-caption-size);
  line-height: var(--bd-type-caption-line);
}

/* The action row stacks until there is room for it to sit in a line. Two 44px
   buttons side by side inside a 168px card is how "Zur Sammlung hinzufügen"
   overflows its own card; stretched full-width they stay 44px tall and
   legible, which is what components.md §3 means by an action row that is
   always visible on touch rather than hover-revealed. */
.bd-catalogue .bd-set-card__body > .bd-cluster {
  flex-direction: column;
  align-items: stretch;
}

@media (min-width: 1240px) {
  .bd-catalogue .bd-set-card__body > .bd-cluster {
    flex-direction: row;
    align-items: center;
  }}

@keyframes bd-filter-sheet-in-bottom {
  from { opacity: 0; transform: translateY(24px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes bd-filter-sheet-in-side {
  from { opacity: 0; transform: translateX(24px); }
  to { opacity: 1; transform: translateX(0); }
}
