/* ==========================================================================
   Components: shared across storefront, account area and dashboard
   ========================================================================== */

/* ------------------------------------------------------------------ button */

.btn {
  --btn-bg: var(--accent);
  --btn-fg: #fff;
  --btn-border: transparent;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-5);
  min-height: 2.875rem;              /* comfortable touch target */
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: 550;
  line-height: 1;
  color: var(--btn-fg);
  background: var(--btn-bg);
  border: 1px solid var(--btn-border);
  border-radius: var(--radius);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--duration) var(--ease),
              border-color var(--duration) var(--ease),
              color var(--duration) var(--ease),
              transform var(--duration) var(--ease);
}
.btn:hover { --btn-bg: var(--accent-hover); color: var(--btn-fg); }
.btn:active { transform: translateY(1px); }
.btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

.btn--secondary {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  --btn-border: var(--line-strong);
}
.btn--secondary:hover {
  --btn-bg: var(--surface-sunken);
  --btn-fg: var(--ink);
  --btn-border: var(--ink-subtle);
}

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--accent);
  --btn-border: transparent;
  padding-inline: var(--space-3);
}
.btn--ghost:hover { --btn-bg: var(--accent-soft); --btn-fg: var(--accent-ink); }

/* A button on a brand-red surface. The primary crimson would disappear into
   that ground, so this is an outlined white button that inverts on hover.
   It exists as a class because the inline styles it replaces outranked the
   :hover rule, leaving white text on a cream background on hover. */
.btn--on-brand {
  --btn-bg: transparent;
  --btn-fg: #fff;
  --btn-border: rgb(255 255 255 / 0.45);
}
.btn--on-brand:hover {
  --btn-bg: #fff;
  --btn-fg: var(--surface-brand);
  --btn-border: #fff;
}

.btn--lg { padding: var(--space-4) var(--space-6); font-size: var(--text-md); min-height: 3.25rem; }
.btn--sm { padding: var(--space-2) var(--space-3); font-size: var(--text-sm); min-height: 2.25rem; }
.btn--block { width: 100%; }

/* -------------------------------------------------------------------- card */

.card {
  background: var(--surface-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
}
.card--flush { padding: 0; overflow: hidden; }

.card--interactive {
  transition: border-color var(--duration) var(--ease),
              box-shadow var(--duration) var(--ease),
              transform var(--duration) var(--ease);
}
.card--interactive:hover {
  border-color: var(--line-strong);
  box-shadow: var(--shadow);
  transform: translateY(-2px);
}

/* --------------------------------------------------------------- form field */

.field { display: grid; gap: var(--space-2); align-content: start; }

.field__label {
  font-size: var(--text-sm);
  font-weight: 550;
  color: var(--ink);
}

.field__hint { font-size: var(--text-sm); color: var(--ink-subtle); }

.field__error { font-size: var(--text-sm); color: var(--danger); }

.input {
  width: 100%;
  padding: var(--space-3) var(--space-4);
  min-height: 2.875rem;
  font-size: var(--text-base);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  transition: border-color var(--duration) var(--ease),
              box-shadow var(--duration) var(--ease);
}
.input::placeholder { color: var(--ink-subtle); }
.input:hover { border-color: var(--ink-subtle); }
.input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
}
.input[aria-invalid="true"] { border-color: var(--danger); }

/* A select computes a slightly shorter box than a text input under identical
   padding, which reads as a stepped edge when the two sit side by side. Fixing
   the height on both single-line controls settles it; the textarea keeps its
   own min-height so it can still be dragged. */
input.input, select.input { height: 3.25rem; }

/* ------------------------------------------------------------------- badge */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.2em 0.6em;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
  border-radius: var(--radius-pill);
  background: var(--surface-sunken);
  color: var(--ink-muted);
  border: 1px solid var(--line);
}
.badge--accent { background: var(--accent-soft); color: var(--accent-ink); border-color: transparent; }
.badge--warn   { background: var(--warn-soft);   color: var(--warn);       border-color: transparent; }
.badge--danger { background: var(--danger-soft); color: var(--danger);     border-color: transparent; }

/* ---------------------------------------------------------------- section head
   One consistent block for every section opener, so the vertical rhythm of the
   page is set once rather than re-tuned per section. */

.section-head {
  display: grid;
  gap: var(--space-3);
  margin-bottom: var(--space-7);
  max-width: var(--measure);
}
.section-head--centred {
  justify-items: center;
  text-align: center;
  margin-inline: auto;
}

/* ------------------------------------------------------------------- grid */

.grid { display: grid; gap: var(--space-5); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr)); }

/* A short gold rule above section eyebrows. This is the one place the logo's
   gold is load-bearing in the interface: enough to register as part of the
   identity, not enough to compete with the crimson for attention. */
.eyebrow--ruled { display: flex; align-items: center; gap: var(--space-3); }
.eyebrow--ruled::before {
  content: "";
  width: var(--space-6);
  height: 2px;
  background: var(--brand-gold);
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}
.section-head--centred .eyebrow--ruled::after {
  content: "";
  width: var(--space-6);
  height: 2px;
  background: var(--brand-gold);
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}

/* ------------------------------------------------------------ product card
   Used by the home page, the catalogue listing and the related-products rail,
   so it lives here rather than in any one page stylesheet.

   Every product image is normalised to an 800x800 tile at build time, padded
   with its own background colour. Roughly a third of the supplier's shots are
   on black, so the tile is inset and rounded: a dark square sitting inside the
   card's own radius reads as deliberate, where a flush black rectangle against
   a rounded white card reads as broken. */

.product-card {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  height: 100%;
  transition: border-color var(--duration) var(--ease),
              box-shadow var(--duration) var(--ease),
              transform var(--duration) var(--ease);
}
.product-card:hover {
  color: inherit;
  border-color: var(--line-strong);
  box-shadow: var(--shadow);
  transform: translateY(-2px);
}

.product-card__media {
  position: relative;
  margin: var(--space-3) var(--space-3) 0;
  aspect-ratio: 1;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-sunken);
  display: grid;
  place-items: center;
  color: var(--line-strong);
  flex-shrink: 0;
}
.product-card__media svg { width: 28%; height: 28%; }

/* The tile is already square and already padded, so it fills its box exactly.
   No object-fit letterboxing, and no second background showing through. */
.product-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.product-card__flag { position: absolute; top: var(--space-3); left: var(--space-3); z-index: 1; }

.product-card__body {
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  flex: 1;
}

.product-card__cat {
  font-size: var(--text-xs);
  color: var(--ink-subtle);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: var(--leading-snug);
  /* Two lines maximum, always occupying two lines, so the product name below
     starts at the same height on every card in the row. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: calc(2 * 1.3 * var(--text-xs));
}

.product-card__name {
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--ink);
  line-height: var(--leading-snug);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: calc(2 * 1.3 * var(--text-base));
}

.product-card__sku {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ink-subtle);
  letter-spacing: 0.02em;
}

/* Pushed to the bottom so the call to action lines up across the row however
   long the names above it run. */
.product-card__foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: auto;
  padding-top: var(--space-3);
}

.product-card__cta {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--accent);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.product-card__cta .arrow {
  width: 1.15rem;
  height: 0.7rem;
  overflow: visible;
  transition: transform var(--duration) var(--ease);
}
.product-card:hover .product-card__cta .arrow { transform: translateX(3px); }

.product-card__stock { font-size: var(--text-xs); color: var(--accent); font-weight: 600; }
.product-card__stock--low { color: var(--warn); }
.product-card__stock--out { color: var(--ink-subtle); }


/* ------------------------------------------------------------- image reveal
   Product tiles fade in once decoded. An image already in cache is marked
   instant instead, because fading in something the browser already had reads
   as a flicker rather than a load. */
.product-card__media img {
  opacity: 0;
  transition: opacity 320ms var(--ease);
}
.product-card__media img.is-loaded { opacity: 1; }
.product-card__media img.is-instant { opacity: 1; transition: none; }
.product-card__media.is-loading { background: var(--surface-sunken); }

/* ------------------------------------------------------------------ skeleton
   Used while a page of results is in flight, so the grid holds its shape and a
   click visibly does something instead of leaving the previous page on screen. */
.skeleton {
  position: relative;
  overflow: hidden;
  background: color-mix(in srgb, var(--ink) 6%, var(--surface-sunken));
  border-radius: var(--radius-sm);
}
.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg,
    transparent,
    color-mix(in srgb, var(--surface) 70%, transparent),
    transparent);
  animation: shimmer 1.25s infinite;
}
@keyframes shimmer {100% { transform: translateX(100%); } }

.skeleton--text { display: block; height: 0.7rem; }
.skeleton--lg { height: 0.95rem; }

.product-card--skeleton { pointer-events: none; }
.product-card--skeleton .product-card__media { border-radius: var(--radius); }
.product-card--skeleton .product-card__body { gap: var(--space-3); }

@media (prefers-reduced-motion: reduce) {
  .skeleton::after { animation: none; }
  .product-card__media img { transition: none; }
}
