/**
 * MCB Productions — Inventory Page
 *
 * Graphite carries the catalog itself. The category filter becomes a
 * seated rail with engraved divisions and right-aligned tabular counts:
 * the readout panel from DESIGN.md section 5 turned into navigation, so
 * what is filtered is always legible as equipment rather than as a set of
 * pressed buttons. The results sit on paper beside it as wide rows.
 *
 * Continuous with About and Production, and closest to the customer portal
 * to come, where a persistent rail of readings beside a list is the shape
 * every screen wants. Two alternatives were built and rejected; see
 * docs/briefs/2026-07-21-inventory-product.md.
 *
 * Layout only. Everything this file draws with is defined in tokens.css and
 * base.css.
 */

/* =====================================================================
   1. OPENING
   ===================================================================== */

.inv-open {
  padding-block: var(--space-4xl) var(--space-2xl);
}

.inv-open-grid {
  display: grid;
  gap: var(--space-2xl);
}

@media (min-width: 900px) {
  .inv-open-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-5xl);
    align-items: start;
  }
}

.inv-eyebrow {
  margin-bottom: var(--space-lg);
  color: var(--ink-muted);
}

.inv-display {
  max-width: 14ch;
}

/* The masthead photograph, under the display and above nothing else. It
   fills the space the short heading leaves in this column rather than
   adding height to the page, so the catalog is no further down the
   screen than it was.

   A .plate-half from base.css §8.1 — the frame, the caption, and the
   role width are the component. The 2:1 band is baked into the source by
   scripts/bake-crops.py; 2:1 is the widest this frame survives, and
   nothing here re-crops it. */
.inv-masthead-plate {
  margin-top: var(--space-2xl);
}

.inv-open-aside {
  display: grid;
  gap: var(--space-xl);
}

.inv-lede {
  color: var(--ink-muted);
}

.inv-pricing-note {
  max-width: 52ch;
  padding-top: var(--space-lg);
  border-top: 1px solid var(--paper-300);
}

.inv-pricing-head {
  margin: 0 0 var(--space-sm);
  color: var(--ink-muted);
}

.inv-pricing-body {
  margin: 0;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--ink-muted);
  text-wrap: pretty;
}

/* =====================================================================
   2. THE GRID
   ===================================================================== */

.inv-body {
  padding-bottom: var(--space-4xl);
}

.inv-grid {
  display: grid;
  gap: var(--space-2xl);
}

@media (min-width: 900px) {
  .inv-grid {
    grid-template-columns: 300px minmax(0, 1fr);
    gap: var(--space-3xl);
    align-items: start;
  }
}

/* =====================================================================
   3. THE RAIL
   A seated graphite faceplate. Bounded on all four sides with paper
   visible above, below, and beside it, which is what keeps graphite an
   object placed on the page rather than a dark column.
   ===================================================================== */

.inv-rail-sticky {
  display: block;
}

@media (min-width: 900px) {
  /* The grid sets align-items: start so the results column is not
     stretched, but that also collapses this column to the rail's own
     height — leaving a sticky child with zero travel and a rail that
     silently does not follow the page. The column has to span the row for
     the sticky to have anywhere to go. */
  .inv-rail-column {
    align-self: stretch;
  }

  .inv-rail-sticky {
    position: sticky;
    top: var(--space-xl);
  }
}

.inv-rail {
  background: var(--graphite-900);
  border-radius: var(--radius-panel);
  box-shadow: var(--shadow-seated);
  padding: var(--space-xl) var(--space-xl) var(--space-sm);
  /* The row hover and active states extend their fill sideways with a
     hard box-shadow to reach the panel's inner edge. A child's shadow is
     not clipped by the parent's radius, so the last row's fill would
     square off over the rounded bottom corners without this. */
  overflow: hidden;
}

.inv-rail-head {
  margin: 0 0 var(--space-md);
  padding: 0;
  color: var(--graphite-300);
}

.inv-rail-head--categories {
  margin-top: var(--space-xl);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--graphite-700);
}

/* Mode switch, seated into the faceplate. */
.inv-mode {
  margin: 0;
  padding: 0;
  border: 0;
}

.inv-mode-switch {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  padding: 3px;
  background: var(--graphite-800);
  border: 1px solid var(--graphite-700);
  border-radius: var(--radius-control);
}

.inv-mode-btn {
  min-height: 44px;
  padding-inline: var(--space-sm);
  background: transparent;
  border: 0;
  border-radius: 3px;
  color: var(--graphite-300);
  font-family: var(--font-sans);
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  letter-spacing: var(--type-label-tracking);
  text-transform: uppercase;
  cursor: pointer;
  transition:
    background-color var(--duration-hover) var(--ease-out-quart),
    color var(--duration-hover) var(--ease-out-quart),
    transform var(--duration-press) var(--ease-out-quart);
}

.inv-mode-btn:hover {
  color: var(--paper-000);
}

.inv-mode-btn:active {
  transform: translateY(1px);
}

.inv-mode-btn.is-active {
  background: var(--signal-ink);
  color: var(--paper-000);
  box-shadow: var(--bevel);
}

/* The readout rows. The whole row is the target, the divisions are
   engraved as rules rather than raised as boxes, and the count goes
   Signal Bright when the row is live — the readout-panel behaviour from
   base.css, applied to a control rather than a link. */
.inv-rail-list {
  display: grid;
}

.inv-rail-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-lg);
  width: 100%;
  padding-block: var(--space-lg);
  background: transparent;
  border: 0;
  border-top: 1px solid var(--graphite-700);
  color: var(--paper-200);
  text-align: left;
  cursor: pointer;
  transition:
    background-color var(--duration-hover) var(--ease-out-quart),
    color var(--duration-hover) var(--ease-out-quart);
}

.inv-rail-row:first-child {
  border-top: 0;
}

.inv-rail-row:hover {
  background: var(--graphite-800);
  color: var(--paper-000);
  /* Extends the tint to the panel's inner edge so the row reads as a
     full-width target rather than a floating band. */
  box-shadow: -24px 0 0 var(--graphite-800), 24px 0 0 var(--graphite-800);
}

.inv-rail-name {
  color: inherit;
}

.inv-rail-count {
  color: var(--graphite-300);
  font-weight: 700;
  font-size: 1.0625rem;
  transition: color var(--duration-hover) var(--ease-out-quart);
}

.inv-rail-row:hover .inv-rail-count {
  color: var(--signal-bright);
}

/* The selected row is marked three ways — fill, an accent bar at the
   inner edge, and the count going Signal Bright — because the state must
   not depend on colour perception alone. */
.inv-rail-row.is-active {
  background: var(--graphite-800);
  color: var(--paper-000);
  box-shadow:
    -24px 0 0 var(--graphite-800),
    24px 0 0 var(--graphite-800),
    inset 3px 0 0 var(--signal-bright);
  padding-left: var(--space-md);
}

.inv-rail-row.is-active .inv-rail-count {
  color: var(--signal-bright);
}

/* =====================================================================
   4. RESULTS
   ===================================================================== */

.inv-results-head {
  display: grid;
  gap: var(--space-md);
  margin-bottom: var(--space-xl);
}

@media (min-width: 640px) {
  .inv-results-head {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    gap: var(--space-xl);
  }
}

.inv-search {
  max-width: 420px;
}

.inv-status {
  margin: 0;
  min-height: 1.4em;
  color: var(--ink-muted);
}

@media (min-width: 640px) {
  .inv-status {
    /* Sits on the field's baseline rather than on its top edge. */
    padding-bottom: var(--space-lg);
    text-align: right;
  }
}

.inv-error {
  margin: 0 0 var(--space-xl);
  padding: var(--space-lg);
  border: 1px solid var(--alert);
  border-radius: var(--radius-control);
  color: var(--alert);
  font-family: var(--font-sans);
  font-size: 1rem;
  line-height: 1.5;
}

.catalog-state {
  margin: 0;
  padding-block: var(--space-2xl);
  color: var(--ink-muted);
  font-family: var(--font-sans);
}

.inv-list {
  display: grid;
  gap: var(--space-md);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* A wide row rather than a card in a grid: at this width one item per row
   lets the plain-English sentence run at a readable measure instead of
   being cropped into a card's three lines. */
.inv-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-lg);
  padding: var(--space-lg) var(--space-xl);
  background: var(--paper-000);
  border: 1px solid var(--paper-200);
  border-radius: var(--radius-panel);
  box-shadow: var(--shadow-paper);
  text-decoration: none;
  color: inherit;
  transition:
    border-color var(--duration-hover) var(--ease-out-quart),
    box-shadow var(--duration-hover) var(--ease-out-quart),
    transform var(--duration-press) var(--ease-out-quart);
}

.inv-item:hover {
  border-color: var(--graphite-500);
  box-shadow:
    0 1px 1px oklch(18% 0.014 252 / 0.10),
    0 3px 6px oklch(18% 0.014 252 / 0.08);
}

.inv-item:active {
  transform: translateY(1px);
}

/* An item with a photograph gains a plate at the head of the row.
   160px, chosen 2026-07-21 by comparing 132 / 160 / 180 against stand-in
   photography. At 132 the photograph is a footnote to the name; at 180 it
   starts competing with the title and pushes most descriptions onto an
   extra line for nothing. 160 is where the two balance. */
.inv-item.has-plate {
  grid-template-columns: 160px minmax(0, 1fr);
  align-items: center;
}

.inv-item-plate {
  display: block;
  overflow: hidden;
  border-radius: var(--radius-hairline);
  background: var(--paper-100);
}

/* 3:2 is the ratio the admin frame dialog bakes every catalog photograph
   to, so the box and the source agree and neither value re-crops anything.
   `contain` rather than `cover` for the source that does not conform: it
   renders whole on the paper ground instead of being cropped. */
.inv-item-plate img {
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: contain;
}

.inv-item-body {
  display: block;
  min-width: 0;
}

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

.inv-item-name {
  font-size: 1.1875rem;
  transition: color var(--duration-hover) var(--ease-out-quart);
}

.inv-item:hover .inv-item-name {
  color: var(--signal-ink);
}

.inv-item-sku {
  color: var(--ink-muted);
  white-space: nowrap;
}

.inv-item-desc {
  display: block;
  max-width: 72ch;
  margin-top: var(--space-xs);
  color: var(--ink-muted);
  font-size: 1rem;
  line-height: 1.55;
  text-wrap: pretty;
}

.inv-item-price {
  display: inline-block;
  margin-top: var(--space-md);
  color: var(--ink);
  font-size: 1.0625rem;
  font-weight: 700;
}

@media (max-width: 520px) {
  .inv-item.has-plate {
    grid-template-columns: minmax(0, 1fr);
  }

  .inv-item.has-plate .inv-item-plate {
    width: 160px;
  }

  .inv-item-head {
    flex-direction: column;
    gap: var(--space-xs);
  }
}

/* =====================================================================
   5. EMPTY AND PAGINATION
   ===================================================================== */

.inv-empty {
  max-width: 56ch;
  padding-block: var(--space-4xl);
}

.inv-empty-heading {
  margin-bottom: var(--space-md);
}

.inv-empty-message {
  margin: 0 0 var(--space-xl);
  color: var(--ink-muted);
  text-wrap: pretty;
}

.inv-empty-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
}

.inv-pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  margin-top: var(--space-2xl);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--paper-300);
}

.inv-pagination-info {
  margin: 0;
  color: var(--ink-muted);
}

.inv-pagination-controls {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.inv-page-numbers {
  display: flex;
  gap: var(--space-xs);
}

.catalog-page-btn {
  min-width: 44px;
  min-height: 44px;
  padding-inline: var(--space-md);
  background: var(--paper-000);
  border: 1px solid var(--paper-300);
  border-radius: var(--radius-control);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--type-data-size);
  font-weight: var(--type-data-weight);
  font-variant-numeric: tabular-nums slashed-zero;
  cursor: pointer;
  transition:
    background-color var(--duration-hover) var(--ease-out-quart),
    border-color var(--duration-hover) var(--ease-out-quart),
    transform var(--duration-press) var(--ease-out-quart);
}

.catalog-page-btn:hover:not(:disabled) {
  background: var(--paper-100);
  border-color: var(--graphite-500);
}

.catalog-page-btn:active:not(:disabled) {
  transform: translateY(1px);
  box-shadow: var(--shadow-pressed);
}

.catalog-page-btn.is-active {
  background: var(--signal-ink);
  border-color: var(--signal-ink);
  color: var(--paper-000);
}

.catalog-page-btn:disabled {
  color: var(--ink-muted);
  background: var(--paper-100);
  cursor: not-allowed;
}

.catalog-page-step {
  font-family: var(--font-sans);
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  letter-spacing: var(--type-label-tracking);
  text-transform: uppercase;
}

/* =====================================================================
   6. CLOSE
   ===================================================================== */

.inv-close {
  padding-bottom: var(--space-5xl);
}

.inv-close-inner {
  display: grid;
  gap: var(--space-2xl);
  padding-top: var(--space-3xl);
  border-top: 1px solid var(--paper-300);
}

@media (min-width: 860px) {
  .inv-close-inner {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-4xl);
  }
}

.inv-close-heading {
  margin-bottom: var(--space-md);
}

.inv-close-body {
  max-width: 52ch;
  margin: 0;
  color: var(--ink-muted);
  text-wrap: pretty;
}

.inv-close-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
}

/* =====================================================================
   STATE FEEDBACK
   The acknowledgement register from base.css §17. The empty and error
   panels arrive by having [hidden] removed, so they animate from CSS
   alone; the result list is replaced in place and is announced by
   catalog-browse.js instead.
   ===================================================================== */

.inv-empty:not([hidden]),
.inv-error:not([hidden]) {
  animation: state-in var(--duration-state) var(--ease-out-quart);
}

/* While a filter, a search or a page change is in flight, the results
   standing on screen are the PREVIOUS answer. Holding them at full
   strength reads as "these are your results" right up until they are
   replaced, so they step back instead — and stop taking clicks, because
   a row pressed here belongs to the query that is being abandoned.

   .is-loading is set on the browse root by catalog-browse.js setBusy().
   The delay means a fast response never dims at all: the transition has
   not started by the time the class comes off again. */
.is-loading .inv-list {
  opacity: 0.45;
  pointer-events: none;
  transition: opacity var(--duration-state) var(--ease-out-quart) 120ms;
}

.inv-list {
  transition: opacity var(--duration-state) var(--ease-out-quart);
}
