/**
 * T16 "Cart" — reskin of Magento's own cart page.
 *
 * COMPONENT OWNERSHIP — and the first version of this docblock got it WRONG in a way worth
 * recording, because the rules built on it were wrong too.
 *
 * It said `_product-card.css` "does NOT reach the cart". The narrow claim was right —
 * lines 16-20 scope `.product-item-info` to grids/related/upsell, so the cart's `.item-info`
 * really is unclaimed — but I generalised from one class to the whole file. MEASURED, that
 * file reaches this page through THREE UNSCOPED selectors, all of which match classes the
 * cart emits:
 *     :41  .product-item-photo                 display:block, aspect-ratio 1/1, radius, overflow, tint
 *     :89  .product-item-details               display:flex, column, gap
 *     :102 / :294  .product-item-name          font-size, and font-weight:700 at :294
 * Consequences of believing otherwise, all real:
 *   - the cart product name inherited font-weight:700 that was never asked for
 *   - my media rule put a 1:1 rounded box INSIDE _product-card's already-1:1 rounded box
 *   - my `.product-item-photo { display: block }` duplicated :42 exactly — dead
 * The rules below now RECONCILE with those three instead of competing.
 *
 * Verified exact and left alone: `_buttons.css:55` owns
 * `.cart.table-wrapper .actions-toolbar > .action` — this file does NOT restyle the cart
 * action buttons. T15 shipped that redundancy and it outranked _buttons.css's hover, leaving
 * the primary CTA with no hover feedback at all. `_mini-cart.css` is minicart-scoped (27 of
 * 28 selectors; the odd one is `.hrv-cart-count`, not emitted here).
 *
 * THE LINE ITEM IS A <tr>. MEASURED on the live page: `.item-info` is a table row. `border`
 * and `border-radius` on a row are ignored under `border-collapse: collapse`, so the first
 * version's "line item as a card" rule was a well-tokenised no-op — this theme's recurring
 * defect shape. The card is therefore drawn with `border-collapse: separate` on the table so
 * row borders and radii actually paint.
 *
 * Scope class READ from the live page: `checkout-cart-index`, plain hyphens. The cart is
 * ALREADY `page-layout-1column`, so no layout override is written. The theme's own
 * `hrv-page-cart` scope class is added in Magento_Checkout/layout/checkout_cart_index.xml and
 * is used ONLY where a rule has to win on weight — see the specificity arithmetic further down.
 */

/* Row borders and radii only paint if the table is not collapsing. */
.checkout-cart-index .cart.table-wrapper .cart.table {
    border-collapse: separate;
    border-spacing: 0;
}

/* THE CARD SPANS TWO ROWS, AND THE FIRST VERSION DID NOT — FOUND ONLY BY LOOKING.
   Core renders each line item as TWO sibling <tr>s: `.item-info` (media, name, price, qty)
   and `.item-actions` (Edit, Remove item). Styling only `.item-info > td` left the two
   buttons floating in the page background BELOW the card, visually detached from the item
   they act on. Every numeric check passed — image sizes, no overlap, no body scroll — because
   each measured element was individually correct. The picture showed two orphaned buttons.
   That is the third time this sprint a defect was visible only in a screenshot.
   So `.item-info` carries the card's TOP corners and `.item-actions` its BOTTOM corners, with
   the join between them borderless. */
.checkout-cart-index .cart.item .item-info > td {
    background: var(--hrv-surface);
    border-top: 1px solid var(--hrv-border);
    border-bottom: 0;   /* the .item-info / .item-actions join is borderless — see above */
    padding: 14px;
    /* Everything top-aligned left a tall empty band in each card (S3 MAJOR-4: 185.5px against
       the comp's ~123px). The comp is a compact, vertically-centred row. */
    vertical-align: middle;
}

/* ITEM SEPARATION IS NOT DONE WITH border-spacing. An earlier revision used
   `border-spacing: 0 12px`, which applies between EVERY row — so it separated one item from
   the next AND split .item-info from .item-actions, rendering each line item as two stacked
   cards instead of one. CSS cannot vary border-spacing per row, so border-spacing is 0 above
   and the visible separation comes from the padding-top on the NEXT item's `.item-info` cells
   — which, as the rule below says plainly, is padding INSIDE card 2 and not space between the
   two cards. */
.checkout-cart-index .cart.item .item-actions > td {
    background: var(--hrv-surface);
    border-bottom: 1px solid var(--hrv-border);
    border-left: 1px solid var(--hrv-border);
    border-right: 1px solid var(--hrv-border);
    border-bottom-left-radius: var(--hrv-radius);
    border-bottom-right-radius: var(--hrv-radius);
    padding: 0 14px 14px;
}

/* THERE IS NO GAP BETWEEN ITEMS, and the comment here has now claimed one twice in two
   different wrong ways. `gapBetweenItems` measures 0. What reads as separation is the 26px of
   padding INSIDE the second card, so item 2's content sits slightly lower relative to its
   border than item 1's. That is an honest trade-off of drawing a card on table rows.
   The second wrong claim was that the separation was "PAINTED by the tbody's box-shadow in the
   page colour". It was not. Under `border-collapse: separate`, row and row-group backgrounds
   paint BELOW cell backgrounds in the table painting order, so that shadow lay underneath the
   next item's opaque cells. The S4 render probe measured gap 0 and an identical rendered
   height with and without it — inert at every position — so the box-shadow rule is deleted
   rather than kept as decoration.
   SCOPED >=768. This selector computes (0,6,1), which also outranked the mobile per-cell
   padding, so at 375 the stacked cells of cards 2+ took 26px of top padding against card 1's
   12px. Below 768 the row draws one continuous outline and wants uniform 12px cells, so the
   rule is confined to the viewport it was designed for. */
@media (min-width: 768px) {
    .checkout-cart-index .cart.item + .cart.item > .item-info > td {
        padding-top: 26px;
    }
}

.checkout-cart-index .cart.item .item-info > td:first-child {
    border-left: 1px solid var(--hrv-border);
    border-top-left-radius: var(--hrv-radius);
}

.checkout-cart-index .cart.item .item-info > td:last-child {
    border-right: 1px solid var(--hrv-border);
    border-top-right-radius: var(--hrv-radius);
}

/* The WRAPPER had no rule, which made `object-fit: cover` below a no-op: width/height:100%
   resolved against a 110x160 wrapper, so the anchor simply clipped the top-left 96x96 of a
   portrait image — dead band on top, subject cut off at the bottom. Visible only in a picture.
   Sizing both boxes is what lets cover actually compose. */
.checkout-cart-index .cart.item .product-image-container,
.checkout-cart-index .cart.item .product-image-wrapper {
    display: block;
    max-width: none;
}

/* THE 100%/100% PAIR IS THE MOBILE BRANCH, and it is scoped here rather than explained 150
   lines away. At >=768 the container and wrapper are sized to 96x96 explicitly by the
   `.col.item` block at the bottom of this file, and those (0,6,0) declarations already
   overrode this (0,4,0) pair — so it only ever did anything below 768. Below 768 it MUST stay
   100%/100%: a 96px box overruns Blank's 60px absolutely-positioned photo. */
@media (max-width: 767px) {
    .checkout-cart-index .cart.item .product-image-container,
    .checkout-cart-index .cart.item .product-image-wrapper {
        width: 100%;
        height: 100%;
    }
}

.checkout-cart-index .cart.item .product-image-photo {
    position: static;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* NAME — `_product-card.css:294` sets font-weight:700 unscoped. A cart row is a list line,
   not a product card, so the weight is brought back deliberately rather than inherited by
   accident. */
.checkout-cart-index .cart.item .product-item-name {
    font-size: 15px;
    font-weight: 600;
    line-height: 1.3;
    margin: 0 0 6px;
}

.checkout-cart-index .cart.item .product-item-name a {
    color: var(--hrv-text);
    text-decoration: none;
}

/* Core's cart anchor carries no `.product-item-link`, so _product-card.css's hover rule does
   not reach it. Without this the name link has no hover feedback — the same defect class as
   T15's dead CTA hover, arrived at by omission rather than over-declaration. */
.checkout-cart-index .cart.item .product-item-name a:hover,
.checkout-cart-index .cart.item .product-item-name a:focus-visible {
    text-decoration: underline;
}

.checkout-cart-index .cart.item .col.price .cart-price .price,
.checkout-cart-index .cart.item .col.subtotal .cart-price .price {
    font-family: var(--hrv-font-display);
    font-weight: 700;
    color: var(--hrv-text);
}

.checkout-cart-index .cart.item .field.qty .input-text.qty {
    border-radius: var(--hrv-radius-pill);
    text-align: center;
}

/* REMOVE CONTROL — native `a.action.action-delete`, and `_buttons.css` has no bare `.action`
   selector, so without this it renders as Blank default. NOT --hrv-accent: accent-on-bg
   measures 2.82:1 on wheat, the DEFAULT palette, which fails AA — the exact trap T15 hit
   twice in one commit. */
.checkout-cart-index .cart.item .action-delete {
    color: var(--hrv-text-muted);
}

.checkout-cart-index .cart.item .action-delete:hover,
.checkout-cart-index .cart.item .action-delete:focus-visible {
    color: var(--hrv-text);
}

/* Promo code. THE FIRST ATTEMPT AT THIS RULE WAS 100% DEAD — it targeted `.cart-discount`,
   which the page never emits (0 matches). Core emits `div.block.discount` (#block-discount)
   as a CHILD of `.cart-summary`. That is the third dead-selector instance on this sprint, and
   the reason the battery now has to assert `querySelectorAll(sel).length > 0` for every
   selector shipped.
   NO card treatment here, deliberately: `.cart-summary` is already a surface+border+radius
   card, so boxing this inside it would draw the doubled card shell that was one of this
   sprint's picture-only defects. The comp puts promo code in its own card BELOW the line
   items — that is a layout move, not a selector edit, and it is declared rather than faked
   with a nested box. */
.checkout-cart-index .cart-summary .block.discount {
    border-top: 1px solid var(--hrv-border);
    margin-top: 16px;
    padding-top: 16px;
}

/* Totals — native `#cart-totals`. The first version styled the summary PANEL and nothing
   inside it, which is where a structurally-correct, visually-wrong page comes from. */
.checkout-cart-index .cart-summary {
    background: var(--hrv-surface);
    border: 1px solid var(--hrv-border);
    border-radius: var(--hrv-radius);
    padding: 20px;
}

.checkout-cart-index .cart-summary .totals th,
.checkout-cart-index .cart-summary .totals td {
    /* The horizontal padding is NOT what separates the label from the amount — forcing
       `8px 0` back still measures 34.1px of gap, because "Order Total" wraps inside a 70px
       cell. An earlier comment credited this rule with fixing that collision; it did not.
       Kept because the spacing is right, not because it fixes anything. */
    padding: 8px 12px 8px 0;
    border: 0;
}

.checkout-cart-index .cart-summary .grand.totals th,
.checkout-cart-index .cart-summary .grand.totals td {
    font-family: var(--hrv-font-display);
    font-weight: 700;
    border-top: 1px solid var(--hrv-border);
    padding-top: 14px;
}

/* ---------------------------------------------------------------------------
   S3 round 4. Both remedies below are S3's, measured on the live pod at 375 /
   768 / 1440. I got both wrong first and the record should say how.

   MAJOR-A. I applied HALF this remedy — `table-cell; width:96px` on the anchor
   while the unscoped `.product-image-container` / `.product-image-wrapper` rule
   still said `width:100%; height:100%` — measured photo 14x27 / img 0x0, and
   declared the remedy broken. It was not; my application was. A percentage width
   on a child of a table-cell resolves against an INDEFINITE width, and `width` on
   a table-cell is only a hint the auto table-layout algorithm may shrink. The px
   sizes on container+wrapper are the load-bearing half: they make the cell
   definite so the algorithm settles at 96. S3 re-measured four variants; the full
   remedy gives 96x96, name beside the image, row 134px. Scoped >=768 — mobile
   MUST keep 100%/100% or 96px overruns Blank's 60px absolutely-positioned photo.

   MAJOR-B. My mobile fix assumed Blank stacks the row into ONE column. It does
   not: `.col.item` goes full width but Price/Qty/Subtotal stay a 3-across strip
   of block siblings. Borders on every td therefore drew internal vertical rules,
   left a 3.4px cream sliver (3 x 113.2 = 339.6 vs the card's 343), and stopped
   at each cell's own bottom (the 9px gap and the 9px ragged step) because block
   siblings in a <tr> that is not a flex container do not equalise height. And
   `border-radius: 0` on `.item-info > td` (0,4,1) lost outright to the desktop
   `.checkout-cart-index .cart.item .item-info > td:last-child` rule at (0,5,1)
   — the mid-card rounded corner S3 could still see.

   Fix: give the <tr> `display: flex`. That takes it out of the table model
   entirely, so the ROW can carry one continuous outline instead of per-cell
   segments that cannot be made to align, and `flex: 1 1 0` makes the strip fill
   the width exactly.

   SPECIFICITY, COMPUTED. The earlier version of this paragraph claimed "every
   reset below is written at (0,5,1) or higher on purpose". That was false of
   seven of the block's own selectors: the four row-level ones compute (0,4,0) and
   three per-cell ones computed (0,4,1). The replacement paragraph then shipped a
   DIFFERENT wrong number -- "the two that DID reach (0,5,1)" -- in a comment about
   false arithmetic. Read off the parsed sheet: EIGHT selectors reached (0,5,1)
   pre-fix (the four `:first-child`/`:last-child` members plus `> td.item`,
   `.price`, `.qty`, `.subtotal`; `.msrp` is new here). The true point that number
   was reaching for is narrower: of those eight, TWO -- `.item-info > td:first-child`
   and `:last-child` -- merely TIED the desktop rules at (0,5,1) and won on source
   order, not on weight. The per-cell reset is therefore now double-scoped with
   `.hrv-page-cart` (the body class added in
   Magento_Checkout/layout/checkout_cart_index.xml), which makes every selector in
   that list win on weight:

     `.checkout-cart-index.hrv-page-cart .cart.item .item-info > td:first-child`
        = 6 classes + 1 element = (0,6,1)
        beats `.checkout-cart-index .cart.item .item-info > td:first-child`
        = 5 classes + 1 element = (0,5,1)                          -- real win

     `.checkout-cart-index.hrv-page-cart .cart.item .item-info > td`
        = 5 classes + 1 element = (0,5,1)
        beats `.checkout-cart-index .cart.item .item-info > td`
        = 4 classes + 1 element = (0,4,1)                          -- real win

   RAISING WEIGHT IS NOT FREE, AND THIS COST A REGRESSION BEFORE IT WAS CAUGHT.
   Only `border` / `border-radius` / `background` are in that promoted list. The
   cell `padding` is kept OUT of it, in its own single-scoped rule at (0,4,1),
   because at the promoted weight it started beating Blank's media-cell
   `padding: 25px 0 10px 75px` and buried the product name under the photo. A
   promoted selector wins EVERY fight it is in, including ones it was never meant
   to enter — check what else the rule would newly outrank before lifting it, not
   just the rule you were aiming at.

   The row rules ((0,4,0)) and the flex sizing ((0,4,1) and (0,5,1)) are NOT
   lifted and do not need to be — nothing in this file contests them. No selector
   in the block could be dropped: `> td` at (0,5,1) still only ties the desktop
   `:first-child` / `:last-child` rules, so those two forms have to stay spelled
   out to reach (0,6,1).
   --------------------------------------------------------------------------- */

@media (min-width: 768px) {
  /* CONSTRAIN THE INNER BOX, NOT THE CELL — and this took three attempts, each fixing the
     previous one's damage. `width` on the anchor is overridden by the table layout algorithm
     (96px gave a 5px box). `min-width` worked but beat Blank's mobile `max-width: 60px` per
     spec and pushed the photo over the product name. `display: block` fixed the aspect ratio
     (aspect-ratio does not apply to table cells) but removed the anchor from Luma's ANONYMOUS
     TABLE — Luma lays `.product-item-photo` and `.product-item-details` out as two table-cells
     inside the block td, so taking the anchor out of it dropped the name onto the next line
     with ~460px of empty white beside the photo.
     Keeping the anchor a table-cell preserves the horizontal row; sizing the container and
     wrapper explicitly gives the square without needing aspect-ratio at all. MEASURED: photo
     96x96, img 96x96, name side-by-side, row 134px (was 160.5px stacked, comp ~123px). */
  .checkout-cart-index .cart.item .col.item .product-item-photo {
    display: table-cell;
    width: 96px;
    min-width: 0;
    /* The gutter CANNOT live here. This element carries a background tint from
       components/_product-card.css, and backgrounds paint across the PADDING box —
       so padding-right:20px rendered as a 20px gray tab bleeding past every
       thumbnail against a white card, and stretched the anchor to 116x96 so the
       14px radius clipped the TAB's corners and left the image's own right corners
       square. S3 measured 20px of bleed at 1440 and 768, and 0 at HEAD~1. The
       gutter now lives on .product-item-details below, which has no background. */
    padding-right: 0;
    vertical-align: top;
  }

  /* NAME/PRICE ALIGNMENT — and the mechanism was chosen for what it does LATER, not for
     what it does now. MEASURED at 1440 before this rule: the row centres on 439.36 and the
     price, subtotal and qty controls all land within 0.5px of it (439.86 / 439.86 / 437.36),
     while the product name centres on 411.11 — 28.75px high, alone.

     WHY. `.col.item` is a block cell containing Luma's ANONYMOUS TABLE (photo cell +
     details cell). The details cell is 96px tall to match the photo, but holds only the
     19.5px name plus its 6px margin, so ~70px of it is empty — the origin line and the
     substitution checkbox that will fill it are declared gaps, not omissions. Top-aligned,
     the name therefore sits at the top of a mostly-empty box while everything else in the
     row is centred.

     THE RULE RETIRES ITSELF. `vertical-align: middle` on a table-cell only moves content
     that is SHORTER than the row. The moment those two declared gaps land and the details
     stack fills its 96px, middle and top compute identically and this declaration becomes a
     no-op — the name returns to the top of the stack, which is where the finished design
     wants it. That is the whole reason this mechanism was picked over the alternatives: a
     `margin-top` nudge is a magic number that would be visibly WRONG once the box fills, and
     giving the details cell an explicit height/flex layout would take it out of the
     anonymous table — the exact move that, on the photo anchor, dropped the name onto the
     next line with ~460px of empty white beside it.

     WHAT IT DOES NOT FIX, stated rather than glossed. ~8.5px of the 28.75 is not the name's
     position inside its box at all: `.col.item` carries Blank's asymmetric media-cell
     padding (measured 27px top / 10px bottom), so the whole 96px inner box centres on 448.36
     against the row's 439.36. Correcting that means beating Blank on the media cell's
     padding, which is precisely the change this file already records as having buried the
     product name UNDERNEATH the photo at 375. It is left alone deliberately. Residual after
     this rule: ~5.5px, against 28.75px before.

     >=768 ONLY, matching every other rule in this block. Below 768 the row is a flex
     container and `.col.item` is full-width with Blank's absolutely-positioned 60px photo —
     a different layout that this measurement says nothing about. */
  .checkout-cart-index .cart.item .col.item .product-item-details {
    padding-left: 20px;
    vertical-align: middle;
  }

  .checkout-cart-index .cart.item .col.item .product-image-container,
  .checkout-cart-index .cart.item .col.item .product-image-wrapper {
    width: 96px;
    height: 96px;
    padding: 0;   /* M-1: this selector is (0,6,0) and the generated ratio-box rule is (0,2,1),
                     so a plain declaration already wins; no !important needed, and !important
                     here would also break the @supports-not padding fallback */
  }

  .checkout-cart-index .cart.item .col.item .product-image-photo {
    /* `object-fit: cover` is NOT repeated here — the unscoped `.product-image-photo` rule
       above already sets it at every viewport. */
    width: 96px;
    height: 96px;
  }
}

@media (max-width: 767px) {
  .checkout-cart-index .cart.item .item-info,
  .checkout-cart-index .cart.item .item-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    background: var(--hrv-surface);
    border-left: 1px solid var(--hrv-border);
    border-right: 1px solid var(--hrv-border);
  }

  .checkout-cart-index .cart.item .item-info {
    border-top: 1px solid var(--hrv-border);
    border-radius: var(--hrv-radius) var(--hrv-radius) 0 0;
  }

  .checkout-cart-index .cart.item .item-actions {
    border-bottom: 1px solid var(--hrv-border);
    border-radius: 0 0 var(--hrv-radius) var(--hrv-radius);
  }

  /* The <tr> now draws the outline, so every cell gives up its own borders, radii and
     background. Double-scoped with `.hrv-page-cart` so this wins on weight — arithmetic in
     the block comment above. */
  .checkout-cart-index.hrv-page-cart .cart.item .item-info > td,
  .checkout-cart-index.hrv-page-cart .cart.item .item-info > td:first-child,
  .checkout-cart-index.hrv-page-cart .cart.item .item-info > td:last-child,
  .checkout-cart-index.hrv-page-cart .cart.item .item-actions > td,
  .checkout-cart-index.hrv-page-cart .cart.item .item-actions > td:first-child,
  .checkout-cart-index.hrv-page-cart .cart.item .item-actions > td:last-child {
    border: 0;
    border-radius: 0;
    background: transparent;
  }

  /* THE CELL PADDING IS DELIBERATELY *NOT* DOUBLE-SCOPED, AND THAT IS LOAD-BEARING.
     This is the one live declaration left over from the earlier mobile block (whose borders
     and radii the reset above already overrode, which is why the rest of it was deleted).
     Folding it INTO that reset list raised it from (0,4,1) to (0,5,1)/(0,6,1), and it then
     beat a Blank rule it had never beaten before: Blank gives the media cell
     `padding: 25px 0 10px 75px` and absolutely positions the 60px photo INTO that 75px
     gutter. MEASURED at 375 with the promoted version: `.col.item` padding-left collapsed
     75px -> 12px and the product name rendered UNDERNEATH the photo ("nal Harvest Crate").
     At (0,4,1) it loses the media cell to Blank — which is correct — and still wins the
     price/qty/subtotal strip and the actions cell. That split is what HEAD shipped and S3
     approved; the A/B against HEAD is in the review notes. Do not merge this upward. */
  .checkout-cart-index .cart.item .item-info > td,
  .checkout-cart-index .cart.item .item-actions > td {
    padding: 12px;
  }

  .checkout-cart-index .cart.item .item-info > td.item { flex: 0 0 100%; }

  /* `.msrp` is in the same qty/price/subtotal cell group in Blank and is emitted when MSRP
     ("Minimum Advertised Price") is switched on. It is not emitted on this store today, so
     listing it changes nothing now; without it, turning MSRP on would drop a fourth cell out
     of the strip. */
  .checkout-cart-index .cart.item .item-info > td.price,
  .checkout-cart-index .cart.item .item-info > td.qty,
  .checkout-cart-index .cart.item .item-info > td.subtotal,
  .checkout-cart-index .cart.item .item-info > td.msrp {
    flex: 1 1 0;
    min-width: 0; /* flex items floor at min-content without this, reintroducing the sliver */
  }

  .checkout-cart-index .cart.item .item-actions > td { flex: 1 1 100%; }
}

/* ===========================================================================
   EMPTY CART — T17.

   PROVENANCE, because it decides how much of this block to trust. The first
   version of it shipped INSIDE T16 as an emergency fix: T16's final gate found
   the empty cart completely unstyled, and the repair was written against the
   LIVE MARKUP ONLY. It was never compared to T17's artboard. This rewrite is
   the first time the comp has been read, and the comp disagrees with that
   draft on the single biggest decision it made.

   WHAT THE COMP ACTUALLY DRAWS (design/store-theme/03-harvest-food/colors/
   Wheat/pages/T17 Cart Empty.dc.html, line 29):

       padding: 80px 40px 0; display:flex; flex-direction:column;
       align-items:center; gap:16px; text-align:center

   — a bare centred column ON THE PAGE BACKGROUND. No surface, no border, no
   radius, no max-width box. THE PREVIOUS DRAFT WRAPPED ALL OF IT IN A 560px
   WHITE CARD, reasoning that the populated cart's `.cart-summary` is a card so
   the empty state should be one too. That reasoning is defensible and it is
   still wrong: it was derived from a sibling STATE instead of from the comp
   for THIS state, and the comp is the authority. The card is removed.

   ONE KNOCK-ON, DECLARED RATHER THAN LEFT TO BE FOUND: `_wishlist.css` gives
   the wishlist's empty state a card (surface + border + radius) built by the
   same reasoning. T17 is the ONLY artboard in this theme that draws an empty
   state at all, and it draws no card, so the two pages now disagree. That is
   a THEME-WIDE question — do empty states get a card? — and it is raised, not
   silently settled by editing a CLOSED page from inside T17.

   HOW THE COMP'S HIERARCHY MAPS ONTO CORE'S MARKUP. Core's
   Magento_Checkout::cart/noItems.phtml emits ONLY this (read from the pod, and
   re-read from vendor to confirm there is no auth branch in it):

       <div class="cart-empty">
         <?= getChildHtml('checkout_cart_empty_widget') ?>
         <p>You have no items in your shopping cart.</p>
         <p>Click <a href="/">here</a> to continue shopping.</p>
         <?= getChildHtml('shopping.cart.table.after') ?>
       </div>

   The comp's stack is: basket mark, then a 32px display headline, then one
   muted line, then two pill CTAs. The page ALREADY HAS a 32px display
   headline — `h1.page-title`, MEASURED at 32px/600 Bricolage at 1440 and 768
   and 26px at 375. So the h1 plays the comp's headline SLOT — its type and
   position, not its words: it still reads core's "Shopping Cart" against the
   comp's "Your basket is empty", which is a text change CSS cannot make (see
   the wording note below). The first <p> plays the comp's muted line, and the
   basket mark is hung on the title wrapper so it lands ABOVE the headline
   exactly as the comp orders them.
   The alternative — keeping the mark on `.cart-empty` — puts the headline
   above the mark and inverts the comp's order.

   WHY `:has()` AND NOT A NEW SCOPE CLASS. `.page-title-wrapper` is shared with
   the POPULATED cart, and the empty state has no body class of its own
   (`checkout-cart-index` covers both — verified on the live page). `:has()` is
   the only thing in CSS that can tell the two apart. If it is unsupported the
   page degrades to a left-aligned title with no mark, which is the current
   behaviour, not a broken one.

   COLOURS WERE MEASURED, NOT ROUNDED BY EYE. The comp mixes its own ratios
   (20% / 40% / 60% of text into bg) and this theme forbids page CSS minting
   ratios, so each one maps to the nearest EXISTING token — chosen by resolving
   both through the browser and comparing OKLab distance, after calibrating the
   instrument against a published figure (band-text on band = 12.31, reproduced
   exactly). Deltas from the comp, in OKLab:
       ring   comp 20%  ->  --hrv-border        dE 0.074  (border-strong 0.110)
       glyph  comp 40%  ->  --hrv-border-strong dE 0.037  (text-subtle   0.075)
       copy   comp 60%  ->  --hrv-text-muted    dE 0.038  (text-subtle   0.072)
   The glyph moves OFF `--hrv-text-subtle`, which the previous draft used: the
   measurement puts border-strong twice as close to what the comp draws. The
   ring lands slightly fainter than the comp (1.25:1 against the comp's own
   1.58:1 on wheat) — it is a decorative mark, not a meaningful UI component,
   so 1.4.11's 3:1 does not apply; the number is recorded so the choice is
   reviewable rather than invisible.

   WHAT NEEDED MARKUP, AND WHERE IT NOW LIVES. Three things in the comp were
   unreachable from CSS: the two pill CTAs, the "Popular right now" grid, and the
   headline WORDING. THE FIRST TWO ARE BUILT — see the CTA and section blocks
   further down this same file, and Magento_Checkout/layout/checkout_cart_index.xml
   for the composition. Both use core's own seams (`checkout.cart.empty.widget`
   inside `.cart-empty`, and the `checkout.cart.noitems` container for the grid),
   so no core template is overridden and no new one is added.

   THE WORDING IS THE ONE THAT REMAINS, and it qualifies the paragraph above: the
   h1 is styled as the comp's headline, but it renders core's "Shopping Cart"
   where the comp reads "Your basket is empty". CSS cannot change text.

   AND AN i18n CSV ROW IS NOT THE WAY OUT, which is worth saying because it is the
   first thing anyone reaches for. The h1 is `page-title`, shared with the POPULATED
   cart: a `"Shopping Cart","Your basket is empty"` row would retitle the page that
   HAS items too, where the phrase is simply false — and it would do it in every
   place that string appears. The wording needs something state-aware: a title set
   on the empty branch (layout `setPageTitle` under the no-items handle, or a
   ViewModel), not a translation. Not done here, and not to be read as done.

   NO ACCENT COLOUR AS TEXT anywhere in this block. This file already records
   accent-on-background at 2.82:1 on wheat (AA fail). Body copy is
   --hrv-text-muted on --hrv-bg, MEASURED at 5.69:1, and the link's state
   feedback is underline weight rather than a colour change.
   =========================================================================== */

/* The comp's centred column. Every declaration the card version set —
   background, border, border-radius, max-width, padding — is GONE rather than
   overridden, so nothing is left to leak into a later rule. */
.checkout-cart-index .cart-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    text-align: center;
    margin: 0 auto 72px;
    padding: 0;
}

/* The basket mark + the headline, as one centred stack above the copy.
   `padding-top: 80px` is the comp's own figure and lands on this element
   because `.page-main` contributes 0 top padding (MEASURED, not assumed). */
body.checkout-cart-index:has(.cart-empty) .page-title-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding-top: 80px;
}

/* Ring and glyph are TWO pseudo-elements because they are two different tones
   in the comp (20% and 40%), and one element painted through a mask can only
   carry one colour — a mask clips the element's whole rendering, border
   included, so a bordered-and-masked single box loses its ring.
   They are overlaid without absolute positioning: both are flex items, `order`
   pulls them in front of the h1, and the glyph's `margin-top: -76px` lifts it
   exactly one ring-height so the two boxes coincide. Nothing here is coupled
   to the container's padding, which an absolutely positioned mark would be. */
body.checkout-cart-index:has(.cart-empty) .page-title-wrapper::before {
    content: '';
    order: -2;
    width: 76px;
    height: 76px;
    border: 2px solid var(--hrv-border);
    border-radius: var(--hrv-radius-circle);
    box-sizing: border-box;
}

/* The glyph. A MASK, not a background-image, so it takes a token and tracks
   every palette instead of baking a hex into the data URI. The stroke inside
   the URI is opaque black only because a mask reads ALPHA: opaque = show, and
   the paint comes from `background-color`.

   @supports IS LOAD-BEARING. Without it a browser with no masking renders this
   as a 76x76 SOLID BLOCK. Gated, such a browser gets the empty ring, which is
   a coherent mark rather than a defect. */
@supports ((mask-image: none) or (-webkit-mask-image: none)) {
    body.checkout-cart-index:has(.cart-empty) .page-title-wrapper::after {
        content: '';
        order: -1;
        width: 76px;
        height: 76px;
        margin-top: -76px;
        margin-bottom: 16px;
        background-color: var(--hrv-border-strong);
        -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 9h18l-1.7 9.3a2 2 0 0 1-2 1.7H6.7a2 2 0 0 1-2-1.7L3 9Z'/%3E%3Cpath d='m8 9 2.2-5M16 9l-2.2-5'/%3E%3Cpath d='M9.6 13v3.4M14.4 13v3.4'/%3E%3C/svg%3E") center / 30px 30px no-repeat;
        mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 9h18l-1.7 9.3a2 2 0 0 1-2 1.7H6.7a2 2 0 0 1-2-1.7L3 9Z'/%3E%3Cpath d='m8 9 2.2-5M16 9l-2.2-5'/%3E%3Cpath d='M9.6 13v3.4M14.4 13v3.4'/%3E%3C/svg%3E") center / 30px 30px no-repeat;
    }
}

/* Only the two properties that differ from what the page already renders.
   font-size is NOT set: MEASURED at 32px desktop / 26px at 375, which is the
   comp's 32px and a mobile step the comp does not specify but Blank already
   provides. Re-declaring it would freeze the mobile size at the desktop one. */
body.checkout-cart-index:has(.cart-empty) .page-title-wrapper .page-title {
    font-weight: 700;
    line-height: 1.12;
    text-wrap: balance;
    margin: 0 0 16px;
}

/* `:first-of-type` / `:last-of-type` rather than `:first-child` — the first
   ELEMENT child of `.cart-empty` is the first <p>, but keying on element type
   survives core's `checkout_cart_empty_widget` container being filled, which
   `:first-child` would not.
   The first <p> is the comp's muted supporting line (14.5px, 46ch measure).
   The WORDS are core's and are not changed here; changing them is a template
   and i18n concern. */
.checkout-cart-index .cart-empty p:first-of-type {
    font-size: 14.5px;
    color: var(--hrv-text-muted);
    max-width: 46ch;
    margin: 0;
}

/* The second <p> sits where the comp puts its CTA row, and carries the comp's
   8px offset. The container's 16px gap plus this 8px reproduces the comp's
   16 + 8 exactly. */
.checkout-cart-index .cart-empty p:last-of-type {
    font-size: 14.5px;
    color: var(--hrv-text-muted);
    margin: 8px 0 0;
}

/* FALLBACK STYLING ONLY — this link is normally NOT VISIBLE.
   The rule above hides core's "Click here to continue shopping." whenever the CTA
   pills are present, because they point at the same destination and the comp draws
   only one control. These declarations therefore apply exactly in the case that
   rule does not match: the layout failed to merge and the pills are absent, leaving
   the inline "here" as the page's only way out. Styling it as a strong underlined
   link rather than a pill is deliberate for that case — "Click [ HERE ] to continue
   shopping." reads worse than the link it would replace. It is NOT faked with a
   `content:` string either; a CSS string cannot go through `__()` and would be
   untranslatable in de_DE / pl_PL. */
.checkout-cart-index .cart-empty p a {
    color: var(--hrv-text);
    font-weight: 600;
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 3px;
}

/* Hover/focus feedback is UNDERLINE WEIGHT, not colour — see the AA note in
   the block comment. `_buttons.css` owns no bare `.cart-empty a` selector, so
   without this the link would have no state feedback at all, which is the
   defect-by-omission this file already recorded on the line-item name link. */
.checkout-cart-index .cart-empty p a:hover,
.checkout-cart-index .cart-empty p a:focus-visible {
    text-decoration-thickness: 3px;
}

/* ---------------------------------------------------------------------------
   T17 CTA PAIR. Markup comes from Magento_Checkout/layout/checkout_cart_index.xml
   (two Html\Link blocks in a <ul>) — that file carries why it is composition and
   not a template.

   THE BUTTON IS NOT RE-DECLARED HERE. Each link carries `action primary` /
   `action secondary`, so `_buttons.css` supplies the whole system: 13px 28px
   padding (already exactly the comp's), inline-flex centring, the accent fill
   with --hrv-accent-text, the outline variant, hover, focus, and the Blank
   (0,2,1) radius repair. Only the three properties where the comp genuinely
   differs from the theme's button are stated below. T15 shipped a duplicated
   component beside the one that already existed; this is the opposite move.
   --------------------------------------------------------------------------- */

/* NO `order` REVERSAL HERE ANY MORE. An earlier build hung this <ul> off core's
   `checkout_cart_empty_widget`, which renders BEFORE both paragraphs, and flipped it
   back visually with `order: 1`. That left the DOM reading [CTAs][copy] while the page
   showed [copy][CTAs] — screen-reader and tab order reaching the buttons before the
   sentence that explains them (WCAG 1.3.2, 2.4.3). The layout now attaches the
   container to `shopping.cart.table.after`, which vendor's noItems.phtml renders
   INSIDE .cart-empty after both paragraphs, so the source order is already the comp's
   and the sequence needs no correcting.
   The <ul> is a flex item of .cart-empty, so the container's 16px gap plus this 8px
   reproduces the comp's 16 + 8 exactly — the same arithmetic as the paragraph above. */
.checkout-cart-index .cart-empty .hrv-empty-actions {
    display: flex;
    gap: 12px;
    margin: 8px 0 0;
    padding: 0;
    list-style: none;
}

/* MEASURED, NOT PRECAUTIONARY. Blank ships `ul > li { margin-bottom: 10px }`, and the
   first build of this row inherited it: the <ul> measured 56px around a 46px pill,
   i.e. 10px of dead space hanging under the CTAs and pushing everything below them.
   The pills themselves looked perfect in the screenshot — only the container's box
   showed it. This is the reason the row's own height is worth measuring rather than
   eyeballing: `list-style: none` above removes the marker, not the margin. */
.checkout-cart-index .cart-empty .hrv-empty-actions > li {
    margin: 0;
}

/* The comp's pill is 999px / 15px / 700 against the theme button's 14px radius,
   14px, 600. Three properties, page-scoped: the pill-vs-14px question is a
   THEME-WIDE decision recorded in _buttons.css's docblock and is not settled from
   inside T17.
   Weight: (0,4,0) here against `a.action.primary` (0,2,1) in _buttons.css, so this
   wins on specificity rather than relying on source order. */
.checkout-cart-index .cart-empty .hrv-empty-actions .hrv-empty-cta {
    border-radius: var(--hrv-radius-pill);
    font-size: 15px;
    font-weight: 700;
    /* STILL NOT REDUNDANT, AND NOT FOR THE REASON THE OLD COMMENT GAVE — see the history
       note below. The theme-wide repair that shipped in _buttons.css added the `a.`
       qualifier to the SECONDARY colour rule only, so `a.action.secondary` no longer needs
       help here; `a.action.primary` still does, because the base rule in that file lists
       `a.action.primary` (0,2,1) with `border: 1px solid transparent` while the primary
       colour rule is only `.action.primary` (0,2,0). Removing this line was TRIED, DEPLOYED
       AND MEASURED: the primary's computed border-top-color fell to rgba(0,0,0,0) and the
       rendered pill changed — 604 differing pixels at 1440 / 586 at 375, max channel delta
       52, in a bbox exactly the size of this button. Invisible in the fill, visible on the
       antialiased curve of a 999px radius. Retiring it needs `a.action.primary` added to
       _buttons.css's primary colour rule, which is a theme-wide change with its own
       verification pass — filed, not smuggled in here. */
    border-color: var(--hrv-accent);
}

/* THE HOVER STATE HAD TO FOLLOW, because the repair above outranks it.
   `_buttons.css` darkens the primary on hover to --hrv-accent-hover on BOTH
   background and border-color. Its `.action.primary:hover` is (0,3,0); the rule
   above is (0,4,0) and carries no pseudo-class, so it applies in the hover state
   too and pinned the border at --hrv-accent while the fill darkened underneath:
   a lighter ring around a darker pill. A specificity repair inherits every state
   of the thing it outranks.

   MEASURED THIS ROUND, ON BOTH CONTROLS. An S4 pass proposed keeping this rule for a
   DIFFERENT reason — that `_buttons.css`'s `.action.secondary:hover` sets background and
   colour but not border-colour — and concluded it was therefore load-bearing for the
   secondary only. That reasoning names a contest this rule never has: the winner without
   it is the (0,4,0) rule ABOVE, not anything in _buttons.css. Removing the declaration from
   the DEPLOYED sheet and forcing :hover by CDP (forcer control moved in every run):
       WITHOUT   primary   border-top-color rgb(192,138,45) -> rgb(192,138,45)
                 secondary border-top-color rgb(192,138,45) -> rgb(192,138,45)
                 changedProps on both: backgroundColor ONLY — the fill darkens alone
       WITH      both      -> oklab(0.570116 0.0242935 0.102407)
   So it is load-bearing for BOTH buttons, for the reason at the top of this block.
   `--hrv-accent-hover` is `color-mix(in oklab, #C08A2D 85%, black)`, which Chrome reports
   as `oklab(...)` and NEVER as rgb — an assertion written against an rgb() figure would be
   comparing to a value this engine cannot produce. */
.checkout-cart-index .cart-empty .hrv-empty-actions .hrv-empty-cta:hover,
.checkout-cart-index .cart-empty .hrv-empty-actions .hrv-empty-cta:focus-visible {
    border-color: var(--hrv-accent-hover);
}

/* WHY border-color IS RESTATED ABOVE — HALF OF THIS IS NOW HISTORY, HALF IS STILL LIVE.
   `_buttons.css`'s base rule is element-qualified (`a.action.primary`, `a.action.secondary`,
   (0,2,1), `border: 1px solid transparent`) while its colour rules are not (`.action.primary`
   / `.action.secondary`, (0,2,0)). On an <a> the base rule wins, so anchor-based buttons in
   this theme got a transparent border — invisible on a filled primary, and on the outline
   secondary the difference between a bordered pill and bare text. T17 was the first page to
   render one prominently.
   HISTORY: the SECONDARY half of that theme-wide repair has shipped — `_buttons.css`'s
   secondary colour rule now carries `a.action.secondary`, so this page no longer props it up
   and this block no longer asks for a fix that exists.
   STILL LIVE: the PRIMARY half has NOT. The primary colour rule is still only
   `.action.primary`, so `a.action.primary` keeps the transparent border theme-wide. Retiring
   the restatement above was TRIED, DEPLOYED AND MEASURED here, and it is not a no-op: the
   primary's computed border-top-color fell to rgba(0,0,0,0) and the pill's RENDERED PIXELS
   changed — 604 differing pixels at 1440 / 586 at 375, max channel delta 52, in a bbox
   exactly this button's 182x46. The fill hides it dead-centre; the antialiased curve of a
   999px radius does not. Zero geometry moved and the whole-document snapshot diff was clean,
   which is precisely why a computed-value check alone would have called the removal safe.
   So the restatement stays until `a.action.primary` is added to _buttons.css's primary colour
   rule — a theme-wide visual change owing its own verification pass over every page that
   renders an anchor primary, which is not a side effect of a cart task. Fixed where it ships,
   raised where it belongs — the second half of the same raise. */

/* AA — THE COMP'S SECONDARY LABEL COLOUR CANNOT SHIP AS DRAWN.
   The comp paints "Sign in" in var(--ac) on the page background. MEASURED across
   every palette that ships (canvas-resolved, instrument calibrated against
   _derived.css's published 12.31 first), for a 15px/700 label — which is NOT WCAG
   large text, so 4.5:1 applies:
       accent on bg      wheat 2.82 FAIL · honey 4.05 FAIL · basil 4.62 · tomato 4.55 · plum 6.88
       accent-hover on bg wheat 4.21 STILL FAIL · honey 5.80 · basil 6.54 · tomato 6.42 · plum 9.11
   Wheat is the DEFAULT palette and the one this task ships, so the comp's own
   colour fails on the storefront it was drawn for, and the obvious darker token
   does not rescue it either. The label therefore takes --hrv-text (15.34:1 on
   wheat, 14.75-15.55 across the set) while the BORDER keeps the comp's accent, so
   the control still reads as the accent-outlined pill the comp draws.

   PAGE-SCOPED ON PURPOSE. `.action.secondary` carries the accent label theme-wide,
   so this is a systemic component question — does the theme need an accent variant
   that is legible as text on --hrv-bg? — and _derived.css already routes exactly
   that class of question to design rather than settling it inside one page's task.
   Fixed where it ships; raised where it belongs.

   The border is accent-on-bg too (2.82 on wheat, under 1.4.11's 3:1 for UI
   component boundaries). Left as the comp draws it: the control is identified by
   its label, which is now high-contrast, and the border is emphasis rather than the
   sole affordance. Recorded so the judgement is reviewable instead of invisible.

   Hover: this rule is (0,5,0) and `_buttons.css`'s `.action.secondary:hover` is
   (0,3,0), so the label stays --hrv-text on hover rather than moving to
   --hrv-accent-hover (4.21 on wheat, also failing). Feedback survives as the
   component's accent-tint background, which this file does not touch. */
.checkout-cart-index .cart-empty .hrv-empty-actions .action.secondary {
    color: var(--hrv-text);
}

/* Core's own CTA is the word "here" inside "Click here to continue shopping." —
   pointing at the SAME base URL the "Browse the aisles" pill now does. Two
   controls, one destination, and the comp draws only one; the sentence goes.

   CONDITIONAL ON THE PILLS ACTUALLY BEING THERE, deliberately. If the layout ever
   fails to merge (a renamed container, a disabled module), :has() does not match,
   the sentence stays, and the page still has a way out. A plain `display: none`
   would turn one silent failure into a dead end. Nothing is lost to a screen
   reader either — the destination survives under a better label. */
.checkout-cart-index .cart-empty:has(.hrv-empty-actions) p:last-of-type {
    display: none;
}

/* ---------------------------------------------------------------------------
   T17 "POPULAR RIGHT NOW". Block and collection are wired in the layout XML; read
   the docblock there for why Bestsellers rather than a CatalogWidget.

   The block renders nothing on a store with no order history — its own deliberate
   guard, so a heading never appears over an empty grid. This tenant HAS order
   history, so the section renders four cards and everything below is measured
   against a live render, not reasoned about.

   THE CARD AND THE GRID ARE NOT RE-DECLARED HERE. `_product-card.css` owns the
   shell, the card width, the media sizing and the columns for this markup. What
   remains below is only what that component cannot supply — the heading type and
   the section's measure — because every `.hrv-home-*` rule in _home.css is scoped
   `.cms-index-index.hrv-page-home`.
   --------------------------------------------------------------------------- */
.checkout-cart-index .hrv-home-headrow {
    margin: 0 0 20px;
    text-align: center;
}

.checkout-cart-index .hrv-home-title {
    font-family: var(--hrv-font-display);
    font-size: 22px;
    font-weight: 700;
    line-height: 1.12;
    margin: 0;
}

/* The comp caps this row at 1180px and centres it. No top margin: .cart-empty's
   own 72px bottom margin IS the comp's 72px gap between the two, and a second one
   would double it. */
.checkout-cart-index .widget-product-grid {
    max-width: 1180px;
    margin: 0 auto;
    padding: 0;
    list-style: none;
}

/* NO BOTTOM MARGIN IS SET HERE. An earlier 72px had no source in the comp — it was
   symmetry with the section's top gap — and it measured 111px of space below the row
   against the comp's 64px, which the footer's own top margin already provides. A
   figure invented for balance is still an invented figure. */

@media (max-width: 767px) {
    /* The comp has no mobile artboard, so only the one figure that does not
       survive the smaller canvas is changed: 80px of dead space above the mark
       is most of a 375-wide viewport's first screen. Type is deliberately NOT
       touched — the h1 already steps to 26px here on its own, and the copy is
       14.5px body text at both sizes. */
    body.checkout-cart-index:has(.cart-empty) .page-title-wrapper {
        padding-top: 48px;
    }

    .checkout-cart-index .cart-empty {
        margin-bottom: 48px;
    }

    /* Two 28px-padded pills side by side do not fit a 343px column. Wrapping
       rather than stacking, so the pair still reads as one row where it fits. */
    .checkout-cart-index .cart-empty .hrv-empty-actions {
        flex-wrap: wrap;
        justify-content: center;
    }
}
