/* ==========================================================================
   commerce.css — Apex Holding
   PURPOSE: the shop surface, and only the shop surface. A grid of product
   cards, each holding a 3D bottle you can turn, a name, a description, a
   price, and one button to that bottle's own checkout.

   There is nothing else to style. The basket, the quantity stepper, the review
   panel and the "price on request" treatment were all removed with the code
   that drew them: the client asked for a price and a button, so a price and a
   button is the whole of it.

   It is an EXTENSION of components.css, not a parallel system: the cards are
   .card, the buttons are .btn, and only the commerce-specific additions live
   here. Nothing in this file restyles a shared class, and nothing here touches
   the .mv__* classes cinema-viewer.css owns — a single --mv-* token override
   is the only thing this file says about the 3D stage.

   THIS IS THE MONEY SURFACE. The price is set in the display face at heading
   scale, in the world accent, over a hairline rule, with its currency code
   held small and quiet beside it — the proportions of a wine list, not of a
   shelf ticket. Everything else on the card gives way to it.

   THEMING: every colour is a --w-* semantic token, so the same markup reads as
   Greppo on the winery page and would read as any other world elsewhere, with
   no per-world rule anywhere below.

   RULES OBSERVED
     - Only --w-* semantic tokens and the scale tokens. No raw hex.
     - No !important. No selector deeper than three levels.
     - Interactive targets never smaller than 44px (.btn is 2.75rem).
     - Transitions animate transform / opacity / colour only.

   Loaded AFTER components.css and the world theme, BEFORE the cinema layer.
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. THE MOUNT
   Before JavaScript builds anything, the mount holds the authored fallback
   copy and reads as ordinary prose. Once the shop is built, the mount holds
   .commerce__grid instead — so the two states can never appear together.

   The mount is matched by class AND by either authored attribute spelling
   (data-commerce="…" or data-commerce-catalogue), because an attribute
   selector matches an exact attribute name and the pages may use either. The
   same pair is what commerce.js queries for.
   -------------------------------------------------------------------------- */

.commerce,
[data-commerce],
[data-commerce-catalogue] {
  display: block;
}

.commerce__grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  margin: 0;
  padding: 0;
  list-style: none;
}


/* --------------------------------------------------------------------------
   2. PRODUCT CARD
   .card is already the column, the surface, the border and the hover
   elevation, and the bottle is the whole picture — so there is no media ratio
   to declare and no surface to restate. .commerce-card itself needs no rule of
   its own; only its children do.
   -------------------------------------------------------------------------- */

.commerce-card__body {
  gap: var(--space-4);
}

.commerce-card__title {
  font-size: var(--step-2);
}

.commerce-card__notes {
  font-size: var(--step-0);
  line-height: var(--lh-body);
  text-wrap: pretty;
}


/* --------------------------------------------------------------------------
   3. THE 3D STAGE
   cinema/bottle-viewer.js builds the canvas inside .commerce-card__viewer (a
   [data-bottle-viewer] element), and bottle-viewer.css styles it — including
   the captioned placeholder shown when there is no WebGL2, no CDN or no
   JavaScript at all. All this file does is inset the stage from the card edge;
   the size of the frame is decided by bottle-viewer.css's own --bv-* defaults
   and, on this surface, retuned per world in themes/greppo.css. Nothing here
   restates a --bv-* token, so this file can never fall out of step with
   bottle-viewer.css.

   cinema/viewer.js and its --mv-* / [data-model-viewer] vocabulary are NOT used
   on this surface: no [data-model-viewer] element is written anywhere, so a
   rule targeting it would match nothing.

   THE OPENER. cinema/bottle-showcase.js inserts one .bshow-open button as the
   last child of this stage, under the frame. bottle-showcase.css skins it, but
   it assumes a host with no padding of its own; this stage HAS side padding, so
   the button's own inset is zeroed below to keep it flush with the frame above.
   -------------------------------------------------------------------------- */

/* The side padding matches .card__body's, so the stage and the words below it
   share one left edge. */
.commerce-card__stage {
  padding: var(--space-6) var(--space-6) 0;
}

/* The showcase opener sits inside this already-padded stage. Zero the button's
   own margin-inline (bottle-showcase.css sizes it for a host with no padding)
   so it lines up edge-for-edge with the bottle frame directly above it. */
.commerce-card__stage .bshow-open {
  margin-inline: 0;
  width: 100%;
}


/* --------------------------------------------------------------------------
   4. THE FOOT — price and call to action
   Both live in one element carrying the rule and the automatic top margin, so
   every button in a row of cards lands on the same line however long a
   description runs.
   -------------------------------------------------------------------------- */

.commerce-card__foot {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-block-start: auto;
  padding-block-start: var(--space-5);
  border-block-start: 1px solid var(--w-line);
}


/* --------------------------------------------------------------------------
   5. PRICE
   Scoped one level deeper than components.css's `.card__body p`, which would
   otherwise win on specificity and dim the figure.

   The figure is the loudest thing on the card and the currency code is the
   quietest, which is the order a buyer reads them in. Tabular numerals keep
   $35 and $45 the same width down a row of cards.
   -------------------------------------------------------------------------- */

.commerce-card__body .commerce-card__price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
}

.commerce-card__figure {
  font-family: var(--font-display);
  font-size: var(--step-3);
  line-height: var(--lh-tight);
  letter-spacing: var(--track-tight);
  color: var(--w-accent);
  font-variant-numeric: tabular-nums;
}

.commerce-card__currency {
  font-family: var(--font-utility);
  font-size: var(--step--1);
  letter-spacing: var(--track-widest);
  text-transform: uppercase;
  color: var(--w-text-dim);
}


/* --------------------------------------------------------------------------
   6. CALL TO ACTION, AND THE INERT STATE
   A product with a Payment Link gets one full-width primary button. A product
   without one gets no button at all — commerce.js never draws a dead control —
   and this line stands in its place, set as a deliberate house note in the
   utility face rather than as an error.
   -------------------------------------------------------------------------- */

/* .btn--block already gives it the full width. This gives it a little more
   height and a little more tracking than an ordinary button: the one control
   on the page that takes money should feel like the largest thing on the card
   after the price itself. */
.commerce-card__cta {
  min-height: 3rem;
  letter-spacing: var(--track-wide);
}

.commerce-card__body .commerce-card__inert {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 2.75rem;
  font-family: var(--font-utility);
  font-size: var(--step--1);
  line-height: var(--lh-body);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--w-text-dim);
}

/* A short ruled tick, the same device the eyebrow uses, so the line reads as
   something the house wrote rather than as a control that failed to load. */
.commerce-card__inert::before {
  content: "";
  flex: none;
  inline-size: var(--space-5);
  block-size: 1px;
  background-color: var(--w-accent);
}


/* --------------------------------------------------------------------------
   7. REDUCED MOTION
   The shop has no decorative motion of its own; this only removes the hover
   travel the shared components contribute, so the alternate cut loses nothing
   but movement.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .commerce-card {
    transition: none;
  }
}


/* --------------------------------------------------------------------------
   8. NARROW VIEWPORTS
   -------------------------------------------------------------------------- */

@media (max-width: 40em) {
  .commerce__grid {
    gap: var(--space-5);
  }

  /* components.css tightens .card__body to --space-5 at this width; the stage
     follows it exactly, so the shared left edge survives the breakpoint. */
  .commerce-card__stage {
    padding: var(--space-5) var(--space-5) 0;
  }
}
