/* ══════════════════════════════════════════════════════════════════════════
   The shelf, the tray and the product card (§2.1, §5).

   THE CARD IS NOT A CARD. It has no border, no radius, no shadow and no
   outline. It is a grid cell standing on a shelf, and the shelf is the only
   thing separating it from its neighbours.

   INV-3 is the single most load-bearing detail in the direction: `column-gap:
   0` plus internal cell padding plus one horizontal rule per cell means
   adjacent cells' rules FUSE into one continuous line across the row, at any
   column count, at any breakpoint, with no JS. Add a column gap and the
   archive becomes 22 boxes.
   ══════════════════════════════════════════════════════════════════════ */

@layer components {
  /* ── the shelf ────────────────────────────────────────────────────────── */
  .rtv-shelf {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(248px, 1fr));
    /* Zero, and it must stay zero. This is what fuses the rules. */
    column-gap: 0;
    row-gap: var(--s-6);
    /* The last cell's rule is drawn PAST its own right edge, across the empty
       tracks of a partial last row (see .rtv-card:last-child below). The
       shelf clips that overhang at its own edge. `clip` rather than `hidden`:
       it establishes no scroll container, so nothing here can become a
       stray horizontal scrollbar. */
    overflow-x: clip;
  }

  @media (max-width: 479px) {
    /* 248 would drop to one column on a 390 viewport. Two up is the floor:
       a single-column archive stops reading as a shelf of goods. */
    .rtv-shelf {
      grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    }
  }

  /* ── the cell ─────────────────────────────────────────────────────────── */
  .rtv-card {
    /* One inset for the whole cell, declared once so the rule extension below
       can add it back and land exactly on the cell's edge. */
    --rtv-card-pad: var(--s-4);
    /* The height the meta lane occupies when it renders: its top margin plus
       one 11px line at the dense leading. */
    --rtv-meta-lane: calc(var(--s-3) + var(--t-11) * var(--lh-tight));

    /* tray · meta · name(1fr) · shelf-edge strip.
       The name row takes the free space, so the strip aligns across the whole
       row no matter how many lines a name takes. This replaces the F10
       reserved-two-lines hack, which the 71-character PureGold name breaks.

       The meta TRACK is floored at one lane even when no meta element exists.
       A grid track occupies its size whether or not anything is placed in it,
       so the lane is reserved in layout while the lane itself still collapses
       in the DOM — `VEGÁN PROTEIN 750g` has no brand term and no size
       attribute, and without the floor its name starts 25px above every
       neighbour's. Nothing is printed: the spec's "the lane collapses, it
       never prints a placeholder" is about content, and no content is
       invented here. `minmax(…, auto)` and not a fixed track because at 390
       a two-term lane (`APPLIED NUTRITION · 2 KG`) legitimately wraps. */
    display: grid;
    grid-template-rows: auto minmax(var(--rtv-meta-lane), auto) 1fr auto;
    min-inline-size: 0;
  }

  /* ── the desktop composition (§2.1, §1) ────────────────────────────────
     At ≥641px the cell is wide — 270px at 1440 across five tracks — and with
     the mobile 16px inset each tray sits in a 32px moat of ground, 48px below
     the shelf above it and 100px above its own rule. Ground on all four sides
     of a white rounded object is what a card is; the row read as five
     floating boxes, which is the exact failure the direction names ("get this
     wrong and the archive becomes 22 boxes"). The tray is a white object BY
     SPEC and stays one — what changes is its RELATIONSHIP to the shelf:
       · the inset halves, so the ground between two trays is a 16px slot
         rather than a moat and the row reads as goods packed on one shelf
         (tray:gutter goes from 7.4:1 to 15.9:1 at 1440);
       · the space between one shelf and the next tightens by a third, which
         is F3 — 64px of dead ground below the price strip becomes 48px, and
         the measured strip-base-to-next-tray distance 48px becomes 32px.
     At ≤640px none of this applies: two large trays with a close rule already
     read correctly, and an 8px inset on a 179px cell would be cramped. */
  @media (min-width: 641px) {
    .rtv-shelf { row-gap: var(--s-5); }
    .rtv-card { --rtv-card-pad: var(--s-2); }
  }

  /* Explicit row placement, because the meta lane legitimately does not
     render when a product has neither a brand term nor a size attribute. With
     implicit placement the name would slide into the auto row and the strip
     into the 1fr row, and the strip would stretch. */
  .rtv-card__tray-link { grid-row: 1; }
  .rtv-card__meta      { grid-row: 2; }
  .rtv-card__name      { grid-row: 3; }
  .rtv-card__strip     { grid-row: 4; }

  /* Cell padding sits on the CONTENT, not on the cell, so the shelf rule can
     run edge to edge and meet its neighbour. */
  .rtv-card__tray-link,
  .rtv-card__meta,
  .rtv-card__name {
    padding-inline: var(--rtv-card-pad);
  }

  .rtv-card__tray-link {
    display: block;
    padding-block-start: var(--rtv-card-pad);
  }


  /* ── the meta lane ────────────────────────────────────────────────────── */
  .rtv-card__meta {
    margin: var(--s-3) 0 0;
    color: var(--rtv-ink-2);
    font-family: var(--rtv-font-system);
    font-size: var(--t-11);
    font-weight: 600;
    letter-spacing: .12em;
    line-height: var(--lh-tight);
    text-transform: uppercase;
  }

  /* ── the name ─────────────────────────────────────────────────────────── */
  .rtv-card__name {
    margin: var(--s-2) 0 var(--s-3);
    font-family: var(--rtv-font-goods);
    font-size: var(--t-15);
    font-weight: 700;
    /* 1.22 is the measured Hungarian mixed-case floor for Gabarito 700 plus
       an optical gap. Below it the dots of Ö fuse with the line above. */
    line-height: var(--lh-goods);
    /* Sentence case as WooCommerce stores it. Never uppercase: the PureGold
       name in caps is unreadable and is three lines either way. */
    text-transform: none;
    /* Unbounded wrap. No clamp, no ellipsis, no min-height. */
    overflow-wrap: anywhere;
  }

  .rtv-card__name a {
    color: var(--rtv-ink);
    text-decoration: none;
  }

  .rtv-card__name a:hover {
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
  }

  /* One visible ring, on the name link. The tray is decorative and its link
     is aria-hidden and out of the tab order, so focus never lands twice. */
  .rtv-card__name a:focus-visible {
    outline: 3px solid var(--rtv-ink);
    outline-offset: 2px;
  }

  /* ── the shelf-edge strip ─────────────────────────────────────────────── */
  .rtv-card__strip {
    /* The containing block for the last cell's rule extension. */
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2) var(--s-3);
    padding: var(--s-3) var(--rtv-card-pad) var(--s-4);
    /* THE SHELF. Full cell width, so it meets the neighbouring cell's rule
       with no seam. The price and the button hang on its front edge. */
    border-block-start: var(--bw-rule) solid var(--rtv-rule);
  }

  /* ── the rule runs THROUGH AND PAST the last tray ──────────────────────
     INV-3 says the rule fuses "across the row at any column count". Adjacent
     cells' borders do that for a full row — but the last row is almost never
     full. 22 products at 5, 4, 3 or 2 columns leaves a remainder of 2, 2, 1
     and 0; the six-product harness leaves 1 at five columns. A row is not a
     DOM element in a CSS grid, so no per-cell border can reach into an empty
     track, and a per-cell border that simply stops turns the remainder into
     a floating box — the failure the whole direction is built to avoid.

     The last cell in flow is always the rightmost occupied cell of the last
     row, so its rule is the one that has to keep going. It is extended past
     the cell's own edge and the shelf clips it at its right edge. When the
     last row happens to be full the overhang is clipped entirely and nothing
     changes. No JS, no filler cells, and the row is never forced full. */
  .rtv-shelf > .rtv-card:last-child .rtv-card__strip::after {
    content: '';
    position: absolute;
    /* The containing block is the strip's PADDING box, which spans the whole
       cell horizontally (the strip has no side borders) but starts one
       rule-width BELOW the rule. So 100% lands exactly on the cell's right
       edge, and -1px lifts the extension onto the rule's own line. A verified
       junction: tests/primitives/shelf.browser.test.mjs samples the rendered
       pixels across it, because an 8px seam here is invisible in review and
       fatal to the mechanism. */
    inset-block-start: calc(-1 * var(--bw-rule));
    inset-inline-start: 100%;
    inline-size: 100vw;
    border-block-start: var(--bw-rule) solid var(--rtv-rule);
    pointer-events: none;
  }

  /* The whole product moves, not a shadow — it is an object on a shelf. */
  .rtv-card:hover .rtv-tray {
    transform: translateY(-2px);
    transition: transform var(--dur-1) var(--ease-out);
  }

  @media (prefers-reduced-motion: reduce) {
    /* Reduced motion resolves to opacity only: no transform, no translate. */
    .rtv-card:hover .rtv-tray { transform: none; }
  }

  /* ── out of stock ─────────────────────────────────────────────────────
     Not a button, because it is not an action. The tray and the name are
     UNCHANGED and deliberately not dimmed: the product is still merchandise,
     and dimming it is a scarcity signal the store has not earned. */
  .rtv-card__oos {
    display: inline-flex;
    align-items: center;
    min-block-size: var(--ctl-h-dense);
    padding-inline: var(--s-3);
    border-radius: var(--r-1);
    background-color: var(--rtv-ground-sunk);
    color: var(--rtv-ink-2);
    font-family: var(--rtv-font-system);
    font-size: var(--t-13);
    font-weight: 500;
  }

  .rtv-card__form { margin: 0; }

  .rtv-card__error {
    flex-basis: 100%;
    color: var(--rtv-alert);
    font-family: var(--rtv-font-system);
    font-size: var(--t-13);
  }

  /* ── the empty shelf ──────────────────────────────────────────────────
     A rule with nothing on it. The counter is visibly empty, which is a
     state, not a broken page. There is no illustration here and none may be
     added — Rutriven owns zero drawn assets (§4.4). */
  .rtv-shelf-empty {
    padding-block: var(--s-6);
    border-block-start: var(--bw-rule) solid var(--rtv-rule);
    color: var(--rtv-ink-2);
    font-family: var(--rtv-font-system);
    font-size: var(--t-15);
  }
}
