/* redesign-components.css — Track A shared components.
   Built once here and reused across homepage, catalog and dashboard, so the
   product card on the homepage and the one on the catalog page are the SAME
   component rather than two that drift apart.

   Requires redesign-tokens.css. Not loaded on store.html / product.html.
   Mobile-first: base rules are the phone; breakpoints scale up. */

/* ---------------- Base ---------------- */
*, *::before, *::after { box-sizing: border-box; }

/* The `hidden` attribute must always win.
   The browser's own rule is `[hidden] { display: none }`, but ANY author
   declaration that sets `display` beats it, regardless of specificity -- and
   this file gives `display` to buttons, cards and panels. That silently broke
   every element the app hides by attribute: the dashboard's per-product Save
   button (revealed only once a price is edited) sat visible on every row, and
   the same applied to inline errors and modal panels.
   One rule here removes the whole class of bug, so no component has to
   remember to re-hide itself. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--color-background);
  color: var(--color-text-primary);
  font-family: var(--font-body);
  font-size: var(--type-body);
  font-weight: var(--weight-regular);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;            /* zero horizontal scrolling, ever */
}

img, svg, video { max-width: 100%; height: auto; display: block; }

/* The UA stylesheet gives <figure> and <blockquote> a 40px inline margin,
   which silently narrows any card built on them. Zero it once, here. */
figure, blockquote { margin: 0; }

a { color: inherit; }

/* One focus style for the whole site. Never remove an outline without a
   replacement — keyboard users need to see where they are. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--color-text-primary);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Visually hidden, still announced. For labels that would be redundant on
   screen (a search field with an icon and a placeholder) but that assistive
   tech still needs. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------------- Layout ---------------- */
.wrap, .container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-margin);
}

.section { padding-block: var(--section-pad); }

/* ---------------- Typography ---------------- */
h1, h2, h3, h4 { margin: 0 0 var(--space-s); line-height: var(--leading-heading); }

/* Cormorant Garamond is deliberately rare. Hero, section titles and editorial
   statements only — never body, buttons, labels, forms or table data. */
.display, h1, h2 {
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
}

h1, .type-hero {
  font-size: var(--type-hero);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-hero);
  /* Cormorant is a delicate face; at display size it needs optical sizing on
     and the old-style figures off, or the headline looks thin rather than fine. */
  font-optical-sizing: auto;
  text-wrap: balance;
}

h2, .type-section {
  font-size: var(--type-section);
  letter-spacing: var(--tracking-heading);
  text-wrap: balance;
}

h3, h4, .card-title {
  font-family: var(--font-body);
  font-size: var(--type-card);
  font-weight: var(--weight-semibold);
}

.eyebrow {
  margin: 0 0 var(--space-s);
  font-size: var(--type-label);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  /* accent-dark, NOT accent. Measured 2026-08-26: #B56A2D on ivory is
     3.75:1 and fails AA for normal text (color-system.md claims 4.15:1 —
     that figure is wrong). Eyebrows are 12px, so they are not "large text"
     and cannot use the base accent. #8A4C1D is 6.06:1.
     The .band-dark override below flips this back, because on #111111 the
     relationship inverts: accent is 4.55:1 (pass), accent-dark 2.82:1 (fail). */
  color: var(--color-accent-dark);
}

.lede {
  font-size: calc(var(--type-body) + 2px);
  line-height: var(--leading-body);
  color: var(--color-text-secondary);
  margin: 0 0 var(--space-m);
  max-width: 56ch;
}

.text-secondary { color: var(--color-text-secondary); }
.small { font-size: var(--type-small); }

/* Hand-drawn underline under the hero's emphasised phrase. Inline SVG, not a
   font effect — one specific, non-generic touch, per homepage-layout.md. */
.underline-accent { position: relative; white-space: nowrap; }
.underline-accent > svg {
  position: absolute;
  left: 0; right: 0; bottom: -0.18em;
  width: 100%; height: 0.28em;
  color: var(--color-accent);
  overflow: visible;
}

/* ---------------- Buttons ---------------- */
.btn-primary, .btn-secondary, .btn-accent {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  min-height: var(--control-height);
  padding-inline: var(--space-m);
  border-radius: var(--radius-button);
  font-family: var(--font-body);
  font-size: var(--type-body);
  font-weight: var(--weight-semibold);
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background var(--motion-base) var(--ease-out),
              border-color var(--motion-base) var(--ease-out),
              color var(--motion-base) var(--ease-out);
  /* Full-width on mobile, per responsive-guidelines.md. */
  width: 100%;
}

.btn-primary {
  background: var(--color-text-primary);
  color: #FFFFFF;
}
.btn-primary:hover { background: #2A2A2A; }

.btn-secondary {
  background: transparent;
  color: var(--color-text-primary);
  border-color: var(--color-text-primary);
  /* No shadow, ever, on secondary buttons. */
}
.btn-secondary:hover { background: rgba(23, 23, 23, 0.04); }

/* Accent fill uses --color-accent-dark, NOT --color-accent: white on #B56A2D
   is 4.15:1 and fails AA. #8A4C1D clears it at 6.70:1.
   Hover lightens toward the base accent — the inverse of the black button,
   correct here because the resting state needed the darker value. */
.btn-accent {
  background: var(--color-accent-dark);
  color: #FFFFFF;
}
.btn-accent:hover { background: var(--color-accent); }

.btn-primary[disabled], .btn-secondary[disabled], .btn-accent[disabled],
.btn-primary[aria-disabled="true"], .btn-accent[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

@media (min-width: 640px) {
  .btn-primary, .btn-secondary, .btn-accent { width: auto; }
}

/* ---------------- Forms ---------------- */
.field { display: block; margin-bottom: var(--space-s); }

/* Labels are always real, visible elements above the field — never
   placeholder-only. Placeholders vanish on first keystroke and are not
   announced consistently by screen readers. */
.field > label, .field-label {
  display: block;
  margin-bottom: var(--space-xs);
  font-size: var(--type-small);
  font-weight: var(--weight-medium);
  color: var(--color-text-primary);
}

/* Every text-like type is listed explicitly. Missing one is not cosmetic:
   `password` was absent at first and every password field on the site fell
   back to the browser default, rendering 21px tall on the login and
   password-reset screens. */
input[type="text"], input[type="email"], input[type="search"],
input[type="number"], input[type="tel"], input[type="password"],
input[type="url"], input[type="date"], select, textarea {
  width: 100%;
  min-height: var(--control-height);
  padding: 0 var(--space-m);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-input);
  background: var(--color-surface);
  color: var(--color-text-primary);
  font-family: var(--font-body);
  font-size: var(--type-body);   /* 16px+ also stops iOS zooming on focus */
  transition: border-color var(--motion-base) var(--ease-out);
}
textarea { padding-block: var(--space-s); min-height: 96px; resize: vertical; }

input:focus, select:focus, textarea:focus { border-color: var(--color-text-primary); }

/* The wrapper carries the visible border once a toggle sits inside it. */
.password-field input { width: 100%; }

::placeholder { color: var(--color-text-secondary); opacity: 1; }

.form-hint { margin: var(--space-xs) 0 0; font-size: var(--type-small); color: var(--color-text-secondary); }

.form-error {
  margin: var(--space-xs) 0 0;
  font-size: var(--type-small);
  color: var(--color-error);
}
/* Errors carry an icon and text, never colour alone, and are announced via
   role="alert" / aria-live in the markup. */
.form-error::before { content: "\26A0  "; }

/* Password show/hide. Built at runtime by makePasswordField() in utils.js, so
   it never appears in any page's markup — it has to be styled here or the
   input collapses to 21px and the toggle to 6px on every auth screen.
   44px target, up from the 32px the old stylesheet used. */
.password-field { position: relative; margin-bottom: var(--space-s); }
.password-field input {
  margin-bottom: 0;
  padding-right: 52px;          /* room for the toggle */
}
.password-toggle-btn {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: none;
  border: 0;
  border-radius: var(--radius-pill);
  color: var(--color-text-secondary);
  font-size: 20px;
  cursor: pointer;
  transition: color var(--motion-base) var(--ease-out);
}
.password-toggle-btn:hover { color: var(--color-text-primary); }

/* ---------------- Product card ---------------- */
.product-card {
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-1);
  padding: var(--space-m);
  cursor: pointer;
  transition: box-shadow var(--motion-slow) var(--ease-out),
              transform var(--motion-slow) var(--ease-out);
}
.product-card:hover { box-shadow: var(--shadow-2); transform: translateY(-4px); }
.product-card:hover .card-thumb-img { transform: scale(1.02); }

.card-thumb {
  position: relative;
  aspect-ratio: var(--cover-ratio);
  border-radius: var(--radius-image);
  background: var(--cover-mat);
  overflow: hidden;
  margin-bottom: var(--space-s);
}
/* cover + center top, matching the live fix shipped 2026-08-23 — NOT `contain`.
   component-specifications.md tells you to use `contain`; that guidance is
   wrong for this catalogue and was already tried and reverted. `contain` fits
   the whole image inside the box, so every cover whose ratio differs from the
   container's gets empty strips down both sides — only 1 of 24 covers was an
   exact fit. `center top` anchors the crop to the bottom, where these mockups
   have shadow and base, instead of through the title at the top.

   A real <img> rather than a background, so covers carry alt text and can be
   lazily loaded — the catalogue renders 44 of them. */
.card-thumb-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Two renderings share this class: a real <img> on the catalogue and
     homepage, and a background-image <div> in the store cards and the
     dashboard's product picker. Only object-fit was set, so the <div> form
     fell back to background-size:auto and displayed the 1536px source at
     full size -- the "overly zoomed" thumbnails. Both are covered now. */
  object-fit: cover;
  object-position: center top;
  background-size: cover;
  background-position: center top;
  background-repeat: no-repeat;
  transition: transform var(--motion-slow) var(--ease-out);
}

.card-badges {
  position: absolute;
  top: var(--space-xs);
  left: var(--space-xs);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}

.card-title {
  margin: 0 0 var(--space-xs);
  font-size: var(--type-card);
  font-weight: var(--weight-semibold);
  /* Product names are author-supplied and can contain very long unbroken
     tokens. Without this they set the card's min-content width and blow the
     grid track out again, which is what made the columns unequal. */
  overflow-wrap: anywhere;
}

.card-desc {
  margin: 0 0 var(--space-s);
  overflow-wrap: anywhere;
  font-size: var(--type-body);
  color: var(--color-text-secondary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.card-cta-hint {
  margin: auto 0 0;
  font-size: var(--type-small);
  color: var(--color-text-secondary);
  display: flex;
  align-items: center;
  gap: 6px;
}

/* Optional price slot. Used on the reseller dashboard (base price) only —
   never on the public catalog, which has no single true price to show. */
.card-price {
  margin: 0 0 var(--space-xs);
  font-size: var(--type-body);
  font-weight: var(--weight-semibold);
}

/* ---------------- Badges ---------------- */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  font-size: var(--type-label);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.02em;
  background: var(--color-accent-tint);
  color: var(--color-accent-dark);   /* dark text on tint — passes AA */
}
.badge-bestseller { background: var(--color-accent-dark); color: #FFFFFF; }
/* Success at full strength is 4.32:1 on its own tint — just under AA.
   The darker green clears it. */
.badge-new { background: rgba(47, 125, 75, 0.12); color: var(--color-success-dark); }
.badge-featured { background: var(--color-accent-tint); color: var(--color-accent-dark); }

/* ---------------- Grids ---------------- */
/* Explicit repeat(N, 1fr) at every breakpoint — never auto-fit. auto-fit
   collapses empty columns and stretches whatever is left, which is why a
   single product once rendered as one giant card. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-s);
}
@media (min-width: 640px) {
  .card-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-m); }
}
@media (min-width: 1024px) {
  .card-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* ---------------- Navigation ---------------- */
.site-nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--color-background);
  border-bottom: 1px solid var(--color-border);
}
.nav-inner {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding: var(--space-s) var(--container-margin);
}
.nav-logo {
  display: inline-flex;
  align-items: center;
  min-height: 44px;               /* touch target — the logo is a link home */
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: var(--weight-semibold);
  letter-spacing: -0.01em;
  text-decoration: none;
  color: var(--color-text-primary);
}

.nav-links {
  display: none;
  align-items: center;
  gap: var(--space-m);
}
.nav-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;               /* touch target */
  font-size: var(--type-small);
  font-weight: var(--weight-medium);
  color: var(--color-text-secondary);
  text-decoration: none;
  transition: color var(--motion-base) var(--ease-out);
}
.nav-links a:hover, .nav-links a.active { color: var(--color-text-primary); }
.nav-links a.nav-cta { color: #FFFFFF; }

.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-button);
  color: var(--color-text-primary);
  font-size: 20px;
  cursor: pointer;
}

/* Open state on mobile: stack the links below the bar. */
.nav-links.open {
  display: flex;
  position: absolute;
  left: 0; right: 0;
  top: 100%;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-xs);
  padding: var(--space-s) var(--container-margin) var(--space-m);
  background: var(--color-background);
  border-bottom: 1px solid var(--color-border);
}

@media (min-width: 1024px) {
  .nav-links { display: flex; }
  .nav-toggle { display: none; }
  .nav-links.open { position: static; flex-direction: row; padding: 0; border: 0; background: none; }
}

/* ---------------- Dark band (Why Zyentora) ---------------- */
/* Used exactly once per page. Not a secondary theme — do not reuse it for
   cards, footers or other sections. */
.band-dark {
  background: var(--color-dark-band);
  color: var(--color-text-on-dark);
  padding-block: var(--section-pad);
}
.band-dark .lede, .band-dark .text-secondary { color: rgba(255, 255, 255, 0.72); }
.band-dark .eyebrow { color: var(--color-accent); }

/* ---------------- Footer ---------------- */
.site-footer {
  background: var(--color-background);
  border-top: 1px solid var(--color-border);
  padding-block: var(--space-xl) var(--space-m);
}
.footer-full-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-l);
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-margin);
}
.footer-col { display: flex; flex-direction: column; gap: var(--space-xs); }
.footer-col h4 {
  margin: 0 0 var(--space-xs);
  font-size: var(--type-label);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}
.footer-col a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--type-small);
  color: var(--color-text-secondary);
  text-decoration: none;
}
.footer-col a:hover { color: var(--color-text-primary); }
.footer-logo {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: var(--weight-semibold);
}
.footer-tagline { margin: var(--space-xs) 0 0; font-size: var(--type-small); color: var(--color-text-secondary); }
.footer-full-bottom {
  max-width: var(--container-max);
  margin: var(--space-l) auto 0;
  padding: var(--space-m) var(--container-margin) 0;
  border-top: 1px solid var(--color-border);
  font-size: var(--type-small);
  color: var(--color-text-secondary);
}
@media (min-width: 640px) { .footer-full-inner { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .footer-full-inner { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); } }

/* ---------------- Filter chips + search ---------------- */
.category-filter {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);           /* 8px minimum between adjacent touch targets */
  margin-bottom: var(--space-m);
}
.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 var(--space-s);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  color: var(--color-text-secondary);
  font-family: var(--font-body);
  font-size: var(--type-small);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: border-color var(--motion-base) var(--ease-out),
              color var(--motion-base) var(--ease-out);
}
.filter-chip:hover { border-color: var(--color-text-primary); color: var(--color-text-primary); }
.filter-chip.active {
  background: var(--color-text-primary);
  border-color: var(--color-text-primary);
  color: #FFFFFF;
}
.chip-count { opacity: 0.6; font-variant-numeric: tabular-nums; }

.hub-search { position: relative; margin-bottom: var(--space-s); }
.hub-search input { padding-left: 48px; }
.hub-search > i {
  position: absolute;
  left: var(--space-s);
  top: 50%;
  transform: translateY(-50%);
  color: var(--color-text-secondary);
  font-size: 20px;
  pointer-events: none;
}

/* ---------------- States: empty, loading, error ---------------- */
/* Same pattern on every list view across every page — a person should not get
   a different "nothing here" style depending on which page they are on. */
.state-message {
  grid-column: 1 / -1;
  padding: var(--space-xl) var(--space-m);
  text-align: center;
  color: var(--color-text-secondary);
  font-size: var(--type-body);
}
.state-message .btn-secondary, .state-message .btn-primary { margin-top: var(--space-s); }

.skeleton {
  background: linear-gradient(90deg,
    rgba(23,23,23,0.05) 25%, rgba(23,23,23,0.09) 37%, rgba(23,23,23,0.05) 63%);
  background-size: 400% 100%;
  animation: skeleton-sweep 1.4s ease-in-out infinite;
  border-radius: var(--radius-card);
}
@keyframes skeleton-sweep { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

/* Skeleton matches the card it replaces, so nothing jumps when data lands. */
.skeleton-card { aspect-ratio: var(--cover-ratio); border-radius: var(--radius-card); }

/* ---------------- Avatar / initials ---------------- */
.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-pill);
  background: var(--color-text-primary);
  color: #FFFFFF;
  font-weight: var(--weight-semibold);
  font-size: var(--type-small);
}

/* ---------------- Progress bar (payout) ---------------- */
.progress {
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--color-border);
  overflow: hidden;
}
.progress > span {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--color-accent-dark);
  transition: width var(--motion-slow) var(--ease-out);
}

/* ---------------- Tabs ---------------- */
.tabs { display: flex; gap: var(--space-m); border-bottom: 1px solid var(--color-border); }
.tab {
  min-height: 44px;
  padding: 0 2px;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--color-text-secondary);
  font-family: var(--font-body);
  font-size: var(--type-small);
  font-weight: var(--weight-medium);
  cursor: pointer;
}
.tab[aria-selected="true"] {
  color: var(--color-text-primary);
  border-bottom-color: var(--color-accent-dark);
}

/* ---------------- Modal ---------------- */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  background: rgba(23, 23, 23, 0.45);
}
.modal-backdrop[hidden] { display: none; }
.modal {
  position: relative;
  width: 100%;
  max-height: 92vh;
  overflow-y: auto;
  background: var(--color-surface);
  border-radius: var(--radius-card) var(--radius-card) 0 0;
  padding: var(--space-m);
  box-shadow: var(--shadow-2);
}
.modal-close {
  position: absolute;
  top: var(--space-s);
  right: var(--space-s);
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--color-background);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  color: var(--color-text-primary);
  font-size: 18px;
  cursor: pointer;
}
@media (min-width: 640px) {
  .modal-backdrop { align-items: center; padding: var(--space-m); }
  .modal { max-width: 560px; border-radius: var(--radius-card); padding: var(--space-l); }
}

/* ---------------- Newsletter ---------------- */
.newsletter-form { display: flex; flex-direction: column; gap: var(--space-xs); }
@media (min-width: 640px) {
  .newsletter-form { flex-direction: row; }
  .newsletter-form input { flex: 1; }
}

/* ---------------- Reduced motion ---------------- */
/* Required, not optional. Motion-sensitive users need a working escape hatch. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .product-card:hover { transform: none; }
  .product-card:hover .card-thumb-img { transform: none; }
}
