/* ============================================================
   cart.css — Flex Europa · finaldesign
   The sliding shopping bag (brief §2).

   PROVENANCE: structure ported from dev/design3a/css/shared/cart.css
   (the most complete drawer in the design family — it was the only one
   with a focus trap and aria-modal). Restyled to finaldesign's
   vocabulary: sharp corners (--radius-card: 0), hairline borders, mono
   uppercase labels, the graphite/teal palette. Behavioural structure —
   fixed panel entering from the inline-end edge, scrim, header/footer
   pinned with the body scrolling between them, .is-empty toggling —
   is design3a's and kept.

   UPGRADES over design3a, each required by §2:
     · 44×44 steppers (design3a's were 30×30)
     · loading skeleton, per-line updating state, inline error state
     · deferred removal with an undo bar
     · swipe-to-close with a directional lock
     · safe-area inset on the footer
     · scroll lock that compensates for scrollbar width

   FILE LOCATION: the brief §1.4 says css/components/. finaldesign has
   no such folder — engineering §3 puts shared component CSS in
   css/shared/, and design3a's drawer lived at css/shared/cart.css.
   Following the family precedent keeps ONE component location instead
   of two. Loaded immediately after components.css, i.e. at the
   "components" position in the mandated cascade.
   ============================================================ */

/* ---------------------------------------------------------------- Shell */
.cart-drawer {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
}

.cart-drawer__scrim {
  position: absolute;
  inset: 0;
  background: var(--scrim-backdrop);
  opacity: 0;
  transition: opacity var(--dur-drawer) var(--ease-out);
}
.cart-drawer.is-open .cart-drawer__scrim { opacity: 1; }

.cart-drawer__panel {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  inline-size: min(var(--drawer-inline-size), var(--drawer-mobile-inline));
  display: flex;
  flex-direction: column;
  background: var(--color-bg);
  border-inline-start: var(--hairline);
  box-shadow: var(--shadow-lg);
  /* The browser owns the vertical axis, so the swipe handlers never need
     preventDefault and every touch listener stays passive. */
  touch-action: pan-y;
  /* Off-screen resting state. Logical inset + translate on the inline
     axis means an RTL build mirrors to the start edge with no override. */
  transform: translateX(100%);
  transition: transform var(--dur-drawer) var(--ease-out);
  overflow: hidden;              /* the body scrolls, never the panel */
}
[dir="rtl"] .cart-drawer__panel { transform: translateX(-100%); }
.cart-drawer.is-open .cart-drawer__panel { transform: translateX(0); }

/* While a swipe is in progress the panel follows the finger; the
   inline transition is suppressed so it tracks 1:1. */
.cart-drawer.is-dragging .cart-drawer__panel,
.cart-drawer.is-dragging .cart-drawer__scrim { transition: none; }

/* ---------------------------------------------------------------- Header */
.cart-drawer__header {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-md) var(--space-lg);
  border-block-end: var(--hairline);
}
.cart-drawer__title {
  font-family: var(--font-heading);
  font-size: var(--fs-lg);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-tight);
  color: var(--color-text);
}
.cart-drawer__count {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.cart-drawer__close { margin-inline-start: auto; color: var(--color-text); }
.cart-drawer__close:hover { background: var(--color-surface); }

/* ---------------------------------------------------------------- Body */
.cart-drawer__body {
  flex: 1 1 auto;
  min-block-size: 0;             /* let the flex item actually shrink and scroll */
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;  /* a bounce at the end must not scroll the page */
  padding: var(--space-lg);
}

.cart-drawer__items {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

/* ---------------------------------------------------------------- Line item */
.cart-line {
  display: grid;
  grid-template-columns: 72px 1fr auto;
  grid-template-areas:
    "media info     remove"
    "media controls controls";
  gap: var(--space-xs) var(--space-md);
  align-items: start;
  padding-block-end: var(--space-lg);
  border-block-end: var(--hairline);
  transition: opacity var(--transition-fast);
}
.cart-drawer__items > .cart-line:last-child {
  padding-block-end: 0;
  border-block-end: none;
}

.cart-line__media {
  grid-area: media;
  inline-size: 72px;
  block-size: 72px;
  overflow: hidden;
  background: var(--color-surface);
  border: var(--hairline);
}
.cart-line__img { inline-size: 100%; block-size: 100%; object-fit: cover; }

.cart-line__info { grid-area: info; min-inline-size: 0; }
.cart-line__name {
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--color-text);
  /* 2-line clamp with the space reserved, so a long name cannot make
     the line taller than a short one. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  min-block-size: 2.6em;
}
.cart-line__name:hover { color: var(--color-accent-strong); }
.cart-line__sku {
  font-family: var(--font-mono);
  font-size: var(--fs-tiny);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-faint);
}
.cart-line__unit {
  font-family: var(--font-mono);
  font-size: var(--fs-tiny);
  color: var(--color-text-muted);
}
.cart-line__options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-block-start: var(--space-xs);
  padding: 0;
  list-style: none;
  font-family: var(--font-mono);
  font-size: var(--fs-tiny);
  line-height: 1.4;
  color: var(--color-text-muted);
}
.cart-line__option {
  min-inline-size: 0;
  padding: 3px var(--space-xs);
  border: var(--hairline);
  border-radius: var(--radius-chip, 2px);
  background: var(--color-surface);
  overflow-wrap: anywhere;
}

.cart-line__controls {
  grid-area: controls;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-block-start: var(--space-xs);
}

.cart-line__remove {
  grid-area: remove;
  min-inline-size: var(--step-size);
  min-block-size: var(--step-size);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-faint);
  transition: color var(--transition-fast), background-color var(--transition-fast);
}
.cart-line__remove:hover { color: var(--color-danger); background: var(--color-surface); }
.cart-line__remove .icon { inline-size: 16px; block-size: 16px; }

.cart-line__total {
  font-family: var(--font-heading);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  color: var(--color-text);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Per-line busy state (§2.6 state 4). Only the affected line dims —
   the rest of the drawer stays fully interactive. */
.cart-line.is-busy .cart-line__controls,
.cart-line.is-busy .cart-line__total { opacity: .5; }
.cart-line.is-busy { position: relative; }

/* §2.7 — the just-added line, briefly distinguished at the top of the
   list. Drawn with an INSET SHADOW rather than a border + padding: a
   border would grow the box and push the grid columns sideways, and the
   whole point of this state is that nothing moves. */
.cart-line.is-new {
  background: var(--color-accent-soft);
  box-shadow: inset 3px 0 0 0 var(--color-accent);
  transition: background-color var(--transition-base), box-shadow var(--transition-base);
}
[dir="rtl"] .cart-line.is-new { box-shadow: inset -3px 0 0 0 var(--color-accent); }

/* ---------------------------------------------------------------- Stepper */
.qty {
  display: inline-flex;
  align-items: center;
  border: var(--hairline-strong);
  border-radius: var(--radius-control);
}
.qty__btn {
  inline-size: var(--step-size);
  block-size: var(--step-size);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: var(--fs-md);
  line-height: 1;
  color: var(--color-text);
  transition: background-color var(--transition-fast), color var(--transition-fast);
}
.qty__btn:hover:not(:disabled) { background: var(--color-accent-soft); color: var(--color-accent-deep); }
.qty__btn:disabled { color: var(--color-text-faint); cursor: not-allowed; }
.qty__btn:focus-visible { outline: 2px solid var(--color-accent-strong); outline-offset: -2px; }
.qty__value {
  min-inline-size: 40px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
  border-inline: var(--hairline);
  align-self: stretch;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ---------------------------------------------------------------- Empty state */
.cart-drawer__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-md);
  text-align: center;
  padding-block: var(--space-3xl);
  color: var(--color-text-muted);
}
.cart-drawer__empty-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 64px;
  block-size: 64px;
  border: var(--hairline-strong);
  color: var(--color-text-faint);
}
.cart-drawer__empty-icon .icon { inline-size: 28px; block-size: 28px; }
.cart-drawer__empty-title {
  font-family: var(--font-heading);
  font-size: var(--fs-lg);
  font-weight: var(--fw-semibold);
  color: var(--color-text);
}
.cart-drawer__empty-text { font-size: var(--fs-sm); max-inline-size: 28ch; }

/* ---------------------------------------------------------------- Loading skeleton
   §2.6 state 2 — "skeletons match the real line item's dimensions so
   nothing shifts when data lands". Rather than hand-tuning pixel values
   that would silently drift the next time .cart-line changes, the
   skeleton REPEATS .cart-line's geometry declarations verbatim — same
   grid, same areas, same gaps, same last-child rule — and each bar is
   sized in em against the same font-size token as the text it stands in
   for. That makes the two heights equal by construction:
     info 2.6em@14 + 1.5em@11 + 1.5em@11  = 69.4px
     + row gap 4 + controls margin 4 + stepper 46 = 123.4px
   which is exactly what a rendered .cart-line measures. */
.cart-drawer__skeleton {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}
.skeleton-line {
  display: grid;
  grid-template-columns: 72px 1fr auto;
  grid-template-areas:
    "media info     remove"
    "media controls controls";
  gap: var(--space-xs) var(--space-md);
  align-items: start;
  padding-block-end: var(--space-lg);
  border-block-end: var(--hairline);
}
.cart-drawer__skeleton > .skeleton-line:last-child {
  padding-block-end: 0;
  border-block-end: none;
}

.skeleton-line__media { grid-area: media; inline-size: 72px; block-size: 72px; }
.skeleton-line__info  { grid-area: info; min-inline-size: 0; }
.skeleton-line__remove {
  grid-area: remove;
  inline-size: var(--step-size);
  block-size: var(--step-size);
}
.skeleton-line__controls {
  grid-area: controls;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-block-start: var(--space-xs);
}

.skeleton-line__bar { display: block; }
/* Each bar mirrors the metrics of the element it replaces. */
.skeleton-line__bar--name {
  font-size: var(--fs-sm);
  block-size: 2.6em;             /* == .cart-line__name min-block-size */
  inline-size: 85%;
}
.skeleton-line__bar--sku {
  font-size: var(--fs-tiny);
  block-size: 1.5em;             /* == .cart-line__sku line box */
  inline-size: 45%;
}
.skeleton-line__bar--unit {
  font-size: var(--fs-tiny);
  block-size: 1.5em;             /* == .cart-line__unit line box */
  inline-size: 60%;
}
.skeleton-line__bar--qty {
  /* == .qty: 44px of content plus its 1px border on each block edge. */
  block-size: calc(var(--step-size) + 2px);
  inline-size: 132px;
}
.skeleton-line__bar--total {
  font-size: var(--fs-md);
  block-size: 1.4em;
  inline-size: 72px;
}

.skeleton-line__media,
.skeleton-line__remove,
.skeleton-line__bar {
  background: linear-gradient(90deg,
    var(--drawer-skeleton-1) 0%,
    var(--drawer-skeleton-2) 50%,
    var(--drawer-skeleton-1) 100%);
  background-size: 200% 100%;
  animation: skeleton-sweep 1.2s var(--ease-inout) infinite;
}
@keyframes skeleton-sweep {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}

/* ---------------------------------------------------------------- Error state */
.cart-drawer__error {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-md);
  margin-block-end: var(--space-lg);
  border: 1px solid var(--color-danger);
  background: var(--color-bg);
}
.cart-drawer__error-text { font-size: var(--fs-sm); color: var(--color-text); }
.cart-drawer__error-actions { display: flex; gap: var(--space-sm); }

/* ---------------------------------------------------------------- Undo bar
   Sits above the footer, inside the panel, so it is reachable by the
   same focus trap and cannot be scrolled away from. */
.cart-drawer__undo {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-sm) var(--space-lg);
  background: var(--undo-bg);
  color: var(--undo-text);
}
.cart-drawer__undo-text {
  font-size: var(--fs-sm);
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cart-drawer__undo-btn {
  flex: none;
  min-block-size: var(--step-size);
  padding-inline: var(--space-sm);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-accent-on-dark);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.cart-drawer__undo-btn:focus-visible { outline: 2px solid var(--color-accent-on-dark); outline-offset: 2px; }

/* ---------------------------------------------------------------- Footer */
.cart-drawer__footer {
  flex: none;
  padding: var(--space-lg);
  /* Respect the home-button inset on iOS so the CTAs are never under it. */
  padding-block-end: calc(var(--space-lg) + env(safe-area-inset-bottom, 0px));
  border-block-start: var(--hairline);
  background: var(--color-surface);
}
.cart-drawer__summary { margin-block-end: var(--space-md); }
/* Reuses the .spec-row component from components.css verbatim — the
   drawer adds no summary-row styling of its own. */
.cart-drawer__summary .spec-row:first-child { border-block-start: none; }
.cart-drawer__summary .spec-row--total .spec-row__value { font-size: var(--fs-lg); }
.cart-drawer__summary .spec-row--total { border-block-start: var(--hairline-strong); }
.cart-drawer__note {
  font-size: var(--fs-tiny);
  color: var(--color-text-muted);
  margin-block-end: var(--space-md);
}
.cart-drawer__actions { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-sm); }
.cart-drawer__actions .btn { inline-size: 100%; padding-inline: var(--space-sm); }

/* ---------------------------------------------------------------- Header badge pulse
   §2.7 — the count animates on change. Transform only, and suppressed
   under reduced motion by the block at the foot of this file.
   ---------------------------------------------------------------- */
@keyframes cart-badge-bump {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.35); }
  100% { transform: scale(1); }
}
.site-header__badge.is-bumped { animation: cart-badge-bump var(--dur-base) var(--ease-out); }

/* ---------------------------------------------------------------- Scroll lock
   The page must not shift when the scrollbar is removed. JS measures the
   scrollbar and publishes --scrollbar-w; body and every FIXED full-width
   or edge-anchored element get the same compensation, because a fixed
   element's containing block is the viewport and body padding does not
   move it.
   ---------------------------------------------------------------- */
body.is-scroll-locked { overflow: hidden; padding-inline-end: var(--scrollbar-w, 0px); }
/* Only while the header is OUT OF FLOW. On the home page it is absolute at
   rest and fixed once docked, so body padding cannot move it and it needs
   its own compensation. On internal pages (.page--flat) it is sticky, i.e.
   a normal in-flow child of body — the body's padding already shifts it, so
   compensating again would push its contents in by twice the scrollbar. */
body.is-scroll-locked:not(.page--flat) .site-header { padding-inline-end: var(--scrollbar-w, 0px); }
body.is-scroll-locked .social-rail,
body.is-scroll-locked .back-to-top { margin-inline-end: var(--scrollbar-w, 0px); }

/* ---------------------------------------------------------------- Mobile */
@media (max-width: 767px) {
  .cart-drawer__panel {
    /* ~92vw, so a strip of scrim stays visible and the panel reads as
       an overlay rather than a new page. */
    inline-size: var(--drawer-mobile-inline);
  }
  .cart-drawer__header,
  .cart-drawer__body,
  .cart-drawer__footer { padding-inline: var(--space-md); }
  .cart-drawer__undo { padding-inline: var(--space-md); }
  .cart-drawer__actions { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------------- Reduced motion
   §2.4 — no slide, no fade, instant show/hide. The JS module also skips
   its transition bookkeeping, so nothing waits on a transitionend that
   will never fire.
   ---------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .cart-drawer__panel,
  .cart-drawer__scrim { transition: none; }
  .skeleton-line__media,
  .skeleton-line__remove,
  .skeleton-line__bar { animation: none; }
  .site-header__badge.is-bumped { animation: none; }
  .cart-line { transition: none; }
}
