/* =====================================================================
   EvoTechnik — COMPONENTS
   Layer 3 of 4 · Sprint 2 · 14 August 2026

   Reusable UI. Styled once here, used everywhere. If a component's
   markup is being copied between pages, it belongs in a PHP partial and
   its styling belongs in this file.

   Depends on tokens.css + base.css.
   Every colour is a var(--token). No raw hex — the only exceptions are
   var(--white) and rgba() overlays on dark grounds, which brand.md §3 and §4
   specify literally.

   Mobile first. Breakpoints are literals (640 / 1024 / 1400) because
   custom properties do not work in @media.

   Load order: tokens -> base -> components -> public
   ===================================================================== */


/* =====================================================================
   1. BUTTONS — brand.md §8
   48px min-height, --r-sm, 700 weight.
   ===================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-8);
  min-height: 48px;
  padding: var(--sp-12) var(--sp-24);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--t-fast), border-color var(--t-fast),
              color var(--t-fast), box-shadow var(--t-fast),
              transform var(--t-fast);
}

.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }

.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}

.btn--block { width: 100%; }

.btn--sm {
  min-height: 40px;
  padding: var(--sp-8) var(--sp-16);
  font-size: var(--fs-small);
}

.btn--lg {
  min-height: 54px;
  padding: var(--sp-16) var(--sp-32);
  font-size: 16.5px;
}

/* Primary — the default call to action. */
.btn-primary {
  background: var(--blue);
  border-color: var(--blue);
  color: var(--white);
}
.btn-primary:hover {
  background: var(--blue-hover);
  border-color: var(--blue-hover);
  color: var(--white);
}

/* Secondary — white with a hairline border. */
.btn-secondary {
  background: var(--bg);
  border-color: var(--line);
  color: var(--ink);
}
.btn-secondary:hover {
  background: var(--bg-soft);
  border-color: var(--blue-line);
  color: var(--blue);
}

/* Secondary on a dark ground — footer, hero, dark bands. */
.btn-secondary--on-dark {
  background: transparent;
  border-color: rgba(255, 255, 255, 0.22);
  color: var(--white);
}
.btn-secondary--on-dark:hover {
  background: var(--white);
  border-color: var(--white);
  color: var(--ink);
}

/* WhatsApp green — RESERVED FOR ACTIONS THAT ACTUALLY OPEN WHATSAPP.
   brand.md §2.

   An on-site enquiry form is NOT a WhatsApp action. Every "Get a Quote"
   and "Book Service" button opens the site's own modal, so they all use
   .btn-primary. Painting them green tells the user the button launches
   WhatsApp, and it does not.

   Legitimate uses: a wa.me link, or the floating chat button (which is
   styled by .wa-float rather than this class).

   Never use --wa-green as a generic success colour — that is --green. */
.btn-whatsapp {
  background: var(--wa-green);
  border-color: var(--wa-green);
  color: var(--white);
}
.btn-whatsapp:hover {
  background: var(--wa-green-hover);
  border-color: var(--wa-green-hover);
  color: var(--white);
}

.btn-danger {
  background: var(--red);
  border-color: var(--red);
  color: var(--white);
}
.btn-danger:hover {
  background: var(--red);
  border-color: var(--red);
  color: var(--white);
  filter: brightness(0.92);
}

/* Text-only action, e.g. "View All ->". */
.btn-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-8);
  min-height: 44px;
  color: var(--blue);
  font-weight: var(--fw-bold);
  font-size: var(--fs-body);
}
.btn-link:hover {
  color: var(--blue-hover);
  text-decoration: none;
  gap: var(--sp-12);
}


/* =====================================================================
   2. CARD — brand.md §8
   ===================================================================== */

.card {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
}

.card__body { padding: var(--sp-20); }

.card--hover {
  transition: box-shadow var(--t-base), transform var(--t-base),
              border-color var(--t-base);
}
.card--hover:hover {
  box-shadow: var(--sh-2);
  border-color: var(--blue-line);
  transform: translateY(-3px);
}

/* Card sitting on a dark band. */
.card--on-dark {
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.12);
  color: var(--white);
  box-shadow: none;
}
.card--on-dark:hover { border-color: var(--blue); }


/* =====================================================================
   3. PRODUCT CARD — brand.md §8
   The highest-reuse component in the project. Markup lives in exactly
   one place: includes/product-card.php
   ===================================================================== */

.product-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  transition: box-shadow var(--t-base), transform var(--t-base),
              border-color var(--t-base);
}

.product-card:hover {
  box-shadow: var(--sh-2);
  border-color: var(--blue-line);
  transform: translateY(-3px);
}

/* --- media ------------------------------------------------------- */

.product-card__media {
  position: relative;
  background: var(--bg-soft);
  border-bottom: 1px solid var(--line);
}

.product-card__img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* Editorial badge from products.badge, top-left. */
.product-card__badge {
  position: absolute;
  top: var(--sp-12);
  left: var(--sp-12);
}

/* Discount badge, top-right. */
.product-card__discount {
  position: absolute;
  top: var(--sp-12);
  right: var(--sp-12);
}

/* --- body -------------------------------------------------------- */

.product-card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--sp-8);
  padding: var(--sp-16);
}

.product-card__title {
  margin: 0;
  font-size: 16.5px;
  font-weight: var(--fw-h-bold);
  line-height: 1.3;
}

.product-card__title a { color: var(--black); }
.product-card__title a:hover {
  color: var(--blue);
  text-decoration: none;
}

/* Certification / battery / warranty flags, Sprint 12D.
   flex-wrap matters: at 320px three flags do not fit on one line, and
   a nowrap row here would push the card wider than its column. */
.product-card__flags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
}

.product-card__sku {
  font-size: 12px;
  font-weight: var(--fw-medium);
  color: var(--grey-light);
  letter-spacing: 0.02em;
}

/* Up to 3 rows from product_specs. */
.product-card__specs {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  margin: 0;
  padding: var(--sp-12) 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  list-style: none;
  font-size: var(--fs-small);
}

.product-card__spec {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-12);
  color: var(--grey);
}

.product-card__spec-value {
  color: var(--ink);
  font-weight: var(--fw-semibold);
  text-align: right;
}

/* Push price + button to the bottom regardless of title length. */
.product-card__foot {
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
  margin-top: auto;
  padding-top: var(--sp-8);
}

.product-card__stock { align-self: flex-start; }


/* =====================================================================
   4. PRICE — brand.md §8
   ===================================================================== */

.price-block {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-8);
}

.price {
  font-family: var(--font-heading);
  font-size: 20px;
  font-weight: var(--fw-h-bolder);
  letter-spacing: var(--ls-heading);
  color: var(--blue);
}

.price--lg { font-size: 28px; }

.price-mrp {
  font-size: var(--fs-small);
  color: var(--grey);
  text-decoration: line-through;
}

.price-unit {
  font-size: var(--fs-small);
  color: var(--grey);
}

/* "per Piece · MOQ 10" */
.price-meta {
  font-size: var(--fs-small);
  color: var(--grey);
}


/* =====================================================================
   5. BADGES / PILLS / CHIPS — brand.md §8
   --r-pill, 12px, 700, 4px 10px.
   ===================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-12);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font-size: 12px;
  font-weight: var(--fw-bold);
  line-height: 1.4;
  white-space: nowrap;
}

/* Stock status. Text always present — never colour alone (brand.md §9). */
.badge-stock-in {
  background: rgba(22, 163, 74, 0.10);
  border-color: rgba(22, 163, 74, 0.30);
  color: var(--green);
}

.badge-stock-order {
  background: rgba(217, 119, 6, 0.10);
  border-color: rgba(217, 119, 6, 0.30);
  color: var(--amber);
}

.badge-stock-out {
  background: var(--bg-soft);
  border-color: var(--line);
  color: var(--grey);
}

/* "20% OFF" */
.badge-discount {
  background: var(--discount);
  color: var(--white);
}

/* Sub-category name on a product card. */
.badge-category {
  background: var(--blue-soft);
  border-color: var(--blue-line);
  color: var(--blue);
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: var(--ls-label);
}

/* Manual editorial badge — products.badge. */
/* --- second-hand vehicles (Sprint 12D) ----------------------------
   Three flags, all of them TEXT first (brand.md 9): the colour is a
   second signal, never the only one. --green is the generic success
   colour and is correct here; it is not the WhatsApp green (tokens.css).
   ------------------------------------------------------------------ */

/* "Evotechnik Certified". Solid, because it is the strongest claim on
   the card and has to survive being read at 320px next to a photo. */
.badge-certified {
  background: var(--green);
  color: var(--white);
}

/* "With Battery" / "Without Battery" - a fact, not a status, so it
   borrows the neutral blue of the category badge rather than a
   green/red pair that would imply one is better than the other. */
.badge-battery {
  background: var(--blue-soft);
  border-color: var(--blue-line);
  color: var(--blue);
}

/* "1 Year Warranty". Quietest of the three on purpose. */
.badge-warranty {
  background: var(--bg-soft);
  border-color: var(--line);
  color: var(--ink-2);
}

.badge-editorial {
  background: var(--blue);
  color: var(--white);
}

/* Neutral pill. */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-8);
  padding: var(--sp-8) var(--sp-16);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--bg);
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  color: var(--ink-2);
}

.pill--on-dark {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.16);
  color: var(--white);
}

/* Eyebrow above a section heading. */
.pill--eyebrow {
  padding: var(--sp-8) var(--sp-16);
  border-color: var(--blue-line);
  background: var(--blue-soft);
  color: var(--blue);
  font-size: var(--fs-label);
  font-weight: var(--fw-bold);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.pill--eyebrow.pill--on-dark {
  background: rgba(30, 103, 176, 0.16);
  border-color: rgba(30, 103, 176, 0.5);
  color: var(--white);
}

/* Interactive filter chip. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-8);
  min-height: 44px;
  padding: var(--sp-8) var(--sp-16);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--bg);
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  color: var(--ink-2);
  cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--t-fast), border-color var(--t-fast),
              color var(--t-fast);
}

.chip:hover {
  border-color: var(--blue-line);
  color: var(--blue);
  text-decoration: none;
}

.chip[aria-current="true"],
.chip.is-active {
  background: var(--blue-soft);
  border-color: var(--blue);
  color: var(--blue);
}

.chip__count {
  font-size: 12px;
  font-weight: var(--fw-bold);
  color: var(--grey);
}


/* =====================================================================
   6. FORM CONTROLS — brand.md §8
   48px min-height, --r-sm, 1px var(--input-line) border, blue focus ring,
   label above field, red asterisk on required, inline error in --red.
   ===================================================================== */

.field {
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
}

/* .label lives in base.css — the uppercase field label. */

.label .req {
  color: var(--red);
  margin-left: 2px;
}

.input,
.select,
.textarea {
  width: 100%;
  min-height: 48px;
  padding: var(--sp-12);
  border: 1px solid var(--input-line);
  border-radius: var(--r-sm);
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}

.input::placeholder,
.textarea::placeholder { color: var(--grey-light); }

/* Focus handled in base.css :focus-visible for keyboard users; this
   covers programmatic and mouse focus on form controls specifically. */
.input:focus,
.select:focus,
.textarea:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: var(--focus-ring);
}

.textarea {
  min-height: 110px;
  resize: vertical;
  line-height: var(--lh-body);
}

.select {
  appearance: none;
  padding-right: var(--sp-40);
  /* Caret drawn as an inline SVG data URI so there is no network request
     and no external asset.

     THE ONE PLACE A COLOUR IS NOT A TOKEN. A data URI is an opaque
     string to the CSS engine, so var() cannot be resolved inside it —
     the fill is URL-encoded (%23 = #) and hard-coded to the value of
     --grey. If --grey ever changes, this caret must be changed by hand;
     nothing will warn you. */
  background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%236B7280' d='M1.4 0 6 4.6 10.6 0 12 1.4 6 7.4 0 1.4z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--sp-16) center;
}

/* Read-only field — the pre-filled product name in the quote modal. */
.input[readonly] {
  background: var(--bg-soft);
  color: var(--ink);
  font-weight: var(--fw-semibold);
  cursor: default;
}

/* Error state. */
.input.is-invalid,
.select.is-invalid,
.textarea.is-invalid {
  border-color: var(--red);
}

.error {
  display: block;
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  color: var(--red);
}

.help {
  display: block;
  font-size: var(--fs-small);
  color: var(--grey);
}

/* Honeypot — must be invisible to humans but reachable by bots.
   display:none is skipped by some bots, so move it off-screen instead. */
.hp-field {
  position: absolute !important;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}


/* =====================================================================
   7. ALERTS
   ===================================================================== */

.alert {
  padding: var(--sp-12) var(--sp-16);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  font-size: var(--fs-body);
}

.alert-success {
  background: rgba(22, 163, 74, 0.08);
  border-color: rgba(22, 163, 74, 0.30);
  color: var(--green);
}

.alert-error {
  background: rgba(220, 38, 38, 0.06);
  border-color: rgba(220, 38, 38, 0.28);
  color: var(--red);
}


/* =====================================================================
   8. DROPDOWN — desktop nav menu
   Opens on hover AND on tap/keyboard (.is-open), so it works on a
   touch laptop where hover is unreliable.
   ===================================================================== */

.dropdown { position: relative; }

.dropdown__panel {
  position: absolute;
  top: calc(100% + var(--sp-8));
  left: 0;
  z-index: var(--z-dropdown);
  min-width: 300px;
  max-height: 70vh;
  overflow-y: auto;
  padding: var(--sp-8) 0;
  margin: 0;
  list-style: none;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-3);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px);
  transition: opacity var(--t-fast), transform var(--t-fast),
              visibility var(--t-fast);
}

.dropdown:hover > .dropdown__panel,
.dropdown.is-open > .dropdown__panel {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* Hover-open is a desktop affordance only. */
@media (max-width: 1023px) {
  .dropdown:hover > .dropdown__panel {
    opacity: 0;
    visibility: hidden;
  }
  .dropdown.is-open > .dropdown__panel {
    opacity: 1;
    visibility: visible;
  }
}


/* =====================================================================
   9. MODAL
   ===================================================================== */

.modal {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--sp-16);
}

.modal.is-open { display: flex; }

.modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(11, 14, 19, 0.7);
}

.modal__dialog {
  position: relative;
  width: 100%;
  max-width: 490px;
  max-height: 92vh;
  overflow-y: auto;
  background: var(--bg);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-3);
}

.modal__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-12);
  padding: var(--sp-20);
  background: var(--bg-dark);
  color: var(--white);
}

.modal__title {
  margin: 0;
  font-size: 19.5px;
  color: var(--white);
}

.modal__sub {
  margin: var(--sp-4) 0 0;
  font-size: var(--fs-small);
  color: var(--grey-light);
}

.modal__close {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--white);
  font-size: 20px;
  line-height: 1;
  transition: background-color var(--t-fast);
}

.modal__close:hover { background: rgba(255, 255, 255, 0.12); }

.modal__body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-16);
  padding: var(--sp-20);
}

/* Success panel, swapped in after submit. */
.modal__success {
  display: none;
  padding: var(--sp-40) var(--sp-24);
  text-align: center;
}

.modal__success.is-visible { display: block; }

.modal__success-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin: 0 auto var(--sp-16);
  border-radius: 50%;
  background: var(--green);
  color: var(--white);
  font-size: 27px;
  font-weight: var(--fw-bold);
}


/* =====================================================================
   10. PAGINATION
   Built now so Sprint 4 does not reinvent it.
   ===================================================================== */

.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-8);
  padding: 0;
  margin: 0;
  list-style: none;
}

.pagination__link {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0 var(--sp-12);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg);
  color: var(--ink-2);
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  transition: background-color var(--t-fast), border-color var(--t-fast),
              color var(--t-fast);
}

.pagination__link:hover {
  border-color: var(--blue-line);
  background: var(--blue-soft);
  color: var(--blue);
  text-decoration: none;
}

.pagination__link[aria-current="page"] {
  background: var(--blue);
  border-color: var(--blue);
  color: var(--white);
}

.pagination__ellipsis {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--sp-4);
  color: var(--grey);
}


/* =====================================================================
   11. SECTION HEADING BLOCK
   Eyebrow + h2 + optional lede + optional trailing link. Repeats in
   every band on every page.
   ===================================================================== */

.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-16);
  margin-bottom: var(--sp-32);
}

.section-head__text { max-width: 660px; }

.section-head__eyebrow { margin-bottom: var(--sp-12); }

.section-head__lede {
  margin-top: var(--sp-12);
  color: var(--grey);
  font-size: 16.5px;
}

.section--dark .section-head__lede { color: var(--grey-light); }


/* =====================================================================
   12. HORIZONTAL SCROLL ROW
   Mobile: swipeable. Desktop (>=1024px): a 4-up grid.
   Used by the category product rows and the vehicle strip.
   ===================================================================== */

.hscroll {
  position: relative;
}

.hscroll__track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(250px, 78vw);
  gap: var(--sp-16);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--container-pad);
  padding-bottom: var(--sp-8);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}

.hscroll__track > * { scroll-snap-align: start; }

@media (min-width: 640px) {
  .hscroll__track { grid-auto-columns: minmax(260px, 44vw); }
}

@media (min-width: 1024px) {
  .hscroll__track {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--sp-20);
    overflow-x: visible;
    padding-bottom: 0;
  }
}

/* Prev/next controls — desktop only, hidden when the row is a grid. */
.hscroll__controls {
  display: flex;
  gap: var(--sp-8);
}

@media (min-width: 1024px) {
  .hscroll__controls { display: none; }
}

.hscroll__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg);
  color: var(--ink-2);
  font-size: 16px;
  transition: background-color var(--t-fast), border-color var(--t-fast),
              color var(--t-fast);
}

.hscroll__btn:hover {
  border-color: var(--blue);
  background: var(--blue-soft);
  color: var(--blue);
}

.hscroll__btn[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
}


/* =====================================================================
   SPRINT 14 — GST label, quote-modal price line, short input
   ===================================================================== */

/* "+ GST extra" beside a price. Sprint 14, item 5.
   Deliberately quiet: it is a qualifier on the price, not a second
   price, so it must not compete with the figure it belongs to. Sits on
   its own line under the price on a card, inline on wider blocks. */
.price-gst {
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  color: var(--grey);
  white-space: nowrap;
}

/* The price line inside the quote modal, filled by main.js. */
.quote-price {
  margin: var(--sp-4) 0 0;
  font-weight: var(--fw-semibold);
  color: var(--ink-2);
}

/* PIN code and other fixed-width entries: a 6-digit box the width of a
   city name invites a phone number. */
.input--short {
  max-width: 11rem;
}

/* "Optional" beside a label, so the field reads as skippable at a
   glance rather than after reading the whole form. */
.help-inline {
  margin-left: var(--sp-8);
  font-size: var(--fs-small);
  font-weight: 400;
  color: var(--grey);
}
