/* ═══════════════════════════════════════════════════════════
   Almabsat — Brand Stylesheet
   ═══════════════════════════════════════════════════════════ */

:root {
  --primary-blue: #415FAA;
  --primary-cyan: #60C1D4;
  --primary-gray: #929396;
  --secondary-gold: #F9AE40;
  --secondary-bright-blue: #00ACED;
  --secondary-black: #221F1F;
  --header-height: 72px;
  --surface: #FAFBFC;
  --surface-alt: #F0F4F8;
  --border-light: #E5E8ED;
}

/* ── Reset & Base ───────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

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

body {
  font-family: 'Inter', 'IBM Plex Sans Arabic', 'Noto Sans SC', system-ui, sans-serif;
  color: var(--secondary-black);
  background: var(--surface);
  line-height: 1.6;
  overflow-x: hidden;
}

[dir="rtl"] body {
  font-family: 'IBM Plex Sans Arabic', 'Inter', system-ui, sans-serif;
}

[lang="zh"] body {
  font-family: 'Noto Sans SC', 'Inter', system-ui, sans-serif;
}

img { max-width: 100%; height: auto; display: block; }
a { text-decoration: none; color: inherit; transition: color 0.2s; }
button { cursor: pointer; border: none; background: none; font: inherit; }
ul, ol { list-style: none; }

/* ── Typography ─────────────────────────────────────────── */
h1, h2, h3, h4, h5, h6 { line-height: 1.25; font-weight: 700; }

.text-gradient {
  background: linear-gradient(135deg, var(--primary-blue) 0%, var(--primary-cyan) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

/* ── Layout ─────────────────────────────────────────────── */
.container {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 1.5rem;
}

.section { padding: 5rem 0; }
.section-alt { background: var(--surface); }

.section-header {
  text-align: center;
  max-width: 640px;
  margin: 0 auto 3rem;
}

.section-header h2 {
  font-size: 2rem;
  margin-bottom: 0.75rem;
  color: var(--secondary-black);
}

.section-header p {
  font-size: 1.1rem;
  color: var(--primary-gray);
}

/* ── Header ─────────────────────────────────────────────── */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  transition: background 0.3s, box-shadow 0.3s, backdrop-filter 0.3s;
}

.site-header.scrolled {
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(12px);
  box-shadow: 0 1px 3px rgba(0,0,0,0.06);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 72px;
  gap: 1rem;
}

.logo {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 1.35rem;
  font-weight: 800;
  color: var(--primary-blue);
  flex-shrink: 0;
}

.logo svg { width: 36px; height: 36px; }
.logo img { height: 48px; width: auto; }
/* Arabic logo carries the full brand lockup (icon + wordmark) with no companion
   text, so it is shown larger than the small English/Chinese logo for legibility. */
[dir="rtl"] .logo img { height: 88px; }
.logo { font-size: 1rem; }

.nav-links {
  display: flex;
  align-items: center;
  gap: 0.85rem;
}

.nav-links a {
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--secondary-black);
  transition: color 0.2s;
  white-space: nowrap;
}

.nav-links a:hover { color: var(--primary-blue); }

.header-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-shrink: 0;
}

/* Header WhatsApp stays a compact icon button at every size — the icon is
   universally recognized, and keeping it icon-only frees horizontal room so
   the primary "Order Another Way" CTA always reads as clear text, and the
   header lines up consistently across Arabic, English and Chinese. */
.header-actions .btn-whatsapp {
  padding: 0;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  font-size: 0;
  gap: 0;
  justify-content: center;
  flex-shrink: 0;
}
.header-actions .btn-whatsapp svg { width: 20px; height: 20px; }

/* Arabic header CTA: with the "Order Another Way" button gone from the header
   (ar keeps a single WhatsApp entry point), WhatsApp expands into a full text
   pill on desktop instead of staying icon-only. Mobile still collapses it to
   the compact circular icon button via the existing 768px media query below. */
@media (min-width: 769px) {
  .header-actions .btn-whatsapp.home-hdr-whatsapp {
    width: auto;
    height: auto;
    padding: 0.55rem 1.1rem;
    font-size: 0.85rem;
    border-radius: 10px;
    gap: 0.45rem;
  }
  .header-actions .btn-whatsapp.home-hdr-whatsapp svg { width: 18px; height: 18px; }
}

/* EN/ZH (non-RTL): tighter spacing prevents nav overflow in the 1200px container */
html:not([dir="rtl"]) .header-inner { gap: 0.75rem; }
html:not([dir="rtl"]) .nav-links { gap: 0.5rem; }
html:not([dir="rtl"]) .nav-links a { font-size: 0.8rem; }

/* Language switcher dropdown */
.lang-switcher {
  position: relative;
}

.lang-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: 8px;
  background: var(--surface);
  cursor: pointer;
  transition: background 0.2s, box-shadow 0.2s;
}

.lang-toggle svg {
  width: 20px;
  height: 20px;
  color: var(--text-primary);
  opacity: 0.7;
  transition: opacity 0.2s;
}

.lang-toggle:hover {
  background: var(--border-light);
}

.lang-toggle:hover svg {
  opacity: 1;
}

/* Account/login icon — visual-only preview (see login.html/register.html);
   same bare icon-button treatment as .lang-toggle right next to it, so it
   carries the same visual weight as the other .header-actions controls. */
.account-icon-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: 8px;
  background: var(--surface);
  color: var(--text-primary);
  transition: background 0.2s;
  flex-shrink: 0;
}
.account-icon-btn svg { width: 20px; height: 20px; opacity: 0.7; transition: opacity 0.2s; }
.account-icon-btn:hover { background: var(--border-light); }
.account-icon-btn:hover svg { opacity: 1; }

.lang-dropdown {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 180px;
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 4px 24px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.05);
  padding: 6px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px) scale(0.96);
  transform-origin: top right;
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
  z-index: 1000;
}

[dir="rtl"] .lang-dropdown {
  right: auto;
  left: 0;
  transform-origin: top left;
}

.lang-switcher.open .lang-dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0) scale(1);
}

.lang-dropdown a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-radius: 8px;
  text-decoration: none;
  color: var(--text-primary);
  font-size: 0.85rem;
  transition: background 0.15s;
}

.lang-dropdown a:hover {
  background: var(--surface);
}

.lang-dropdown a.active {
  background: var(--surface);
  font-weight: 600;
  color: var(--primary-blue);
}

.lang-dropdown a img {
  width: 24px;
  height: 16px;
  border-radius: 3px;
  box-shadow: 0 0 0 0.5px rgba(0,0,0,0.1);
  flex-shrink: 0;
}

/* Header "اطلب الآن" entry point (ar only) — the label + cart icon + badge
   grouped into ONE pill, in .header-actions. This is the site's main CTA
   now that ordering is a single merged page (ar/order.html), so it reads
   as a distinct, slightly elevated control — not just another icon
   button — while staying restrained (no color beyond the existing
   palette, no motion beyond the same hover treatment every other header
   control already uses). Visible at every breakpoint, including mobile
   (.header-actions is never hidden by the nav-links collapse — see the
   max-width:1199px rule below), which is why the "اطلب الآن" text link
   was removed from both the desktop nav and the mobile slide-out menu:
   this pill is the one entry point everywhere. */
.order-now-pill {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding-block: 0.35rem;
  padding-inline: 0.9rem 0.35rem;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--border-light);
  color: var(--secondary-black);
  font-size: 0.85rem;
  font-weight: 700;
  white-space: nowrap;
  transition: background 0.2s, border-color 0.2s, color 0.2s;
  flex-shrink: 0;
}
.order-now-pill:hover { background: #fff; border-color: var(--primary-blue); color: var(--primary-blue); box-shadow: 0 2px 8px rgba(65,95,170,0.12); }
.order-now-icon-wrap {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 999px;
  background: #fff;
  flex-shrink: 0;
}
.order-now-icon-wrap svg { width: 16px; height: 16px; opacity: 0.75; }
.order-now-pill:hover .order-now-icon-wrap svg { opacity: 1; }
.cart-badge {
  position: absolute;
  top: -4px;
  inset-inline-end: -4px;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  border-radius: 999px;
  background: var(--secondary-gold);
  color: var(--secondary-black);
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}

/* Mobile menu toggle */
.mobile-toggle {
  display: none;
  width: 40px;
  height: 40px;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  color: var(--secondary-black);
}

.mobile-toggle svg { width: 24px; height: 24px; }

/* Mobile nav — hidden by default, shown only on mobile via media query */
.mobile-nav { display: none; }

/* ── Buttons ────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.7rem 1.5rem;
  border-radius: 12px;
  font-size: 0.9rem;
  font-weight: 600;
  transition: all 0.2s;
  white-space: nowrap;
}

.btn svg { width: 18px; height: 18px; flex-shrink: 0; }

.btn-primary {
  background: var(--primary-blue);
  color: #fff;
}

.btn-primary:hover {
  background: #364f94;
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(65, 95, 170, 0.3);
}

.btn-secondary {
  background: var(--surface);
  color: var(--primary-blue);
  border: 1px solid var(--border-light);
}

.btn-secondary:hover {
  background: #fff;
  border-color: var(--primary-blue);
  transform: translateY(-1px);
}

.btn-whatsapp {
  background: #25D366;
  color: #fff;
}

.btn-whatsapp:hover {
  background: #1fb855;
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(37, 211, 102, 0.3);
}

.btn-outline {
  background: transparent;
  color: var(--primary-blue);
  border: 2px solid var(--primary-blue);
}

.btn-outline:hover {
  background: var(--primary-blue);
  color: #fff;
}

/* Quiet secondary button — minimal weight, defers focus to the primary CTA. */
.btn-ghost {
  background: transparent;
  color: var(--secondary-black);
  border: 1.5px solid rgba(34, 31, 31, 0.14);
}

.btn-ghost:hover {
  background: rgba(65, 95, 170, 0.06);
  border-color: rgba(65, 95, 170, 0.55);
  color: var(--primary-blue);
  transform: translateY(-1px);
}

.btn-sm { padding: 0.5rem 1rem; font-size: 0.85rem; border-radius: 10px; }
.btn-lg { padding: 0.85rem 2rem; font-size: 1rem; border-radius: 14px; }
.btn-icon { padding: 0.5rem; line-height: 0; }
.btn-icon svg { width: 18px; height: 18px; display: block; }
.btn-xl {
  padding: 1.05rem 2.25rem;
  font-size: 1.05rem;
  border-radius: 16px;
  gap: 0.6rem;
}
.btn-xl svg { width: 22px; height: 22px; }

/* Blog article CTAs (in-article "Browse Building Materials" + the 3-button
   end-of-article group). Sized to content on desktop; on mobile they all
   become uniform full-width blocks so they don't look ragged. */
.blog-inline-cta { width: fit-content; }
@media (max-width: 640px) {
  .blog-cta-group { flex-direction: column; align-items: stretch; }
  .blog-cta-group .btn { width: 100%; }
  .blog-inline-cta { width: 100%; }
}

/* Pulse glow on the primary WhatsApp hero CTA */
.btn-whatsapp.btn-xl {
  box-shadow: 0 8px 22px rgba(37, 211, 102, 0.32);
  position: relative;
}
.btn-whatsapp.btn-xl:hover {
  box-shadow: 0 10px 28px rgba(37, 211, 102, 0.42);
}
@keyframes whatsapp-pulse {
  0%, 100% { box-shadow: 0 8px 22px rgba(37, 211, 102, 0.32); }
  50%      { box-shadow: 0 8px 30px rgba(37, 211, 102, 0.55); }
}
@media (prefers-reduced-motion: no-preference) {
  .btn-whatsapp.btn-xl { animation: whatsapp-pulse 2.6s ease-in-out infinite; }
}

/* ── Latest Deals — scrolling card carousel ─────────────── */
/* When the bar is present, push the whole page down so the bar sits below
   the fixed header instead of behind it — keeps the header readable. */
body.has-deals-bar { padding-top: var(--header-height); }

/* Nearly-invisible + blurred so it never reads as a block over the page —
   the deal cards below have their own opaque white background, so they (and
   the arrows) stay fully readable regardless. No bottom padding — the bar
   ends right at the card row, no empty band/line beneath it. */
.announcement-bar {
  position: relative;
  /* No backdrop-filter: it was the single biggest scroll-jank source here —
     blurring whatever sits behind this element has to be recomputed on
     every scroll frame while the bar is on screen, since the page content
     behind it keeps moving. A flatter, slightly more opaque tint reads
     almost identically at a glance and costs nothing to composite. */
  background: rgba(250, 251, 252, 0.94);
  color: var(--secondary-black);
  padding: 0.55rem 0 0;
  overflow: hidden;
  isolation: isolate;
  transition: padding 0.3s ease;
}

/* Compact state — added by js/main.js once the page scrolls past a small
   threshold; removed again at the very top. Shrinks the title row away and
   each card down to a slim single-line summary (see the .announcement-slim,
   .announcement-deal-compact-price and .announcement-deal-full-details
   rules below) so the sticky strip stops competing with page content while
   still surfacing the essentials (name, price + unit, category icon). */
.announcement-bar.is-compact {
  padding: 0.3rem 0 0;
}

/* Compact title + "more details" link — a single slim line above the strip,
   not the old bulky row. Sits at the reading-start edge of the bar. */
.announcement-slim {
  display: flex;
  align-items: center;
  /* Title+link group on the reading-start side, delivery badge pushed to
     the opposite end — see .announcement-slim-start below. */
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0 1rem 0.3rem;
  max-width: 1280px;
  margin: 0 auto;
  font-size: 0.68rem;
  max-height: 2.5rem;
  opacity: 1;
  overflow: hidden;
  transition: max-height 0.3s ease, opacity 0.2s ease, padding-block 0.3s ease;
}
.announcement-bar.is-compact .announcement-slim {
  /* Whole row (title, link, delivery badge together) collapses away in
     compact mode same as before — nothing new to fit there, so no overflow
     risk in that state. */
  max-height: 0;
  opacity: 0;
  padding-block: 0;
}
.announcement-slim-start {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  /* Lets this side (not the delivery badge) give up space first if the row
     is ever too tight — title/link overflow-clip via overflow:hidden on
     the parent rather than pushing the badge out past the visible edge. */
  min-width: 0;
  overflow: hidden;
}
.announcement-delivery-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  flex-shrink: 0;
  font-weight: 700;
  white-space: nowrap;
  /* Nudges the badge in from the far edge instead of sitting flush
     against it; margin-inline-end auto-mirrors for LTR (en/zh). */
  margin-inline-end: 0.9rem;
  font-size: 0.82rem;
}
.announcement-delivery-icon {
  display: inline-flex;
  color: var(--primary-blue);
  flex-shrink: 0;
}
.announcement-delivery-icon svg { width: 19px; height: 19px; display: block; }
.announcement-delivery-text { color: #000; }
.announcement-title-compact {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: #000;
  text-transform: uppercase;
  white-space: nowrap;
}
.announcement-title-compact::before {
  content: '';
  display: inline-block;
  width: 5px;
  height: 5px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--secondary-gold);
  box-shadow: 0 0 0 0 rgba(249, 174, 64, 0.55);
  animation: deal-dot 1.8s ease-in-out infinite;
}
@keyframes deal-dot {
  0%, 100% { box-shadow: 0 0 0 0 rgba(249, 174, 64, 0.55); }
  50%      { box-shadow: 0 0 0 6px rgba(249, 174, 64, 0); }
}
.announcement-more {
  font-size: 11px;
  color: #2a7ae4;
  text-decoration: underline;
  font-weight: 600;
  white-space: nowrap;
}
.announcement-more:hover { color: #1c5fbf; }

/* Prev/next arrows — overlaid directly on the viewport's physical left/right
   edges (semi-transparent circle, not a solid box — the bar still shows
   through), pinned the same way in every language (the inner viewport is
   forced LTR — see .announcement-viewport — so "next" is always the physical
   right and "prev" always the physical left). .announcement-viewport-wrap
   (not the scrolling viewport itself) is their positioning context, so they
   stay fixed at the edges regardless of scroll position, vertically centered
   on the card strip specifically (not the slim header above it). */
.announcement-viewport-wrap { position: relative; }
.announcement-nav-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 4;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.3rem;
  height: 2.3rem;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.65);
  color: var(--primary-blue);
  cursor: pointer;
  box-shadow: 0 1px 5px rgba(20, 35, 75, 0.20);
  transition: background 0.18s, box-shadow 0.18s;
}
.announcement-nav-btn:hover,
.announcement-nav-btn:focus-visible {
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 2px 8px rgba(20, 35, 75, 0.28);
}
.announcement-nav-btn svg { width: 1.2rem; height: 1.2rem; }
.announcement-nav-btn--prev { left: 0.4rem; }
.announcement-nav-btn--next { right: 0.4rem; }

/* ── Price disclaimer: always-visible summary line + expandable details ──
   Reused on the homepage ticker strip (light surface) and inside the deals
   page hero (add .price-note--on-dark for the blue background). Built on the
   native <details>/<summary> element — toggles with no JS and no reload. */
.price-note {
  font-size: 0.66rem;
  color: var(--primary-gray);
  max-width: 680px;
  margin: 0.45rem auto 0;
  text-align: start;
  line-height: 1.5;
}
.price-note-summary {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.3rem 0.6rem;
  list-style: none;
  cursor: pointer;
  font-weight: 500;
}
/* Hide the default disclosure triangle (WebKit + the rest). */
.price-note-summary::-webkit-details-marker { display: none; }
.price-note-summary::marker { content: ''; }
.price-note-line { color: var(--primary-gray); }
.price-note-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  color: var(--primary-blue);
  font-weight: 700;
  white-space: nowrap;
}
.price-note-chevron {
  width: 0.9em;
  height: 0.9em;
  transition: transform 0.2s ease;
}
.price-note[open] .price-note-chevron { transform: rotate(180deg); }
.price-note-card {
  margin-top: 0.55rem;
  padding: 0.85rem 1rem;
  background: var(--surface-alt);
  border: 1px solid var(--border-light);
  border-radius: 10px;
}
.price-note-title {
  display: block;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--secondary-black);
  margin-bottom: 0.4rem;
}
.price-note-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.price-note-list li {
  line-height: 1.85;
  color: var(--primary-gray);
}
/* On-dark variant — sits on the deals hero's blue gradient. */
.price-note--on-dark { color: rgba(255, 255, 255, 0.6); }
.price-note--on-dark .price-note-line { color: rgba(255, 255, 255, 0.6); }
.price-note--on-dark .price-note-toggle { color: rgba(255, 255, 255, 0.9); }
.price-note--on-dark .price-note-card {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.14);
}
.price-note--on-dark .price-note-title { color: rgba(255, 255, 255, 0.85); }
.price-note--on-dark .price-note-list li { color: rgba(255, 255, 255, 0.65); }
.announcement-viewport {
  overflow: hidden;
  position: relative;
  /* Force inner layout to LTR so transforms are predictable; flip direction in RTL via animation-direction. */
  direction: ltr;
  /* Reserve a gutter matching the overlaid nav buttons' footprint (0.4rem
     inset + 2.3rem circle) on both physical edges, so no card — in any
     language, at any scroll/animation position — ever renders directly
     under an arrow or gets edge-clipped mid-card at load. */
  padding: 0 3rem;
  /* Layout/paint containment: the marquee's own reflows (the 300+ transform
     translate each frame, plus every card's compact/full collapse) never
     force the rest of the page to re-layout, and vice versa. Safe here —
     it only isolates internal layout from bleeding outside this box, it
     doesn't change how the flex children inside are measured. */
  contain: layout paint;
}
.announcement-viewport::before,
.announcement-viewport::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 48px;
  z-index: 2;
  pointer-events: none;
}
.announcement-viewport::before {
  left: 0;
  background: linear-gradient(90deg, rgba(250, 251, 252, 0.94), rgba(250, 251, 252, 0));
}
.announcement-viewport::after {
  right: 0;
  background: linear-gradient(-90deg, rgba(250, 251, 252, 0.94), rgba(250, 251, 252, 0));
}

.announcement-track {
  display: inline-flex;
  align-items: stretch;
  /* No flex gap — using margin-inline-end on each card so the trailing space
     after the last card matches the inter-card space. This is what makes the
     loop math perfect (translate by 1/N of total width lands the next copy
     exactly where the previous one started). */
  padding: 0.2rem 0;
  width: max-content;
  animation: announcement-marquee 112s linear infinite;
  will-change: transform;
}
[dir="rtl"] .announcement-track { animation-direction: reverse; }
.announcement-viewport:hover .announcement-track,
.announcement-viewport:focus-within .announcement-track {
  animation-play-state: paused;
}
@keyframes announcement-marquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-25%, 0, 0); }
}

.announcement-deal {
  position: relative;
  flex: 0 0 auto;
  /* Wide enough that the longest real content (e.g. a 4-digit truck price
     range + its unit, "864.70 – 912.17 / تريلا") always fits on its own
     line at full size — verified against every current deal in
     announcements.js. Never shrink this to force-fit content; widen it
     instead if a future deal needs more room. */
  width: 300px;
  /* Margin (not flex gap) so the spacing AFTER the last card matches the
     spacing between cards — required for the seamless marquee loop. */
  margin-inline-end: 1.2rem;
  background: #FFFFFF;
  color: var(--secondary-black);
  border: 1px solid var(--border-light);
  border-radius: 10px;
  padding: 0.65rem 1.1rem 0.7rem;
  display: flex;
  flex-direction: column;
  text-decoration: none;
  box-shadow: 0 1px 3px rgba(20, 35, 75, 0.06), 0 2px 8px rgba(20, 35, 75, 0.05);
  transition: transform 0.2s, box-shadow 0.2s, padding 0.35s ease;
  direction: inherit;
  /* No overflow:hidden here — nothing inside is ever clipped. Cards in the
     same row auto-equalize height via .announcement-track's
     align-items:stretch, so uniform size comes from that + the generous
     fixed width above, never from cutting content. */
  /* Layout containment (layout only, NOT paint — paint containment would
     clip this card's own box-shadow, which is meant to extend past its
     edges, especially the larger hover shadow below): each card's own
     max-height/min-height transitions (compact ↔ full) resolve entirely
     inside its own box — the browser doesn't need to re-check every other
     card or the track just because one card's internal content is
     mid-transition. Doesn't affect align-items:stretch above (that only
     reads this box's own computed size, which containment doesn't hide). */
  contain: layout;
}
[dir="rtl"] .announcement-deal { direction: rtl; }
.announcement-deal:hover {
  box-shadow: 0 4px 12px rgba(20, 35, 75, 0.12);
  transform: translateY(-1px);
}
/* Compact mode: keep the card at its full-mode WIDTH (300px) — do not
   narrow it. The name needs that width to stay on one line without
   wrapping; narrowing the card is what caused names to wrap onto a 2nd
   line and, combined with .announcement-track's align-items:stretch,
   made every card in the row inherit that taller height. Only the padding
   tightens; the height itself comes down from collapsing
   .announcement-deal-full-details and .announcement-deal-compact-price
   below, plus letting the name sit on its true single line — there is no
   hidden reserved space left over. */
.announcement-bar.is-compact .announcement-deal {
  padding: 0.45rem 0.85rem;
}

.announcement-deal-head {
  display: flex;
  align-items: flex-start;
  gap: 0.3rem;
  min-width: 0;
}
.announcement-deal-icon {
  font-size: 0.85rem;
  line-height: 1.25;
  flex-shrink: 0;
  opacity: 0.85;
}
.announcement-deal-name {
  font-size: 0.78rem;
  font-weight: 700;
  color: #000;
  line-height: 1.25;
  margin: 0;
  /* Reserve space for 2 lines so cards with a short name and a long one
     still line up — but this is a minimum, not a cap: names never
     truncate, a rare 3rd line just grows the card (and its whole row,
     via align-items:stretch) instead of losing text. */
  min-height: calc(1.25em * 2);
  letter-spacing: -0.005em;
  min-width: 0;
  overflow-wrap: break-word;
  /* Deliberately not transitioned: it's a layout property, and this
     specific height delta (2-line reservation → single nowrap line) is
     the least visually prominent of the three things collapsing at once
     here — snapping it instantly removes one of three simultaneous
     per-card layout transitions without a visible mismatch, since the
     price/details collapse below is the one the eye actually follows. */
}
/* Compact mode: no 2nd line to line up against, so drop the reservation —
   AND force the name onto a single line (no wrap, ellipsis as a safety net
   only for a name longer than the card was sized for — none of the current
   deals are, at this card width). This is what actually fixes the "tall
   card with empty space" bug: a wrapped name was growing the head row,
   and align-items:stretch on .announcement-track was then stretching
   EVERY other card in the strip to match that one tall card. */
.announcement-bar.is-compact .announcement-deal-name {
  min-height: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Delivery ribbon (approved, all 3 languages) — standard corner-ribbon
   recipe (the "GitHub corner" pattern): a wrapper clipped to the card's
   top-left corner (Arabic/RTL default; mirrored to top-right for en/zh
   below) with overflow:hidden, and a wide -45deg ribbon inside it whose
   ends get cut flush by the wrapper's own straight edges. .announcement-deal
   has position:relative so the wrapper anchors to the CARD's box, not the
   head row, and overlays without pushing or resizing any existing content
   — the head row, price rows and CTA keep their exact pre-existing layout.
   border-top-left-radius:inherit keeps the wrapper's own corner following
   the card's rounded corner.
   No `transition` here on purpose: an earlier version animated width/
   height/top/left/font-size on is-compact toggle, but those are all
   layout-triggering properties, and with 4 marquee copies of every deal
   in the DOM (see trackInner in main.js) that meant dozens of elements
   forcing reflow at the exact same instant — the actual cause of the
   reported scroll lag. The ribbon now snaps instantly between states,
   same as the class toggle itself; the card's own pre-existing
   padding/box-shadow/transform transitions (untouched, already approved)
   still carry the visible smoothness of the mode switch. */
.announcement-deal-ribbon-wrap {
  position: absolute;
  top: 0;
  left: 0;
  width: 90px;
  height: 90px;
  overflow: hidden;
  pointer-events: none;
  border-top-left-radius: inherit;
  z-index: 2;
}
.announcement-deal-tag {
  position: absolute;
  display: block;
  width: 130px;
  top: 22px;
  left: -32px;
  text-align: center;
  line-height: 1;
  transform: rotate(-45deg);
  background: #FDE68A;
  color: #000;
  font-weight: 800;
  font-size: 0.56rem;
  padding: 4px 0;
  white-space: nowrap;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
/* Compact mode: the compact card is only ~46px tall (tight padding + one
   slim price line) — far shorter than the 90x90 wrap above needs, which is
   exactly what was sticking out below the card. A 45deg ribbon needs
   height roughly equal to its own width, so at this card height there's no
   width/font-size combination at 45deg that keeps "شامل التوصيل" both
   fully legible AND inside the card — the geometry doesn't allow it
   (verified: even shrinking to the point of overflow-safety made the text
   too cramped to fit without spilling past the ribbon into the wrap's
   clip, which would just cut it off mid-word). Using a shallower tilt
   (-22deg, back in the -20/-30 range from earlier in this project) needs
   far less vertical room for the same ribbon width, so the full wording
   fits with comfortable margin on all sides — confirmed to stay within
   the compact card's real height. */
.announcement-bar.is-compact .announcement-deal-ribbon-wrap {
  width: 64px;
  height: 38px;
}
.announcement-bar.is-compact .announcement-deal-tag {
  width: 60px;
  top: 13.5px;
  left: 2px;
  transform: rotate(-22deg);
  font-size: 0.5rem;
  padding: 1.5px 0;
}
/* English/Chinese (LTR) — mirror image of the Arabic ribbon: top-right
   corner instead of top-left, angles flipped to the positive equivalent.
   Every size/offset magnitude is identical to the Arabic rules above (only
   left/right and the rotation sign flip), so the two mirror exactly. */
html[lang="en"] .announcement-deal-ribbon-wrap,
html[lang="zh"] .announcement-deal-ribbon-wrap {
  left: auto;
  right: 0;
  border-top-left-radius: 0;
  border-top-right-radius: inherit;
}
html[lang="en"] .announcement-deal-tag,
html[lang="zh"] .announcement-deal-tag {
  left: auto;
  right: -32px;
  transform: rotate(45deg);
}
html[lang="en"] .announcement-bar.is-compact .announcement-deal-tag,
html[lang="zh"] .announcement-bar.is-compact .announcement-deal-tag {
  left: auto;
  right: 2px;
  transform: rotate(22deg);
}
/* English needs its own compact sizing: "Delivery included" (18 characters)
   is much longer than Arabic's "شامل التوصيل" or Chinese's "包含配送" (4
   characters) — reusing the exact same 60px/0.5rem values sized for the
   shorter text would overflow the ribbon and get clipped mid-word by the
   wrap. Same ±22deg angle and wrap height (38px, matching the compact
   card's real height budget) — only ribbon/wrap width and font-size grow
   to fit the longer word, verified to stay within the wrap on both axes. */
html[lang="en"] .announcement-bar.is-compact .announcement-deal-ribbon-wrap {
  width: 76px;
}
html[lang="en"] .announcement-bar.is-compact .announcement-deal-tag {
  width: 70px;
  top: 14.3px;
  right: 3px;
  font-size: 0.4rem;
}

/* Compact-only price line: icon+name have their own row above (full card
   width available to the name — see the nowrap rule above); this is a
   SEPARATE slim row below, not crammed onto the name's row, so it never
   competes with the name for width. Collapsed (zero height) in the full
   card; the bar's is-compact state expands it. Kept as a permanent
   (always-rendered) element rather than injected via JS on scroll, so the
   only thing that changes between states is CSS. */
.announcement-deal-compact-price {
  display: flex;
  align-items: baseline;
  gap: 0.2em;
  font-size: 0.72rem;
  font-weight: 800;
  color: #0d6e3a;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-variant-numeric: tabular-nums;
  max-height: 0;
  opacity: 0;
  margin-top: 0;
  transition: max-height 0.3s ease, opacity 0.2s ease, margin-top 0.3s ease;
}
.announcement-bar.is-compact .announcement-deal-compact-price {
  max-height: 1.2rem;
  opacity: 1;
  margin-top: 0.2rem;
}

/* Wraps everything that only belongs to the FULL card (location, both price
   rows + their VAT captions, the WhatsApp CTA). Collapsing this one element
   is what shrinks the card down to its compact height — max-height (not
   height:auto→0, which can't transition) gives a smooth close/open, capped
   generously above any real card's natural height so nothing is ever cut off
   while open. */
.announcement-deal-full-details {
  display: flex;
  flex-direction: column;
  gap: 0.22rem;
  margin-top: 0.3rem;
  max-height: 15rem;
  opacity: 1;
  overflow: hidden;
  transition: max-height 0.35s ease, opacity 0.25s ease, margin-top 0.35s ease;
}
.announcement-bar.is-compact .announcement-deal-full-details {
  max-height: 0;
  opacity: 0;
  margin-top: 0;
  pointer-events: none;
}

.announcement-deal-location {
  font-size: 0.58rem;
  color: var(--primary-gray);
  display: inline-flex;
  align-items: flex-start;
  gap: 0.18rem;
  line-height: 1.45;
  min-height: 0.84rem;
  min-width: 0;
}
.announcement-deal-location-pin {
  width: 0.72rem;
  height: 0.72rem;
  flex-shrink: 0;
  margin-top: 0.1rem;
  color: var(--primary-blue);
}
.announcement-deal-location-name {
  min-width: 0;
  overflow-wrap: break-word;
}

.announcement-deal-prices {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 0.3rem 0 0.2rem;
  border-top: 1px solid rgba(65, 95, 170, 0.10);
  /* Minimum block height (not a cap) so the CTA below lines up across cards
     whose price/unit text happens to be short — cards that need more
     (a wrapped unit, a longer VAT phrase) simply grow past this. */
  min-height: 2.7rem;
}

/* Hero price — EXCL tax (the row WITHOUT .is-incl). Big bold value with
   caption below. Rendered first in DOM so it appears on top. */
.announcement-deal-price-row:not(.is-incl) {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  min-width: 0;
}
.announcement-deal-price-row:not(.is-incl) .announcement-deal-price-value {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 0.3em;
  row-gap: 0.05em;
  color: #0d6e3a;
  font-size: 0.92rem;
  font-weight: 800;
  letter-spacing: -0.015em;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  order: 1;
  text-align: start;
  /* The price range + currency mark + unit sit on one line at this card
     width for every deal in the data. flex-wrap is a safety net, not the
     plan: if a future deal's unit is ever too long to fit, it wraps to its
     own line below the number instead of being cut off or masked. */
}
.announcement-deal-price-row:not(.is-incl) .announcement-deal-price-label {
  font-size: 0.47rem;
  color: var(--primary-gray);
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  order: 2;
  margin-top: 0.08rem;
  text-align: start;
  /* Full VAT phrase ("غير شامل ضريبة القيمة المضافة") always renders in
     full — no nowrap/ellipsis; wraps to a 2nd line in the rare case a
     device font renders it wider than the card. */
}

/* Secondary price — INCL VAT (.is-incl). Small supporting line:
   "<range> شامل ضريبة القيمة المضافة" — value first, then label. */
.announcement-deal-price-row.is-incl {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2rem;
  margin-top: 0.22rem;
}
.announcement-deal-price-row.is-incl .announcement-deal-price-label {
  font-size: 0.46rem;
  color: var(--primary-gray);
  font-weight: 500;
  letter-spacing: 0.005em;
}
.announcement-deal-price-row.is-incl .announcement-deal-price-value {
  font-size: 0.53rem;
  color: var(--primary-gray);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

/* SAR icon — inline SVG inheriting color and sized to the surrounding em. */
.riyal-icon {
  width: 1em;
  height: 1em;
  fill: currentColor;
  display: inline-block;
  vertical-align: -0.12em;
  flex-shrink: 0;
}

/* Placeholder reference price — product cards (.pm-card-body) and product
   detail pages (above .bm-main-cta). See price-placeholder.js: TEMPORARY
   PLACEHOLDER DATA, not a real price feed. Reuses the Latest Deals page's
   established green price color and .riyal-icon sizing convention for a
   single approximate value (that page's own dual excl/incl-VAT row layout
   doesn't apply here — this is one number, not a range). */
.ref-price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.3rem;
  font-size: 0.78rem;
  font-weight: 700;
  color: #0d6e3a;
  font-variant-numeric: tabular-nums;
  margin-bottom: 0.5rem;
}
.ref-price .riyal-icon { width: 0.85em; height: 0.85em; }
.ref-price-unit { font-weight: 600; color: var(--primary-gray); font-size: 0.7rem; }
/* Excl-VAT price stays primary (above); this is the incl-VAT figure shown
   right after it in parentheses, abbreviated (see price-placeholder.js's
   getVatShortLabel) — same treatment wherever a reference price shows:
   catalog cards, product-detail CTAs, the picker modal, and cart rows. */
.ref-price-incl { font-weight: 500; color: var(--primary-gray); font-size: 0.7rem; white-space: nowrap; }
.ref-price-approx {
  font-size: 0.62rem;
  font-weight: 600;
  color: var(--primary-gray);
  background: var(--surface);
  border: 1px solid var(--border-light);
  border-radius: 999px;
  padding: 0.05rem 0.45rem;
}
/* "Pricing after order" comparison mode (see price-placeholder.js) — a
   handful of example products show this label instead of a number. Blue
   pill instead of the green price + muted approx badge, so it reads as a
   distinct "not priced yet" state at a glance, not just a smaller number. */
.ref-price.ref-price-quote { color: var(--primary-blue); font-size: 0.8rem; }
.ref-price.ref-price-quote .ref-price-approx {
  background: rgba(65,95,170,0.08);
  color: var(--primary-blue);
  border-color: rgba(65,95,170,0.15);
  font-size: 0.72rem;
  padding: 0.15rem 0.6rem;
}

/* Currency span — muted, normal weight, absolute size so it
   matches the unit label regardless of parent font-size inheritance.
   Spacing comes from the parent's column-gap, not margin — the parent
   (.announcement-deal-price-value) is a flex row of [value][currency][unit]. */
.announcement-deal-price-currency {
  font-size: 0.65rem;
  font-weight: 400;
  color: #6b7280;
}

/* Unit suffix (e.g. "/ كيس") — same absolute size as currency so both render identically. */
.announcement-deal-price-unit {
  font-size: 0.65rem;
  font-weight: 400;
  color: #6b7280;
}

.announcement-deal-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.22rem;
  background: #25D366;
  color: #fff;
  font-weight: 700;
  font-size: 0.6rem;
  padding: 0.3rem 0.45rem;
  border-radius: 6px;
  text-decoration: none;
  transition: background 0.2s, box-shadow 0.2s;
  box-shadow: 0 1px 4px rgba(37, 211, 102, 0.35);
  /* Push to bottom (margin-top:auto) AND to the inline-end of the card
     (align-self:flex-end) — small square pill anchored at the corner. */
  margin-top: auto;
  align-self: flex-end;
}
.announcement-deal-cta:hover { background: #1fb855; box-shadow: 0 2px 7px rgba(37, 211, 102, 0.45); }
.announcement-deal-cta svg { width: 11px; height: 11px; fill: currentColor; }

/* Fallback for any .announcement-disclaimer rendered outside .announcement-footer. */
.announcement-disclaimer {
  text-align: center;
  font-size: 0.64rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--primary-gray);
  line-height: 1.4;
}

@media (prefers-reduced-motion: reduce) {
  .announcement-track { animation: none; }
}

/* ── Hero ───────────────────────────────────────────────── */
/* When the deals bar is present, the hero is no longer the page opener,
   so we ease its full-viewport height to keep the spacing harmonious. */
body.has-deals-bar .hero {
  min-height: auto;
  padding: 3.5rem 0 4rem;
}

.hero {
  position: relative;
  min-height: 92vh;
  display: flex;
  align-items: center;
  overflow: hidden;
  background: var(--surface);
}

.hero::before, .hero::after { display: none; }

.hero-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4rem;
  align-items: center;
  position: relative;
  z-index: 1;
}

.hero-content { max-width: 560px; }

.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 1rem;
  background: rgba(65, 95, 170, 0.08);
  border: 1px solid rgba(65, 95, 170, 0.15);
  border-radius: 100px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--primary-blue);
  margin-bottom: 1.5rem;
}

.hero-badge svg { width: 16px; height: 16px; }

.hero h1 {
  font-size: 3.25rem;
  line-height: 1.15;
  margin-bottom: 1.25rem;
  letter-spacing: -0.02em;
}

.hero p {
  font-size: 1.15rem;
  color: var(--primary-gray);
  margin-bottom: 2rem;
  line-height: 1.7;
}

.hero-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-bottom: 2rem;
}

.hero-secondary-ctas {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  font-size: 0.9rem;
  color: var(--primary-gray);
}

.hero-secondary-ctas a {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 500;
  color: var(--primary-blue);
  transition: color 0.2s;
}

.hero-secondary-ctas a:hover { color: var(--primary-cyan); }
.hero-secondary-ctas a svg { width: 16px; height: 16px; }

/* Hero visual */
.hero-visual {
  position: relative;
  display: flex;
  justify-content: center;
}

.hero-visual-card {
  position: relative;
  width: 100%;
  max-width: 480px;
  background: #fff;
  border-radius: 24px;
  box-shadow: 0 20px 60px rgba(65, 95, 170, 0.1), 0 1px 3px rgba(0,0,0,0.04);
  padding: 2rem;
  overflow: hidden;
}

.hero-visual-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 4px;
  background: linear-gradient(90deg, var(--primary-blue), var(--primary-cyan));
}

.visual-stat-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.25rem;
}

.visual-stat {
  padding: 1.25rem;
  background: var(--surface);
  border-radius: 16px;
  text-align: center;
}

.visual-stat-icon {
  width: 44px;
  height: 44px;
  margin: 0 auto 0.75rem;
  background: rgba(65, 95, 170, 0.08);
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--primary-blue);
}

.visual-stat-icon svg { width: 22px; height: 22px; }

.visual-stat h4 { font-size: 1.5rem; color: var(--primary-blue); margin-bottom: 0.25rem; }
.visual-stat p { font-size: 0.8rem; color: var(--primary-gray); }

/* ── Quick Actions ──────────────────────────────────────── */
.quick-actions-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem;
}

.action-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 1.75rem 1rem;
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: 16px;
  transition: all 0.25s;
  cursor: pointer;
}

.action-card:hover {
  border-color: var(--primary-blue);
  box-shadow: 0 8px 24px rgba(65, 95, 170, 0.1);
  transform: translateY(-4px);
}

.action-icon {
  width: 52px;
  height: 52px;
  border-radius: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 1rem;
  color: #fff;
}

.action-icon svg { width: 24px; height: 24px; }

.action-icon-blue { background: var(--primary-blue); }
.action-icon-cyan { background: var(--primary-cyan); }
.action-icon-gold { background: var(--secondary-gold); }
.action-icon-orange { background: #EF7A3D; }
.action-icon-bright { background: var(--secondary-bright-blue); }

.action-card h3 { font-size: 0.9rem; font-weight: 600; margin-bottom: 0.25rem; }
.action-card p { font-size: 0.78rem; color: var(--primary-gray); line-height: 1.5; }

/* ── Materials Preview ──────────────────────────────────── */
.materials-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
}

.material-card {
  position: relative;
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: 20px;
  overflow: hidden;
  transition: all 0.25s;
  cursor: pointer;
  /* Pin the "View All" button to the same height across every card in a grid
     row regardless of description length — same mechanism as .pm-card on
     the products catalog page. */
  display: flex;
  flex-direction: column;
}

/* Stronger hover affordance than the plain shadow+lift other card types
   use — these cards have no visible buttons of their own (see .pm-card-link
   below), so the hover state is the only cue the whole card is clickable. */
.material-card:hover {
  box-shadow: 0 18px 40px rgba(0,0,0,0.14);
  transform: translateY(-6px) scale(1.015);
  border-color: var(--primary-blue);
}

.material-card-visual {
  height: 230px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
}

.material-card-visual.cement,
.material-card-visual.sand,
.material-card-visual.blocks { background: #fff; }

.material-card-visual svg {
  width: 64px;
  height: 64px;
  opacity: 0.6;
}

.material-card-visual img,
.material-detail-visual img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.material-card-body { padding: 1.5rem; display: flex; flex-direction: column; flex: 1; }
.material-card-body h3 { font-size: 1.15rem; margin-bottom: 0.5rem; }
.material-card-body p { font-size: 0.9rem; color: var(--primary-gray); margin-bottom: 0.75rem; line-height: 1.6; }
/* The description (the only <p>) is the variable-length element — it grows
   to absorb the leftover height, pushing the "View All" button below it
   down to the same position on every card. */
.material-card-body p:first-of-type { flex: 1; }
/* The card's own name link (h3 > .pm-card-link) stretches over the whole
   card — the explicit "View All" button is a real distinct destination, so
   it needs its own stacking context to stay independently clickable above it. */
.material-card-body > .btn { position: relative; z-index: 2; }

.material-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 1.25rem;
}

.material-tag {
  padding: 0.25rem 0.65rem;
  background: var(--surface);
  border-radius: 100px;
  font-size: 0.78rem;
  color: var(--primary-gray);
  font-weight: 500;
}

/* ── How It Works ───────────────────────────────────────── */
.steps-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 2rem;
  position: relative;
}

.steps-grid::before {
  content: '';
  position: absolute;
  top: 36px;
  left: 12.5%;
  right: 12.5%;
  height: 2px;
  background: linear-gradient(90deg, var(--primary-blue), var(--primary-cyan));
  opacity: 0.2;
}

.step-card { text-align: center; position: relative; z-index: 1; }

.step-number {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 1.25rem;
  font-size: 1.5rem;
  font-weight: 800;
  color: #fff;
  background: linear-gradient(135deg, var(--primary-blue), var(--primary-cyan));
  box-shadow: 0 6px 20px rgba(65, 95, 170, 0.25);
}

.step-card h3 { font-size: 1rem; margin-bottom: 0.5rem; }
.step-card p { font-size: 0.88rem; color: var(--primary-gray); line-height: 1.6; }

/* ── Trust Section ──────────────────────────────────────── */
.trust-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.5rem;
}

.trust-card {
  text-align: center;
  padding: 2rem 1.25rem;
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: 16px;
}

.cta-riyal-icon {
  width: 3rem;
  margin: 0 auto 1.25rem;
}
.cta-riyal-icon svg { width: 100%; display: block; }

.trust-icon {
  width: 48px;
  height: 48px;
  margin: 0 auto 1rem;
  color: var(--primary-blue);
}

.trust-icon svg { width: 100%; height: 100%; overflow: visible; }

.trust-card h3 { font-size: 0.95rem; margin-bottom: 0.35rem; }
.trust-card p { font-size: 0.82rem; color: var(--primary-gray); line-height: 1.6; }

/* ── Final CTA ──────────────────────────────────────────── */
.cta-section {
  background: linear-gradient(135deg, var(--primary-blue) 0%, #364f94 100%);
  padding: 4.5rem 0;
  text-align: center;
  color: #fff;
  position: relative;
  overflow: hidden;
}

.cta-section::before {
  content: '';
  position: absolute;
  top: -50%;
  right: -20%;
  width: 60%;
  height: 200%;
  background: radial-gradient(ellipse, rgba(96, 193, 212, 0.15) 0%, transparent 70%);
  pointer-events: none;
}

.cta-section h2 { font-size: 2rem; margin-bottom: 0.75rem; }
.cta-section p { font-size: 1.1rem; opacity: 0.85; margin-bottom: 2rem; max-width: 500px; margin-left: auto; margin-right: auto; }

.cta-buttons {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.cta-section .btn-primary {
  background: #fff;
  color: var(--primary-blue);
}

.cta-section .btn-primary:hover {
  background: #f0f4ff;
  box-shadow: 0 4px 12px rgba(255,255,255,0.2);
}

/* ── Footer ─────────────────────────────────────────────── */
.site-footer {
  background: var(--secondary-black);
  color: #fff;
  padding: 3.5rem 0 0;
}

.footer-grid {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1fr 1fr;
  gap: 2.5rem;
  padding-bottom: 2.5rem;
}

.footer-brand p { font-size: 0.9rem; opacity: 0.65; margin-top: 0.75rem; line-height: 1.6; }

.footer-col h4 {
  font-size: 0.85rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  opacity: 0.5;
  margin-bottom: 1rem;
}

.footer-col a {
  display: block;
  font-size: 0.9rem;
  padding: 0.3rem 0;
  opacity: 0.75;
  transition: opacity 0.2s;
}

.footer-col a:hover { opacity: 1; }

.footer-bottom {
  border-top: 1px solid rgba(255,255,255,0.08);
  padding: 1.5rem 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 0.82rem;
  opacity: 0.5;
}

.footer-legal { display: flex; gap: 0.5rem; }

/* ── Floating Contact Widget ─────────────────────────────── */
/* .contact-options used to be position:absolute, floating above the toggle
   with a gap between them — that gap fell outside .contact-widget's own
   hoverable box, so moving the mouse from the toggle up to the options
   exited :hover partway there and the menu vanished before you could click.
   Fixed by keeping .contact-options in normal flex flow (laid out above the
   toggle, always reserving its space even while invisible) so the toggle +
   gap + options form one continuous box with no dead zone — :hover now
   holds for the whole path. pointer-events:none while closed keeps that
   reserved space from blocking clicks on whatever's underneath the corner. */
.contact-widget {
  position: fixed;
  bottom: 1.5rem;
  right: 1.5rem;
  z-index: 90;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

[dir="rtl"] .contact-widget { right: auto; left: 1.5rem; }

.contact-toggle {
  width: 56px;
  height: 56px;
  border: none;
  border-radius: 50%;
  background: var(--primary-blue);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  cursor: pointer;
  box-shadow: 0 4px 16px rgba(65, 95, 170, 0.35);
  transition: transform 0.2s, box-shadow 0.2s;
  flex-shrink: 0;
}

.contact-widget:hover .contact-toggle,
.contact-widget.open .contact-toggle {
  transform: scale(1.08);
  box-shadow: 0 6px 24px rgba(65, 95, 170, 0.45);
}

.contact-toggle svg { width: 26px; height: 26px; fill: #fff; }

.contact-options {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(12px);
  transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s;
  pointer-events: none;
}

.contact-widget:hover .contact-options,
.contact-widget.open .contact-options,
.contact-widget:focus-within .contact-options {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  pointer-events: auto;
}

.contact-option {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  transition: transform 0.2s, box-shadow 0.2s;
}

.contact-option:hover { transform: scale(1.1); }

.contact-option svg { width: 22px; height: 22px; fill: #fff; }

.contact-option-whatsapp { background: #25D366; }
.contact-option-whatsapp:hover { box-shadow: 0 6px 20px rgba(37, 211, 102, 0.4); }

.contact-option-phone { background: var(--primary-cyan); }
.contact-option-phone:hover { box-shadow: 0 6px 20px rgba(96, 193, 212, 0.4); }

/* ── Page Hero (inner pages) ────────────────────────────── */
.page-hero {
  padding: 8rem 0 3rem;
  background: var(--surface);
  text-align: center;
}

.page-hero h1 { font-size: 2.5rem; margin-bottom: 0.75rem; }
.page-hero p { font-size: 1.1rem; color: var(--primary-gray); max-width: 560px; margin: 0 auto; }

/* ── Legal content (terms, privacy, content policy) ────── */
.legal-article h2 { font-size: 1.4rem; margin: 2.5rem 0 1rem; }
.legal-article h2:first-of-type { margin-top: 0; }
.legal-article h3 { font-size: 1.1rem; margin: 1.5rem 0 0.75rem; }
.legal-article p { margin-bottom: 1.1rem; line-height: 1.8; }
.legal-article ul { margin: 0 0 1.25rem; line-height: 1.8; }
.legal-article li { margin-bottom: 0.75rem; }
.legal-article li:last-child { margin-bottom: 0; }

/* ── Forms ──────────────────────────────────────────────── */
.form-group { margin-bottom: 1.25rem; }

.form-label {
  display: block;
  font-size: 0.875rem;
  font-weight: 600;
  margin-bottom: 0.4rem;
  color: var(--secondary-black);
}

.form-input,
.form-select,
.form-textarea {
  width: 100%;
  padding: 0.7rem 1rem;
  border: 1px solid var(--border-light);
  border-radius: 12px;
  font-size: 0.9rem;
  font-family: inherit;
  transition: border-color 0.2s, box-shadow 0.2s;
  background: #fff;
  color: var(--secondary-black);
}

.form-input:focus,
.form-select:focus,
.form-textarea:focus {
  outline: none;
  border-color: var(--primary-blue);
  box-shadow: 0 0 0 3px rgba(65, 95, 170, 0.1);
}

.form-textarea { resize: vertical; min-height: 100px; }

/* Show/hide password — wrapper + eye button injected by js/main.js around
   any input[type=password][data-pw-toggle]. Sits at the inline-end edge
   (left in RTL), so the input reserves padding on that side. */
.pw-field { position: relative; }
.pw-field .form-input { padding-inline-end: 2.75rem; }
.pw-toggle {
  position: absolute;
  top: 50%;
  inset-inline-end: 0.45rem;
  transform: translateY(-50%);
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  color: var(--primary-gray);
  background: transparent;
  cursor: pointer;
}
.pw-toggle:hover { background: var(--surface); color: var(--secondary-black); }
.pw-toggle:focus-visible { outline: 2px solid var(--primary-blue); outline-offset: 1px; }
.pw-toggle svg { width: 18px; height: 18px; }

.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}

.file-upload {
  border: 2px dashed var(--border-light);
  border-radius: 16px;
  padding: 2.5rem;
  text-align: center;
  transition: border-color 0.2s, background 0.2s;
  cursor: pointer;
}

.file-upload:hover {
  border-color: var(--primary-blue);
  background: rgba(65, 95, 170, 0.02);
}

.file-upload-icon {
  width: 48px;
  height: 48px;
  margin: 0 auto 1rem;
  color: var(--primary-blue);
  opacity: 0.6;
}

.file-upload-icon svg { width: 100%; height: 100%; }
.file-upload h4 { font-size: 0.95rem; margin-bottom: 0.25rem; }
.file-upload p { font-size: 0.82rem; color: var(--primary-gray); }

/* File list (multi-file preview) */
.file-list { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.5rem; }
.file-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 0.75rem;
  background: var(--surface);
  border: 1px solid var(--border-light);
  border-radius: 10px;
  font-size: 0.85rem;
}
.file-item-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-item-remove {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  color: #dc2626;
  transition: background 0.2s;
}
.file-item-remove:hover { background: #fef2f2; }

/* ── Contact Grid ───────────────────────────────────────── */
.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3rem;
}
/* Grid items stretch to the row's height (the taller Contact Form column) by
   default, so making this column a flex stack and pushing Company Info down
   with margin-top: auto lines its bottom up with the form card's bottom. */
.contact-methods-col {
  display: flex;
  flex-direction: column;
}
.contact-methods-col .contact-company-info {
  margin-top: auto;
  padding-top: 3rem;
}

.contact-methods {
  display: grid;
  gap: 1rem;
}

.contact-method {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.25rem;
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: 14px;
  transition: all 0.2s;
}

.contact-method:hover {
  border-color: var(--primary-blue);
  box-shadow: 0 4px 12px rgba(0,0,0,0.04);
}

.contact-method-icon {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: #fff;
}

.contact-method h4 { font-size: 0.9rem; margin-bottom: 0.15rem; }
.contact-method p { font-size: 0.82rem; color: var(--primary-gray); }

/* ── Company info box (Contact page) ────────────────────────
   Reference-only data (registration/tax numbers, address) — one
   grouped container with divided rows, using the same colored-icon
   language as the .contact-method cards above it. */
.company-info-box {
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: 14px;
  overflow: hidden;
}
.company-info-row {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--border-light);
}
.company-info-row:last-child { border-bottom: none; }
/* Commercial Registration + Tax Number share one row, split evenly, with a
   vertical divider between them; Address stays full-width below. */
.company-info-split {
  display: flex;
  border-bottom: 1px solid var(--border-light);
}
.company-info-split .company-info-row {
  flex: 1;
  min-width: 0;
  border-bottom: none;
}
.company-info-split .company-info-row:first-child {
  border-inline-end: 1px solid var(--border-light);
}
@media (max-width: 480px) {
  .company-info-split { flex-direction: column; }
  .company-info-split .company-info-row:first-child {
    border-inline-end: none;
    border-bottom: 1px solid var(--border-light);
  }
}
.company-info-icon-wrap {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: #fff;
}
.company-info-icon { width: 18px; height: 18px; }
.company-info-vat-badge { font-size: 0.6rem; font-weight: 800; letter-spacing: 0.02em; }
.company-info-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.15rem; }
.company-info-label { font-size: 0.8rem; color: var(--primary-gray); }
.company-info-value { font-size: 0.95rem; font-weight: 600; color: var(--secondary-black); }
.company-info-copy {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  color: var(--primary-gray);
  transition: background 0.15s, color 0.15s;
}
.company-info-copy:hover { background: var(--surface); color: var(--primary-blue); }
.company-info-copy.is-copied { color: #4CAF50; }
.company-info-copy svg { width: 16px; height: 16px; }

/* ── Card (generic) ─────────────────────────────────────── */
.card {
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: 20px;
  padding: 2rem;
}

/* ── Compact form card (order / upload-boq / become-supplier /
   contact) ─────────────────────────────────────────────────
   Tighter version of .card for form-heavy pages: smaller
   padding, denser field spacing, and a thin blue accent bar
   along the bottom edge. Layout only — no effect on field
   names, validation, or JS behavior. */
.card-compact { position: relative; padding: 1.5rem; padding-bottom: calc(1.5rem + 14px); }
.card-compact .form-group { margin-bottom: 0.85rem; }
.card-compact .form-label { margin-bottom: 0.3rem; font-size: 0.83rem; }
.card-compact .form-row { gap: 0.75rem; }
.card-compact .form-input,
.card-compact .form-select,
.card-compact .form-textarea { padding: 0.55rem 0.85rem; font-size: 0.87rem; }
.card-compact .form-textarea { min-height: 80px; }
.card-compact::after {
  content: "";
  position: absolute;
  bottom: 14px;
  inset-inline: 1.75rem;
  height: 3px;
  border-radius: 3px;
  background: var(--primary-blue);
  opacity: 0.85;
}

/* ── Supplier Benefits ──────────────────────────────────── */
.benefits-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
  margin-bottom: 3rem;
}

.benefit-card {
  text-align: center;
  padding: 2rem 1.5rem;
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: 16px;
}

.benefit-icon {
  width: 52px;
  height: 52px;
  margin: 0 auto 1rem;
  background: rgba(65, 95, 170, 0.08);
  border-radius: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--primary-blue);
}

.benefit-icon svg { width: 24px; height: 24px; }
.benefit-card h3 { font-size: 1rem; margin-bottom: 0.35rem; }
.benefit-card p { font-size: 0.85rem; color: var(--primary-gray); line-height: 1.6; }

/* ── Materials Page ─────────────────────────────────────── */
.material-detail {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: 3rem;
  align-items: center;
  padding: 3rem 0;
}

.material-detail:not(:last-child) {
  border-bottom: 1px solid var(--border-light);
}

.material-detail:nth-child(even) { direction: ltr; }
[dir="rtl"] .material-detail:nth-child(even) { direction: rtl; }

.material-detail-visual {
  height: 460px;
  border-radius: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.material-detail-visual svg { width: 80px; height: 80px; opacity: 0.5; }

.material-detail-content h3 { font-size: 1.5rem; margin-bottom: 0.75rem; }
.material-detail-content p { font-size: 0.95rem; color: var(--primary-gray); line-height: 1.7; margin-bottom: 1rem; }

.product-list { margin-bottom: 1.5rem; }
.product-list li {
  padding: 0.4rem 0;
  font-size: 0.9rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.product-list li::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--primary-cyan);
  flex-shrink: 0;
}

/* ═══ Responsive ═══════════════════════════════════════════ */
/* The English nav is wider than Arabic/Chinese; collapse the inline nav to the
   hamburger menu before it can crowd the header. Below this width the menu
   lives in the slide-out panel, so the header stays clean and consistent in
   every language. The WhatsApp icon + "Order Another Way" CTA stay in place. */
@media (max-width: 1199px) {
  .nav-links { display: none; }
  .mobile-toggle { display: flex; }

  /* Mobile nav overlay — must live at the SAME breakpoint as the toggle
     button above. It previously only existed inside the narrower
     max-width:768px query, so on tablet widths (769px-1199px, e.g. iPad
     portrait/landscape) the hamburger button showed and correctly toggled
     the "open" class via JS, but no CSS rule made the panel visible —
     tapping it appeared to do nothing. */
  .mobile-nav {
    position: fixed;
    inset: 0;
    background: #fff;
    z-index: 200;
    padding: 1.5rem;
    display: none;
    flex-direction: column;
  }

  .mobile-nav.open { display: flex; }

  .mobile-nav-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 2rem;
  }

  .mobile-nav a {
    display: block;
    padding: 0.85rem 0;
    font-size: 1.1rem;
    font-weight: 500;
    border-bottom: 1px solid var(--border-light);
  }

  .mobile-nav-ctas {
    margin-top: auto;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding-top: 1.5rem;
  }

  /* .mobile-nav a above (display:block, text-start padding, border-bottom)
     is a generic rule for the plain nav links — it also matches this CTA
     since a .btn is still an <a>, which broke its centering/height/border.
     .mobile-nav-ctas .btn (two classes) outranks .mobile-nav a (one class +
     one tag) regardless of source order, so this cleanly restores the
     normal button look without touching the nav-links rule above. */
  .mobile-nav-ctas .btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 0.85rem 1.5rem;
    border-bottom: none;
    text-align: center;
  }
}

@media (max-width: 1024px) {
  .hero-grid { grid-template-columns: 1fr; text-align: center; }
  .hero-content { max-width: 100%; margin: 0 auto; }
  .hero-ctas { justify-content: center; }
  .hero-secondary-ctas { justify-content: center; }
  .hero-visual { display: none; }
  .quick-actions-grid { grid-template-columns: repeat(3, 1fr); }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .contact-grid { grid-template-columns: 1fr; }
  /* Lead with the form on tablet/mobile — contact methods + company info
     follow below it. Desktop's two-column side-by-side order is untouched;
     this only takes effect once the grid above has already collapsed to a
     single column. */
  .contact-form-col { order: 1; }
  .contact-methods-col { order: 2; }
  /* Single-column stack on tablet/mobile. Force visual on top regardless of
     DOM order, because some cards (e.g. sand) intentionally put content
     before the image in the source to create an alternating zig-zag layout
     on desktop — that flip must not leak into the mobile stack. */
  .material-detail { grid-template-columns: 1fr; }
  .material-detail-visual { order: 1; }
  .material-detail-content { order: 2; }
}

@media (max-width: 768px) {
  :root { --header-height: 60px; }
  .nav-links { display: none; }
  .mobile-toggle { display: flex; }

  /* Hide desktop header "Order Another Way" CTA on mobile — the WhatsApp
     icon button below is the primary mobile entry point. */
  .header-actions .btn-primary { display: none; }

  /* Mobile: WhatsApp shrinks to a high-visibility icon button in the header
     so visitors see the primary order channel before any scrolling. */
  .header-actions .btn-whatsapp {
    padding: 0;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    font-size: 0;
    gap: 0;
    justify-content: center;
    box-shadow: 0 2px 8px rgba(37, 211, 102, 0.35);
  }
  .header-actions .btn-whatsapp svg { width: 18px; height: 18px; }

  /* "اطلب الآن" pill collapses to icon-only in sync with WhatsApp above,
     same breakpoint — keeps .header-actions from crowding in the
     480-768px range where nav is already hidden behind the hamburger
     but the pill previously kept its full label all the way to 480px. */
  .order-now-pill .order-now-label { display: none; }
  .order-now-pill { padding: 0.4rem; }

  .header-inner { height: 60px; gap: 0.75rem; }

  .section { padding: 3rem 0; }
  .hero { min-height: 80vh; }
  .hero h1 { font-size: 2.25rem; }

  /* Hero CTAs — stacked full-width with unified sizing on all mobile viewports */
  .hero-ctas { flex-direction: column; gap: 0.75rem; }
  .hero-ctas .btn { width: 100%; border-radius: 14px; padding: 0.9rem 1.5rem; }

  .quick-actions-grid { grid-template-columns: repeat(2, 1fr); }
  .materials-grid { grid-template-columns: 1fr; }
  /* Mobile: bulletproof stack — flex column with explicit order so neither RTL,
     a browser quirk, nor missing aspect-ratio support can ever flip the image
     and body. Image always order:1 (top), body always order:2 (bottom). */
  .material-card {
    display: flex !important;
    flex-direction: column !important;
  }
  .material-card-visual {
    order: 1 !important;
    width: 100% !important;
    height: auto !important;
    aspect-ratio: 1 / 1;
    /* Fallback if aspect-ratio isn't supported (older Android browsers): */
    min-height: 260px;
  }
  .material-card-body {
    order: 2 !important;
    width: 100% !important;
  }
  .steps-grid { grid-template-columns: repeat(2, 1fr); gap: 1.5rem; }
  .steps-grid::before { display: none; }
  .trust-grid { grid-template-columns: repeat(2, 1fr); }
  .footer-grid { grid-template-columns: 1fr; gap: 2rem; }
  /* Stacked footer link lists need more than 0.3rem of vertical tap padding
     on touch devices — desktop keeps the tighter default. */
  .footer-col a { padding: 0.6rem 0; }
  .footer-bottom { flex-direction: column; gap: 0.5rem; text-align: center; }
  .footer-legal { flex-wrap: wrap; justify-content: center; }
  .section-header h2 { font-size: 1.6rem; }
  .form-row { grid-template-columns: 1fr; }
  .benefits-grid { grid-template-columns: 1fr; }
  /* Dropzone padding tuned for desktop dominates the viewport height on
     small phones — desktop keeps the generous 2.5rem. */
  .file-upload { padding: 1.5rem; }

  /* Page hero — reduce top padding (shorter fixed header) */
  .page-hero { padding: 6rem 0 2rem; }
  .page-hero h1 { font-size: 1.75rem; }
  .page-hero p { font-size: 0.95rem; }

  /* Order form lines — wrap on mobile */
  .order-line {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  .order-line .order-line-title {
    align-self: center;
  }

  .order-line .remove-line-btn {
    align-self: center;
  }

  .order-line .form-group:first-of-type {
    width: 100%;
  }

  /* Material detail — stack and fix direction hack */
  .material-detail { gap: 1.5rem; }
  .material-detail:nth-child(even) { direction: inherit; }
  .material-detail-visual { height: 320px; }

  /* Contact method — prevent text overflow */
  .contact-method { gap: 0.75rem; }
  .contact-method p { word-break: break-word; }

  /* CTA section — tighter on mobile */
  .cta-section { padding: 3rem 0; }
  .cta-section h2 { font-size: 1.5rem; }
  .cta-section p { font-size: 0.95rem; }
}

@media (max-width: 480px) {
  :root { --header-height: 56px; }
  .container { padding: 0 1rem; }
  /* Tightened so the primary WhatsApp CTA sits above the fold on common
     mobile viewports (iPhone SE / 12 / 13). */
  .hero { min-height: auto; padding: 3.5rem 0 2.5rem; }
  .hero-badge { font-size: 0.74rem; padding: 0.28rem 0.7rem; margin-bottom: 0.85rem; }
  .hero h1 { font-size: 1.65rem; margin-bottom: 0.75rem; }
  .hero p { font-size: 0.95rem; margin-bottom: 1.1rem; }
  .hero-ctas { flex-direction: column; margin-bottom: 1.1rem; gap: 0.75rem; }
  .hero-ctas .btn { width: 100%; border-radius: 14px; padding: 0.9rem 1.5rem; }
  .hero-secondary-ctas { flex-direction: column; gap: 0.75rem; }
  .quick-actions-grid { grid-template-columns: 1fr 1fr; }
  .steps-grid { grid-template-columns: 1fr; }
  .trust-grid { grid-template-columns: 1fr; }
  .section-header h2 { font-size: 1.4rem; }

  /* Header — compact */
  .header-inner { height: 56px; }
  .logo img { height: 36px; }
  [dir="rtl"] .logo img { height: 64px; }
  .lang-flag { padding: 0.2rem 0.4rem; font-size: 1rem; }

  /* Latest deals — tighter on mobile */
  .announcement-bar { padding: 0.45rem 0 0; }
  .announcement-slim { font-size: 0.62rem; gap: 0.4rem; }
  /* Icon-only at this width — three text pieces (title, link, delivery
     note) plus an icon no longer fit one slim line without clipping. The
     title attribute on the badge still surfaces the full text on tap/hold. */
  .announcement-delivery-text { display: none; }
  .announcement-nav-btn { width: 2rem; height: 2rem; }
  .announcement-nav-btn svg { width: 1.05rem; height: 1.05rem; }
  .announcement-viewport { padding: 0 2.6rem; }
  /* Widened in proportion to the desktop card (same reasoning: the longest
     price + unit must fit on one line without truncating — unit/currency
     stay at their absolute 0.65rem size on mobile too, so this needs more
     room than a naive scale-down of the desktop width would give it). */
  .announcement-deal { width: 250px; padding: 0.6rem 0.85rem 0.65rem; margin-inline-end: 0.45rem; }
  .announcement-deal-name { font-size: 0.72rem; }
  /* Mobile: keep the hero size compact */
  .announcement-deal-price-row:not(.is-incl) .announcement-deal-price-value { font-size: 0.84rem; }
  .announcement-deal-cta { font-size: 0.58rem; padding: 0.28rem 0.5rem; }
  .announcement-track { padding: 0.18rem 0; }
  /* Same width as the mobile full card (250px, above) — not narrower, so the
     name still has room to sit on one line instead of wrapping. */
  .announcement-bar.is-compact .announcement-deal { padding: 0.4rem 0.7rem; }
  .announcement-bar.is-compact .announcement-deal-compact-price { font-size: 0.68rem; }

  /* Page hero */
  .page-hero { padding: 5rem 0 1.5rem; }
  .page-hero h1 { font-size: 1.5rem; }

  /* Cards — less padding */
  .card { padding: 1.25rem; }
  .action-card { padding: 1.25rem 0.75rem; }

  /* CTA buttons stack */
  .cta-buttons { flex-direction: column; align-items: center; }
  .cta-buttons .btn { width: 100%; max-width: 320px; }

  /* Floating contact widget — smaller and tighter to edge */
  .contact-widget { bottom: 1rem; right: 1rem; }
  [dir="rtl"] .contact-widget { right: auto; left: 1rem; }
  .contact-toggle { width: 48px; height: 48px; }
  .contact-toggle svg { width: 22px; height: 22px; }
  .contact-option { width: 42px; height: 42px; }
  .contact-option svg { width: 19px; height: 19px; }
}

/* Narrowest phones (iPhone SE and similar, ~400px and under): the 2-up
   quick-action cards from the 480px rule above get too tight with an
   icon+heading+body each — drop to one per row here specifically. */
@media (max-width: 400px) {
  .quick-actions-grid { grid-template-columns: 1fr; }
}

/* ── Form Feedback ──────────────────────────────────────── */
.form-feedback {
  padding: 0.85rem 1rem;
  border-radius: 12px;
  font-size: 0.9rem;
  font-weight: 500;
  margin-bottom: 1.25rem;
  animation: fadeIn 0.3s ease;
}

.form-feedback-success {
  background: #ecfdf5;
  color: #065f46;
  border: 1px solid #a7f3d0;
}

.form-feedback-error {
  background: #fef2f2;
  color: #991b1b;
  border: 1px solid #fecaca;
}

/* Discount code field — compact, premium promo-code styling */
.discount-code-box .form-label {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}
.discount-code-tag-icon { color: var(--primary-gray); flex-shrink: 0; }
.discount-code-input-wrap { position: relative; }
.discount-code-input {
  padding-right: 2.5rem;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.discount-code-input.field-valid {
  border-color: #4CAF50;
  box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.1);
}
.discount-code-check-icon {
  position: absolute;
  top: 50%;
  right: 0.85rem;
  transform: translateY(-50%);
  pointer-events: none;
}

/* Field-level validation errors */
.field-error {
  border-color: #E53935 !important;
  box-shadow: 0 0 0 3px rgba(229, 57, 53, 0.1) !important;
}
.field-error-msg {
  color: #E53935;
  font-size: 0.8rem;
  margin-top: 0.3rem;
  animation: fadeIn 0.2s ease;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ── Customer Type Toggle ─────────────────────────────── */
.customer-type-btn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.75rem 1rem;
  border: 2px solid var(--border-light);
  border-radius: 10px;
  background: var(--surface);
  color: var(--primary-gray);
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s;
}

.customer-type-btn:hover {
  border-color: var(--primary-blue);
  color: var(--primary-blue);
}

.customer-type-btn.active {
  border-color: var(--primary-blue);
  background: rgba(65,95,170,0.06);
  color: var(--primary-blue);
}

/* ── Order Form (Alpine.js) ─────────────────────────────── */
.order-line-block {
  background: var(--surface);
  border-radius: 12px;
  margin-bottom: 0.75rem;
  position: relative;
  z-index: 1;
}

.order-line-title {
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--primary-gray);
  white-space: nowrap;
  align-self: center;
}

.order-line {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  gap: 0.75rem;
  align-items: end;
  position: relative;
  padding: 1rem;
}

.order-line.order-line-active {
  z-index: 50;
}

.order-line-block:has(.order-line-active) {
  z-index: 50;
}

.order-line .form-group { margin-bottom: 0; }

.product-search-wrapper {
  position: relative;
}

/* Product dropdown — anchored to its input, scrollable */
.product-dropdown {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  max-height: 260px;
  overflow-y: auto;
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: 12px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  z-index: 60;
  overscroll-behavior: contain;
}

.product-dropdown::-webkit-scrollbar { width: 5px; }
.product-dropdown::-webkit-scrollbar-track { background: transparent; }
.product-dropdown::-webkit-scrollbar-thumb { background: #ddd; border-radius: 10px; }

/* Category header */
.pdd-cat {
  padding: 0.7rem 1rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--surface);
  border-bottom: 1px solid var(--border-light);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--primary-blue);
  position: sticky;
  top: 0;
  z-index: 1;
  user-select: none;
  transition: background 0.15s;
}

.pdd-cat:hover { background: var(--surface-alt); }

.pdd-cat svg {
  transition: transform 0.2s;
  flex-shrink: 0;
  opacity: 0.5;
}

/* Product item */
.pdd-item {
  padding: 0.6rem 1rem 0.6rem 1.6rem;
  cursor: pointer;
  font-size: 0.88rem;
  color: var(--secondary-black);
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--border-light);
  transition: background 0.15s, color 0.15s;
}

.pdd-item:last-child { border-bottom: none; }

.pdd-item:hover {
  background: #f4f7fb;
  color: var(--primary-blue);
}

/* Selected product */
.pdd-item-active {
  background: #eef2fa;
  color: var(--primary-blue);
  font-weight: 600;
}

.pdd-item-active:hover {
  background: #e4eaf5;
}

/* Checkmark */
.pdd-check {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  opacity: 0;
  color: var(--primary-blue);
  transition: opacity 0.15s;
}

.pdd-item-active .pdd-check { opacity: 1; }

/* Empty state */
.product-dropdown-empty {
  padding: 1.25rem 1rem;
  text-align: center;
  color: var(--primary-gray);
  font-size: 0.88rem;
}

.remove-line-btn {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #dc2626;
  background: none;
  border: none;
  cursor: pointer;
  transition: background 0.2s, opacity 0.2s;
}

@media (max-width: 768px) {
  /* Repeatedly-tapped delete icon on the mobile order form — 36px is under
     the ~40-44px touch-target guideline. Desktop keeps the tighter 36px. */
  .remove-line-btn { width: 42px; height: 42px; }
}

.remove-line-btn:hover { background: #fef2f2; }
.remove-line-btn svg { width: 18px; height: 18px; }

.add-line-btn {
  width: 100%;
  padding: 0.65rem;
  border: 2px dashed var(--border-light);
  border-radius: 12px;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--primary-gray);
  transition: all 0.2s;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}

.add-line-btn:hover {
  border-color: var(--primary-blue);
  color: var(--primary-blue);
}

.order-success {
  text-align: center;
  padding: 3rem 1.5rem;
}

.order-success-icon {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: #ecfdf5;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 1.5rem;
  color: #059669;
}

.order-success-icon svg { width: 36px; height: 36px; }
.order-success h2 { font-size: 1.5rem; margin-bottom: 0.5rem; }
.order-success p { color: var(--primary-gray); margin-bottom: 1.5rem; }

.order-id-badge {
  display: inline-block;
  padding: 0.5rem 1.25rem;
  background: var(--surface);
  border-radius: 10px;
  font-family: monospace;
  font-size: 0.95rem;
  font-weight: 600;
  margin-bottom: 1.5rem;
}

/* ── Animations ─────────────────────────────────────────── */
.fade-up {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}

.fade-up.visible {
  opacity: 1;
  transform: translateY(0);
}

/* ── Force-white header (any page with a solid-color hero flush under the
   fixed header) ─────────────────────────────────────────────────────────
   Shared by the products catalog pages (ar/منتجات/, en/products/,
   zh/products/) and the latest-deals pages (ar/احدث-الاسعار/,
   en/latest-prices/, zh/latest-prices/) — add this class to <body> on any
   future page whose hero needs the same treatment, instead of duplicating
   this rule inline again. */
body.force-white-header header.site-header {
  /* main.js's onScroll() drops the "scrolled" class at scrollY=0, which
     would otherwise expose the colored hero through a transparent header
     on load — this keeps the header opaque white at all times instead. */
  background: rgba(255, 255, 255, 0.95) !important;
  backdrop-filter: blur(12px);
  box-shadow: 0 1px 3px rgba(0,0,0,0.06);
}

.pm-hero { background: var(--primary-blue); color: #fff; padding: 8rem 0 3rem; }
.pm-breadcrumb { font-size: 0.85rem; opacity: 0.8; margin-bottom: 1rem; }
.pm-breadcrumb a { color: #fff; text-decoration: underline; text-underline-offset: 3px; }
.pm-hero h1 { font-size: 2.5rem; color: #fff; margin-bottom: 0.75rem; }
.pm-hero p { font-size: 1.05rem; opacity: 0.9; max-width: 600px; }
/* .price-note's own `margin: auto` (for centering it under a centered strip
   elsewhere) would center it here too, misaligned against the flush-start
   h1/p above — flush it to the same start edge instead. Collapsed, this
   adds no extra height beyond .price-note's own small top margin, so
   .pm-hero's rendered height stays identical to every other page using it. */
.pm-hero .price-note { margin-inline: 0; }

/* Building Materials catalog hero only — the text block (breadcrumb/h1/p)
   is capped at 600px, so on desktop there's real empty space beside it
   within the same row. Variant 2 (the four-card design, see
   build-products-catalog.js) fills that empty side instead of stacking
   below the text, so it adds no extra vertical space; align-items:center
   keeps the (shorter) card grid vertically centered against the text
   block's own height rather than growing the row. */
.pm-hero-inner { display: flex; align-items: center; justify-content: space-between; gap: 2rem; }
.pm-hero-text { flex: 1 1 auto; min-width: 0; max-width: 600px; }
.pm-hero-side-cards { flex: 0 0 auto; display: grid; grid-template-columns: repeat(2, minmax(150px, 1fr)); gap: 0.65rem; max-width: 460px; width: 100%; }
/* Frosted-glass cards, same on-dark palette as .price-note--on-dark
   .price-note-card (rgba(255,255,255,0.06) fill / rgba(255,255,255,0.14)
   border) so they read as part of the hero's own design rather than a
   plain white alert box dropped on top of it. backdrop-filter is
   progressive enhancement — the semi-transparent fill alone still reads
   fine against the blue on browsers without it. Slightly larger radius than
   .price-note-card (10px) to feel a touch softer as a standalone card. */
.pm-hero-mini-card {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 13px;
  padding: 0.7rem 0.85rem;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: background 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.pm-hero-mini-card:hover {
  background: rgba(255, 255, 255, 0.16);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  transform: translateY(-1px);
}
/* Circular tinted badge behind the checkmark — reads as a deliberate
   "feature point" marker rather than a bare floating stroke icon. */
.pm-hero-mini-card-icon-wrap {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
}
.pm-hero-mini-card-icon { width: 13px; height: 13px; color: #fff; }
/* Numbering is stripped before these render (see build-products-catalog.js),
   so lines run shorter now — still clamped to 2 lines so every card's
   height stays the same fixed, predictable value regardless of which
   language's longest line (en's "preliminary/approximate..." point still
   wraps). That uniform height is what keeps the grid from ever exceeding
   the text block's height (see .pm-hero-inner comment) and growing the
   hero. Full numbered text is still available via Variant 1's dormant
   expandable list (see the `hidden` <details id="pmHeroVariant1">). */
.pm-hero-mini-card p { margin: 0; font-size: 0.72rem; line-height: 1.4; color: rgba(255, 255, 255, 0.92); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

@media (max-width: 768px) {
  .pm-hero-inner { flex-direction: column; align-items: flex-start; gap: 1.25rem; }
  .pm-hero-text { max-width: 100%; }
  .pm-hero-side-cards { max-width: 100%; }
}

.pm-search-section { padding: 2rem 0 0; }
.pm-search-box { position: relative; max-width: 640px; }
.pm-search-box svg { position: absolute; top: 50%; inset-inline-start: 1.1rem; transform: translateY(-50%); width: 20px; height: 20px; color: var(--primary-gray); pointer-events: none; }
.pm-search-box input { width: 100%; padding-block: 0.9rem; padding-inline: 3rem 1.25rem; border: 1px solid var(--border-light); border-radius: 999px; font: inherit; font-size: 0.95rem; background: #fff; transition: border-color 0.2s, box-shadow 0.2s; }
.pm-search-box input:focus { outline: none; border-color: var(--primary-blue); box-shadow: 0 0 0 3px rgba(65,95,170,0.12); }

.pm-filters-section { padding: 1.5rem 0 0; }
.pm-filter-row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 1rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--border-light); }
.pm-pills { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.pm-pill { padding: 0.55rem 1.2rem; border-radius: 999px; border: 1.5px solid var(--border-light); background: #fff; color: var(--secondary-black); font-size: 0.9rem; font-weight: 600; transition: all 0.2s; }
.pm-pill:hover { border-color: var(--primary-blue); color: var(--primary-blue); }
.pm-pill.active { background: var(--primary-blue); border-color: var(--primary-blue); color: #fff; }
.pm-count { font-size: 0.9rem; color: var(--primary-gray); font-weight: 600; white-space: nowrap; }
.pm-count span { color: var(--primary-blue); font-size: 1rem; }

.pm-chips-wrap { padding: 1.25rem 0 0.25rem; display: flex; flex-direction: column; gap: 1rem; }
.pm-chips-wrap[hidden] { display: none; }
.pm-chip-group { display: flex; flex-direction: column; gap: 0.6rem; }
.pm-chip-label { font-size: 0.8rem; font-weight: 600; color: var(--primary-gray); }
.pm-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.pm-chip { padding: 0.4rem 0.9rem; border-radius: 999px; border: 1.5px solid var(--border-light); background: transparent; color: var(--secondary-black); font-size: 0.82rem; font-weight: 500; transition: all 0.2s; }
.pm-chip:hover { border-color: var(--primary-blue); color: var(--primary-blue); }
.pm-chip.active { border-color: var(--primary-blue); background: rgba(65,95,170,0.1); color: var(--primary-blue); }

.pm-grid-section { padding-top: 2.5rem; }
.pm-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; }
.pm-card { position: relative; background: #fff; border: 1px solid var(--border-light); border-radius: 16px; overflow: hidden; display: flex; flex-direction: column; transition: box-shadow 0.2s, transform 0.2s, border-color 0.2s; }
.pm-grid .pm-card[hidden] { display: none; }
.pm-card:hover { box-shadow: 0 8px 24px rgba(0,0,0,0.08); transform: translateY(-2px); border-color: var(--primary-blue); }
/* aspect-ratio matches the source photos exactly (every product/group image
   on the site is a 1:1 square) — object-fit:cover on a mismatched 4:3 box
   was cropping the sides off every card's image site-wide. */
.pm-card-media { position: relative; background: rgba(65,95,170,0.07); aspect-ratio: 1/1; display: flex; align-items: center; justify-content: center; overflow: hidden; }
/* scale(1.04) trims ~2% off every edge — several source renders have a
   stray 1px dark border baked into their top/left/right/bottom edge (an
   export artifact, confirmed by inspecting the raw files, not a CSS crop
   bug); this crops it out everywhere without needing per-image exceptions,
   and is imperceptible on the many images that don't have the defect. */
.pm-card-media img { width: 100%; height: 100%; object-fit: cover; transform: scale(1.04); }
.pm-icon { width: 64px; height: 64px; color: var(--primary-blue); opacity: 0.55; }
/* Generic fallback icon for a live/merged picker product with no matching
   real photo (see js/product-picker.js's mergeLiveProducts) — same
   proportions/opacity as .picker-custom-media's icon, just scoped to plain
   .pm-card-media instead of requiring the full "منتج آخر" custom-card markup. */
.pm-card-media.has-icon svg { width: 42%; height: 42%; color: var(--primary-blue); opacity: 0.6; }
/* z-index needed since .pm-card-media img now carries a transform (see
   above) — that gives the image its own stacking context, which without
   this would paint over the badge despite the badge's position:absolute
   and earlier DOM order. */
.pm-badge { position: absolute; top: 0.75rem; inset-inline-start: 0.75rem; z-index: 2; background: #fff; color: var(--primary-blue); font-size: 0.72rem; font-weight: 700; padding: 0.3rem 0.7rem; border-radius: 999px; box-shadow: 0 2px 6px rgba(0,0,0,0.08); }
.pm-card-body { padding: 1.1rem 1.15rem 1.25rem; display: flex; flex-direction: column; flex: 1; }
.pm-card-body h3 { font-size: 1rem; color: var(--primary-blue); margin-bottom: 0.4rem; line-height: 1.4; }
.pm-card-body p { font-size: 0.82rem; color: var(--primary-gray); line-height: 1.6; margin-bottom: 1rem; flex: 1; }
.pm-unit-select {
  position: relative;
  z-index: 2;
  width: 100%;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--border-light);
  border-radius: 8px;
  font-size: 0.75rem;
  font-family: inherit;
  color: var(--secondary-black);
  background: #fff;
  margin-bottom: 0.5rem;
}
.pm-unit-select:focus { outline: none; border-color: var(--primary-blue); }
.pm-unit-select:disabled { background: #f3f4f6; color: var(--primary-gray); cursor: default; }
/* Static (non-interactive) unit label — used when a product has no
   documented secondary unit at all (see live-units.js's
   renderUnitBlockFallback). Same box model as .pm-unit-select so card
   alignment stays identical, but a plain <div>, not a <select>, so it
   never shows a false dropdown arrow or looks clickable. */
.pm-unit-static {
  width: 100%;
  box-sizing: border-box;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--border-light);
  border-radius: 8px;
  font-size: 0.75rem;
  font-family: inherit;
  color: var(--primary-gray);
  background: #f3f4f6;
  margin-bottom: 0.5rem;
}
.bm-main-cta .pm-unit-static {
  width: auto;
  flex: 0 1 180px;
  min-width: 140px;
  padding: 0.6rem 0.9rem;
  border: 1.5px solid var(--border-light);
  border-radius: 10px;
  font-size: 0.88rem;
  font-weight: 600;
  margin-bottom: 0;
}
/* Explicit "Add to Cart" button — sits next to the qty stepper everywhere.
   Full label+icon on the spacious hero CTA row; icon-only (label visually
   hidden but still read by screen readers) inside the compact .pm-card
   mini-cards, where WhatsApp + stepper already fill most of the width. */
.btn-add-cart {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.55rem 0.7rem;
  border: 1.5px solid var(--primary-blue);
  border-radius: 10px;
  background: #fff;
  color: var(--primary-blue);
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
  white-space: nowrap;
  flex-shrink: 0;
}
.btn-add-cart:hover { background: var(--primary-blue); color: #fff; }
.btn-add-cart .cart-icon { width: 16px; height: 16px; flex-shrink: 0; }
.btn-add-cart.added { background: #1a9c5c; border-color: #1a9c5c; color: #fff; }
.bm-purchase-price { margin-bottom: 0.25rem; font-size: 1.05rem; }
.bm-main-cta .pm-unit-select {
  width: auto;
  flex: 0 1 180px;
  min-width: 140px;
  padding: 0.6rem 0.9rem;
  border: 1.5px solid var(--border-light);
  border-radius: 10px;
  font-size: 0.88rem;
  font-weight: 600;
  margin-bottom: 0;
  cursor: pointer;
  transition: border-color 0.15s ease;
}
.bm-main-cta .pm-unit-select:hover { border-color: var(--primary-blue); }
.bm-main-cta .pm-unit-select:focus { box-shadow: 0 0 0 3px rgba(65,95,170,0.12); }
.bm-main-cta .pm-unit-select:disabled { background: #f3f4f6; color: var(--primary-gray); cursor: default; }
/* Whole-card click target — the site-wide shared mechanism, not specific to
   .pm-card: the product/category name link is the card's one real, focusable
   anchor; ::after stretches its hit area over the entire card (the standard
   "stretched link" pattern) so image/name/whitespace all navigate. Any
   sibling action buttons/links sit in their own stacking context above the
   stretched overlay so they keep working as independent targets. Reused as-is
   by .fp-card (homepage featured products) and .material-card (homepage
   main-categories) below — both already have position:relative — so every
   clickable-card section on the site shares this one mechanism instead of
   three separate implementations. */
.pm-card-link { color: inherit; }
.pm-card-link::after { content: ''; position: absolute; inset: 0; z-index: 1; }
.pm-card:has(.pm-card-link:focus-visible),
.fp-card:has(.pm-card-link:focus-visible),
.material-card:has(.pm-card-link:focus-visible) { outline: 2px solid var(--primary-blue); outline-offset: 2px; }
.pm-card-actions { position: relative; z-index: 2; display: flex; gap: 0.5rem; margin-top: auto; }
/* flex:1 alone can't shrink these below their content's natural width: a
   flex item's default min-width is "auto", which — combined with the
   nowrap inherited from .btn — means the browser won't shrink it past what
   the unbroken text needs, so longer EN/ZH labels ("Order via WhatsApp")
   overflowed the card at typical 280-380px grid widths. min-width:0 lets
   the button actually honor its flex:1 share; white-space:normal means
   that if the label still doesn't fit on one line at that width, it wraps
   to a second line instead of spilling past the card edge — text is never
   clipped or truncated, in any language, at any card width. */
.pm-card-actions .btn { flex: 1; min-width: 0; padding: 0.55rem 0.5rem; font-size: 0.8rem; border-radius: 10px; white-space: normal; line-height: 1.25; }

/* Quantity stepper — replaces the "Order via Website" button in
   .pm-card-actions (first version, ar/منتجات/ only, see js/cart.js).
   Sits in the same flex:1 slot the button used to occupy. */
.qty-stepper {
  position: relative;
  z-index: 2;
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: stretch;
  border: 1.5px solid var(--border-light);
  border-radius: 10px;
  overflow: hidden;
  background: #fff;
  transition: border-color 0.2s;
}
.qty-stepper.has-qty { border-color: var(--primary-blue); }
.qty-btn {
  flex: 0 0 auto;
  width: 2.1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
  font-weight: 700;
  color: var(--primary-blue);
  background: var(--surface);
  transition: background 0.15s;
}
.qty-btn:hover { background: var(--border-light); }
.qty-btn:active { background: rgba(65, 95, 170, 0.15); }
.qty-input, .qty-input-custom {
  flex: 1;
  min-width: 0;
  width: 100%;
  border: none;
  border-inline: 1.5px solid var(--border-light);
  text-align: center;
  font-size: 0.85rem;
  font-weight: 600;
  font-family: inherit;
  color: var(--secondary-black);
  background: transparent;
  padding: 0.4rem 0.2rem;
}
.qty-input:focus, .qty-input-custom:focus { outline: none; background: var(--surface); }

.pm-empty { text-align: center; color: var(--primary-gray); padding: 3rem 0; font-size: 0.95rem; }

/* ── Shared product-picker (ar/order.html, ar/سلة/) ────────────────────
   #cartItemsList cards reuse .pm-card/.pm-grid/.pm-badge as-is — see
   js/product-picker.js. This is just the small live counter above it. */
.cart-summary-count { font-size: 0.9rem; color: var(--primary-gray); margin-bottom: 1.25rem; }
.cart-summary-count strong { color: var(--primary-blue); }

/* Cart list — compact horizontal rows (not big cards), so 10-20+ items
   stay scannable and the page doesn't get excessively long. Box values
   (radius/padding/border, no shadow) deliberately match .card/.card-compact
   exactly — same container the order-info box below uses — so the two
   read as one cohesive page instead of mismatched blocks. */
.cart-rows { background: #fff; border: 1px solid var(--border-light); border-radius: 20px; padding: 0.5rem 1.5rem; }
/* Each product is a .cart-row-wrap (the divider/spacing owner) holding the
   compact .cart-row line. */
.cart-row-wrap { border-bottom: 1px solid var(--border-light); }
.cart-row-wrap:last-child { border-bottom: none; }
/* Two lines at every width, not just mobile: thumbnail + name + remove on
   line 1, quantity stepper + note button on line 2 (a zero-height ::after
   flex item with an order between them forces the wrap — no markup
   change). Packing all five controls into one row was squeezing
   .cart-row-info so narrow that even a normal-length product name wrapped
   or got cut off; splitting the row into two lines gives the name column
   the full row width on line 1, so it reliably fits on one line. */
.cart-row { display: flex; align-items: center; flex-wrap: wrap; row-gap: 0.55rem; gap: 0.75rem; padding: 0.85rem 0; }
.cart-row-thumb { order: 1; flex: 0 0 auto; width: 64px; height: 64px; border-radius: 10px; overflow: hidden; background: rgba(65,95,170,0.07); }
.cart-row-thumb img { width: 100%; height: 100%; object-fit: cover; }
/* A "منتج آخر" custom entry's thumb holds the dedicated question-mark icon
   (see CUSTOM_ITEM_ICON_SVG in js/product-picker.js), not a product photo
   — padding gives it the same breathing room a real photo doesn't need. */
.cart-row-thumb.is-custom-thumb { background: var(--surface); padding: 12px; }
.cart-row-thumb.is-custom-thumb svg { width: 100%; height: 100%; }
.cart-row-info { order: 2; flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.3rem; }
.cart-row::after { content: ""; order: 4; flex-basis: 100%; height: 0; }
/* Show the full name, wrapping onto a second line rather than cutting it
   off with an ellipsis — a long compound product name was getting cut off
   mid-word. */
.cart-row-name { font-size: 0.92rem; font-weight: 600; color: var(--secondary-black); white-space: normal; overflow: visible; word-break: break-word; line-height: 1.3; }
.cart-row-badge { align-self: flex-start; font-size: 0.7rem; font-weight: 700; color: var(--primary-blue); background: rgba(65,95,170,0.08); padding: 0.15rem 0.6rem; border-radius: 999px; }
/* Placeholder reference price x qty per cart line (see price-placeholder.js:
   TEMPORARY PLACEHOLDER DATA, not a real price feed), plus the matching
   approximate-total bar below the whole list. Reuses .riyal-icon and
   .ref-price-approx (see the product-card .ref-price rules) for visual
   consistency instead of a third variant of the same idea. */
.cart-row-price { display: flex; align-items: baseline; gap: 0.25rem; font-size: 0.8rem; font-weight: 700; color: #0d6e3a; font-variant-numeric: tabular-nums; }
.cart-row-price .riyal-icon { width: 0.85em; height: 0.85em; }
/* "Pricing after order" comparison mode — same blue-pill treatment as the
   product-card .ref-price.ref-price-quote variant, no icon/number here. */
.cart-row-price.ref-price-quote { color: var(--primary-blue); }
.cart-total-row { display: flex; align-items: baseline; justify-content: flex-end; gap: 0.5rem; background: #fff; border: 1px solid var(--border-light); border-radius: 14px; padding: 0.85rem 1.25rem; margin-top: 0.75rem; }
.cart-total-label { font-weight: 600; font-size: 0.9rem; color: var(--secondary-black); margin-inline-end: auto; }
.cart-total-value { display: inline-flex; align-items: baseline; gap: 0.3rem; font-size: 1.1rem; font-weight: 800; color: #0d6e3a; font-variant-numeric: tabular-nums; }
.cart-total-value .riyal-icon { width: 0.85em; height: 0.85em; }
/* Cart total: shown only once every item in the cart has a real reference
   price (see js/product-picker.js's updatePricesAndTotal). If even one item
   is unpriced (a quoteAfterOrder product, or a "منتج آخر" custom entry —
   see .picker-custom-card below), the row shows the same
   .ref-price.ref-price-quote block used everywhere else instead of a
   number — no separate note, this IS the total row's content in that case. */
.cart-total-row .ref-price.ref-price-quote { margin-bottom: 0; }
/* Disclaimer notes shown right below the total, only alongside the numeric
   (fully-priced) case above — never alongside the quoteAfterOrder block. */
/* A light green callout (echoes the green already used for prices
   sitewide, e.g. .ref-price/.cart-row-price) so these read as a distinct,
   deliberate disclaimer rather than plain trailing text. */
.cart-total-notes { font-size: 0.72rem; color: #2e5a3d; text-align: start; line-height: 1.6; margin-top: 0.5rem; background: rgba(13,110,58,0.06); border: 1px solid rgba(13,110,58,0.18); border-radius: 12px; padding: 0.75rem 1rem; }
.cart-total-notes p { margin: 0.15rem 0; }

/* "منتج آخر" (custom/other product) entry in the add-products picker modal
   — a free-text name + qty instead of a catalog card, for something the
   customer couldn't find. No reference price (see price-placeholder.js /
   product-picker.js's PRICE_MAP lookups, which simply don't have an entry
   for a customer-typed name) — submitted for the team to review/source. */
.picker-custom-card .pm-card-media { display: flex; align-items: center; justify-content: center; background: var(--surface); }
.picker-custom-card .pm-card-media svg { width: 42%; height: 42%; }
.picker-custom-name-input { margin-bottom: 0.6rem; }
.picker-custom-add-btn { width: 100%; margin-top: 0.75rem; }

/* Register page — live password-rules checklist (register.html, ar/en/zh). */
.password-rules { list-style: none; padding: 0; margin: 0.5rem 0 1.25rem; display: flex; flex-wrap: wrap; gap: 0.4rem 1.25rem; font-size: 0.8rem; color: var(--primary-gray); }
.password-rules li { display: flex; align-items: center; gap: 0.35rem; transition: color 0.15s; }
.password-rules .rule-icon { display: inline-flex; width: 1em; justify-content: center; }
.password-rules li.rule-met { color: #2e7d32; }

/* Account page — demo-only sections added on top of profile/locations/orders
   (account.html, ar/en/zh): credit, invoices, price list, analytics, activity. */
.acct-section-header { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 1.25rem; }
.acct-section-header h2 { font-size: 1.15rem; margin: 0; }
.acct-preview-tag { display: inline-block; padding: 0.2rem 0.65rem; border-radius: 999px; font-size: 0.7rem; font-weight: 700; background: rgba(249,174,64,0.15); color: #b8790a; white-space: nowrap; }
.acct-section-note { font-size: 0.82rem; color: var(--primary-gray); margin: -0.5rem 0 1rem; line-height: 1.6; }
.acct-stat-label { display: block; font-size: 0.78rem; color: var(--primary-gray); margin-bottom: 0.2rem; }
.acct-stat-value { font-weight: 700; font-size: 1.05rem; }
.acct-progress-track { margin-top: 1rem; height: 8px; border-radius: 999px; background: var(--surface); overflow: hidden; }
.acct-progress-fill { height: 100%; background: var(--primary-blue); border-radius: 999px; }
.acct-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.acct-table th { text-align: start; color: var(--primary-gray); font-weight: 600; padding: 0.5rem 0.25rem; border-bottom: 1px solid var(--border-light); }
.acct-table td { padding: 0.6rem 0.25rem; border-bottom: 1px solid var(--border-light); }
.acct-status-pill { display: inline-block; padding: 0.2rem 0.65rem; border-radius: 999px; font-size: 0.72rem; font-weight: 700; }
.acct-status-pending { background: rgba(249,174,64,0.15); color: #b8790a; }
.acct-status-paid { background: rgba(13,110,58,0.12); color: #0d6e3a; }
.acct-stat-tile { flex: 1 1 140px; background: var(--surface); border-radius: 12px; padding: 1rem 1.25rem; display: flex; flex-direction: column; gap: 0.3rem; }
.acct-stat-tile-value { font-size: 1.4rem; font-weight: 800; color: var(--primary-blue); }
.acct-stat-tile-label { font-size: 0.78rem; color: var(--primary-gray); }
.acct-timeline { list-style: none; margin: 0; padding: 0; }
.acct-timeline li { display: flex; gap: 0.75rem; padding-bottom: 1.1rem; position: relative; }
.acct-timeline li:not(:last-child)::before { content: ''; position: absolute; top: 0.9rem; inset-inline-start: 5px; width: 1px; height: 100%; background: var(--border-light); }
.acct-timeline-dot { flex-shrink: 0; width: 11px; height: 11px; border-radius: 50%; background: var(--primary-blue); margin-top: 0.25rem; z-index: 1; }
.acct-timeline-text { font-size: 0.88rem; font-weight: 600; }
.acct-timeline-date { font-size: 0.78rem; color: var(--primary-gray); margin-top: 0.15rem; }
.acct-order-project { font-size: 0.82rem; color: var(--primary-gray); margin: -0.4rem 0 0.85rem; }
.acct-no-results { text-align: center; color: var(--primary-gray); font-size: 0.88rem; padding: 1.5rem 0; }

/* ── Status pill color modifiers (reused across the customer portal's
   orders/quotes/invoices/deliveries — the base .acct-status-pill sizing
   above is shared, these just add color) ─────────────────────────── */
.acct-status-gray { background: rgba(146,147,150,0.14); color: var(--primary-gray); }
.acct-status-blue { background: rgba(65,95,170,0.1); color: var(--primary-blue); }
.acct-status-cyan { background: rgba(96,193,212,0.16); color: #12798c; }
.acct-status-teal { background: rgba(13,148,136,0.12); color: #0d9488; }
.acct-status-green-solid { background: #0d6e3a; color: #fff; }
.acct-status-red { background: rgba(229,57,53,0.1); color: #c62828; }

/* ── Customer Portal Dashboard (account.html, ar) ───────────────────
   Single-page dashboard shell: the site's standard .pm-hero band (same
   one login.html/register.html/become-supplier.html/etc. use — breadcrumb
   + h1 + subtitle) with a row of portal-only action icons added via the
   .portal-hero modifier below, plus a sidebar of 15 sections switched
   purely client-side (Alpine `activeSection`, see the page's own
   <script>). Everything here is layout/chrome only — content inside each
   section reuses .card/.btn/.form-input/.acct-* from elsewhere in this
   file; .pm-hero itself (background/padding/h1/p) is untouched. */
.portal-hero .container { position: relative; }
.portal-hero-actions { position: absolute; top: 0; inset-inline-end: 1.5rem; display: flex; align-items: center; gap: 0.5rem; }
.portal-icon-btn,
.portal-mobile-menu-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: rgba(255,255,255,0.12);
  color: #fff;
}
.portal-icon-btn:hover,
.portal-mobile-menu-btn:hover { background: rgba(255,255,255,0.22); }
.portal-icon-btn svg,
.portal-mobile-menu-btn svg { width: 20px; height: 20px; }
.portal-icon-dot {
  position: absolute;
  top: 6px;
  inset-inline-end: 7px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--secondary-gold);
  border: 1.5px solid var(--primary-blue);
}
.portal-mobile-menu-btn { display: none; }

.portal-shell { padding: 1.5rem 0 4rem; }
.portal-layout { display: flex; align-items: flex-start; gap: 1.5rem; }

.portal-sidebar {
  flex: 0 0 250px;
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: 20px;
  padding: 0.75rem;
  position: sticky;
  top: calc(var(--header-height) + 1rem);
  max-height: calc(100vh - var(--header-height) - 2rem);
  overflow-y: auto;
}
.portal-nav-item {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  width: 100%;
  padding: 0.7rem 0.85rem;
  border-radius: 12px;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--secondary-black);
  text-align: start;
}
.portal-nav-item:hover { background: var(--surface); }
.portal-nav-item.active { background: rgba(65,95,170,0.08); color: var(--primary-blue); }
.portal-nav-item .portal-nav-emoji { font-size: 1.05rem; flex-shrink: 0; width: 1.3em; text-align: center; }
.portal-nav-item.portal-nav-logout { margin-top: 0.5rem; border-top: 1px solid var(--border, #e5e7eb); border-radius: 0 0 10px 10px; padding-top: 0.85rem; color: #c0392b; }
.portal-nav-item.portal-nav-logout:hover { background: rgba(192,57,43,0.06); }
.portal-icon-btn.portal-icon-logout:hover { background: rgba(229,57,53,0.35); }

/* Logout confirmation — reuses the .picker-modal-overlay/.picker-modal
   shell (same backdrop, radius, shadow, open animation); this only makes
   it a compact dialog instead of the picker's tall scrolling list. */
.picker-modal.confirm-modal { max-width: 400px; max-height: none; }
.confirm-modal-body { padding: 1.35rem; }
.confirm-modal-body p { font-size: 0.9rem; color: var(--primary-gray); margin: 0; }
.confirm-modal-actions { display: flex; gap: 0.6rem; justify-content: flex-end; padding: 0 1.35rem 1.35rem; }
.btn.btn-danger { background: #E53935; color: #fff; border-color: #E53935; }
.btn.btn-danger:hover { background: #c62828; border-color: #c62828; }

/* ── Customer portal: quotes / invoices / credit / profile completion ──
   Everything below is ar/account.html-only chrome, built on the existing
   portal-* / acct-* tokens. Modals reuse .picker-modal-* (see above). */
.acct-status-muted { background: rgba(146,147,150,0.1); color: #7b7c80; text-decoration: line-through; text-decoration-color: rgba(123,124,128,0.5); }
.acct-progress-fill.is-complete { background: #0d6e3a; }
.acct-progress-fill.is-danger { background: #E53935; }
.portal-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.portal-btn-danger-text { color: #c62828; }
.portal-btn-danger-text:hover { color: #c62828; border-color: rgba(229,57,53,0.5); background: rgba(229,57,53,0.06); }
.btn:disabled, .btn[disabled] { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }
.portal-field-hint { font-size: 0.78rem; color: var(--primary-gray); margin: 0.35rem 0 0; }
.portal-subhead { font-size: 0.95rem; margin: 0 0 0.75rem; }
.portal-result-count { font-size: 0.78rem; color: var(--primary-gray); margin: -0.4rem 0 0.75rem; }
.portal-visually-hidden { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Alerts / banners */
.portal-alert { display: flex; align-items: flex-start; gap: 0.7rem; border-radius: 12px; padding: 0.8rem 1rem; margin-bottom: 1.1rem; font-size: 0.86rem; line-height: 1.6; border: 1px solid transparent; }
.portal-alert-icon { flex-shrink: 0; font-size: 1.05rem; line-height: 1.4; }
.portal-alert-body { flex: 1 1 auto; min-width: 0; }
.portal-alert-actions { display: flex; align-items: center; gap: 0.4rem; flex-shrink: 0; }
.portal-alert-close { width: 30px; height: 30px; border-radius: 8px; font-size: 1.3rem; line-height: 1; color: var(--primary-gray); display: inline-flex; align-items: center; justify-content: center; }
.portal-alert-close:hover { background: rgba(0,0,0,0.05); }
.portal-alert-info { background: rgba(65,95,170,0.06); border-color: rgba(65,95,170,0.22); color: #2b3f73; }
.portal-alert-warn { background: rgba(249,174,64,0.1); border-color: rgba(249,174,64,0.35); color: #7a5209; }
.portal-alert-warning { background: rgba(214,138,0,0.08); border-color: rgba(214,138,0,0.35); color: #7a4b00; }
.portal-alert-danger { background: rgba(229,57,53,0.07); border-color: rgba(229,57,53,0.3); color: #8e1f1c; }
.portal-alert-success { background: rgba(13,110,58,0.07); border-color: rgba(13,110,58,0.25); color: #0d5a31; }

/* Home: quick actions + profile completion */
.portal-quick-actions { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.85rem; margin-bottom: 1.5rem; }
.portal-quick-action { display: flex; align-items: center; gap: 0.65rem; padding: 0.95rem 1.1rem; background: #fff; border: 1px solid var(--border-light); border-radius: 14px; font-weight: 700; font-size: 0.9rem; color: var(--secondary-black); text-align: start; transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s; }
.portal-quick-action:hover { border-color: var(--primary-blue); color: var(--primary-blue); box-shadow: 0 6px 18px rgba(65,95,170,0.1); transform: translateY(-1px); }
.portal-quick-action-icon { width: 38px; height: 38px; flex-shrink: 0; border-radius: 10px; background: rgba(65,95,170,0.08); display: inline-flex; align-items: center; justify-content: center; font-size: 1.15rem; }

/* Section headers with a button group (invoices) */
.portal-btn-group { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
.portal-btn-group-label { font-size: 0.8rem; color: var(--primary-gray); font-weight: 600; }
.portal-date-field { display: flex; align-items: center; gap: 0.4rem; flex: 1 1 170px; font-size: 0.8rem; color: var(--primary-gray); }
.portal-toolbar .portal-date-field .form-input { flex: 1 1 auto; min-width: 0; }
.portal-uploaded-tag { font-size: 0.72rem; color: #0d6e3a; margin-top: 0.2rem; white-space: nowrap; }
.portal-file-chip { display: inline-block; font-size: 0.82rem; background: var(--surface); border: 1px solid var(--border-light); border-radius: 8px; padding: 0.35rem 0.7rem; margin: 0 0 0.85rem; }
.portal-overdue-days { display: block; font-size: 0.72rem; color: #c62828; font-weight: 700; margin-top: 0.2rem; }

/* Modals: form + invoice sizes of the shared picker shell */
.picker-modal.form-modal { max-width: 540px; max-height: min(90vh, 760px); }
.picker-modal.invoice-modal { max-width: 760px; max-height: 92vh; }
.portal-modal-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.6rem; margin-top: 0.5rem; }
.picker-modal-body .form-group { margin-bottom: 1rem; }

/* Quote details drawer */
.portal-drawer-overlay { position: fixed; inset: 0; z-index: 1900; background: rgba(34,31,31,0.45); opacity: 0; visibility: hidden; transition: opacity 0.2s ease, visibility 0.2s; }
.portal-drawer-overlay.open { opacity: 1; visibility: visible; }
.portal-drawer { position: absolute; top: 0; bottom: 0; inset-inline-end: 0; width: min(560px, 100%); background: #fff; box-shadow: 0 0 40px rgba(0,0,0,0.2); display: flex; flex-direction: column; transform: translateX(100%); transition: transform 0.25s ease; }
[dir="rtl"] .portal-drawer { transform: translateX(-100%); }
.portal-drawer-overlay.open .portal-drawer { transform: translateX(0); }
.portal-drawer-inner { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.portal-drawer-header { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding: 1rem 1.35rem; border-bottom: 1px solid var(--border-light); flex-shrink: 0; }
.portal-drawer-title { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.portal-drawer-title h3 { font-size: 1.05rem; margin: 0; }
.portal-drawer-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 1.25rem 1.35rem 2rem; }
.portal-drawer-h { font-size: 0.92rem; margin: 0.25rem 0 0.75rem; }
.portal-linked-order { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); align-items: center; gap: 0.75rem; padding: 0.9rem 1rem; border: 1px solid rgba(65,95,170,0.22); background: rgba(65,95,170,0.04); border-radius: 12px; margin-bottom: 1rem; }
.portal-linked-order .acct-stat-label { margin-bottom: 0.3rem; }
.portal-comments { display: flex; flex-direction: column; gap: 0.6rem; margin-bottom: 0.85rem; }
.portal-comment { max-width: 85%; padding: 0.6rem 0.85rem; border-radius: 12px; font-size: 0.86rem; line-height: 1.6; }
.portal-comment.is-customer { align-self: flex-start; background: rgba(65,95,170,0.08); border-start-start-radius: 4px; }
.portal-comment.is-staff { align-self: flex-end; background: var(--surface); border: 1px solid var(--border-light); border-start-end-radius: 4px; }
.portal-comment-meta { font-size: 0.72rem; color: var(--primary-gray); margin-bottom: 0.2rem; }
.portal-typing { font-size: 0.78rem; color: var(--primary-gray); font-style: italic; margin: 0; }
.portal-comment-form { display: flex; gap: 0.5rem; align-items: flex-end; }
.portal-comment-form .form-textarea { min-height: 0; flex: 1 1 auto; }

/* Credit: demo switch, stepper, documents */
.portal-demo-switch { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; padding: 0.7rem 0.9rem; margin-bottom: 1.25rem; border: 1.5px dashed rgba(249,174,64,0.6); border-radius: 12px; background: rgba(249,174,64,0.05); font-size: 0.85rem; }
.portal-demo-switch .form-select { width: auto; flex: 1 1 260px; max-width: 380px; padding: 0.45rem 0.8rem; }
.portal-stepper { list-style: none; margin: 0 0 1.5rem; padding: 0; display: flex; gap: 0.5rem; counter-reset: none; }
.portal-step { flex: 1 1 0; display: flex; align-items: center; gap: 0.6rem; padding: 0.75rem 0.9rem; border-radius: 12px; background: var(--surface); border: 1px solid var(--border-light); position: relative; min-width: 0; }
.portal-step-num { width: 30px; height: 30px; flex-shrink: 0; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-weight: 800; font-size: 0.85rem; background: #fff; border: 1.5px solid var(--border-light); color: var(--primary-gray); }
.portal-step-text { display: flex; flex-direction: column; min-width: 0; }
.portal-step-label { font-weight: 700; font-size: 0.88rem; }
.portal-step-sub { font-size: 0.74rem; color: var(--primary-gray); }
.portal-step.is-done .portal-step-num { background: #0d6e3a; border-color: #0d6e3a; color: #fff; }
.portal-step.is-current { background: rgba(65,95,170,0.07); border-color: var(--primary-blue); }
.portal-step.is-current .portal-step-num { background: var(--primary-blue); border-color: var(--primary-blue); color: #fff; }
.portal-step.is-error { background: rgba(229,57,53,0.06); border-color: rgba(229,57,53,0.5); }
.portal-step.is-error .portal-step-num { background: #E53935; border-color: #E53935; }
.portal-doc-list { list-style: none; margin: 0 0 0.5rem; padding: 0; border: 1px solid var(--border-light); border-radius: 12px; }
.portal-doc-row { display: flex; align-items: center; gap: 0.75rem; padding: 0.7rem 0.9rem; }
.portal-doc-row + .portal-doc-row { border-top: 1px solid var(--border-light); }
.portal-doc-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.portal-doc-label { font-weight: 600; font-size: 0.88rem; }
.portal-doc-file { font-size: 0.76rem; color: var(--primary-gray); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.portal-upload-btn { cursor: pointer; position: relative; }
.portal-upload-btn:focus-within { outline: 2px solid var(--primary-blue); outline-offset: 2px; }
.portal-submit-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin-bottom: 1.75rem; }
.portal-review-msg { font-size: 0.9rem; margin: 0.85rem 0 1rem; line-height: 1.7; }
.portal-review-note { font-size: 0.85rem; background: rgba(249,174,64,0.08); border-radius: 10px; padding: 0.6rem 0.85rem; color: #7a5209; margin-bottom: 1rem; }
.portal-stat-card.is-suspended { background: repeating-linear-gradient(-45deg, #fff, #fff 8px, rgba(229,57,53,0.04) 8px, rgba(229,57,53,0.04) 16px); }
.portal-stat-card.is-suspended .portal-stat-card-value { color: var(--primary-gray) !important; text-decoration: line-through; }

/* Printable invoice / invoice history (screen preview + print) */
.portal-print-area { display: none; }
.print-doc { font-size: 0.85rem; color: #221f1f; }
.print-head { display: flex; justify-content: space-between; gap: 1rem; padding-bottom: 0.85rem; margin-bottom: 1rem; border-bottom: 2px solid var(--primary-blue); }
.print-head h1 { font-size: 1.25rem; margin: 0 0 0.2rem; }
.print-brand { font-size: 1.35rem; font-weight: 800; color: var(--primary-blue); }
.print-muted { color: #6b6c70; font-size: 0.78rem; }
.print-label { font-size: 0.72rem; color: #6b6c70; font-weight: 700; margin-bottom: 0.2rem; }
.print-parties { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1rem; line-height: 1.7; }
.print-table { width: 100%; border-collapse: collapse; margin-bottom: 1rem; font-size: 0.8rem; }
.print-table th, .print-table td { border: 1px solid #dcdde0; padding: 0.4rem 0.55rem; text-align: start; }
.print-table thead th, .print-table tfoot th { background: #f4f5f7; }
.print-totals { margin-inline-start: auto; border-collapse: collapse; min-width: 280px; font-size: 0.82rem; }
.print-totals td { padding: 0.3rem 0.6rem; border-bottom: 1px solid #eceef1; }
.print-totals td:last-child { text-align: end; font-weight: 600; white-space: nowrap; }
.print-totals .is-total td { font-weight: 800; font-size: 0.95rem; border-top: 2px solid #221f1f; }
.print-bank { margin: 0.5rem 0 1rem; border: 1px solid #dcdde0; border-radius: 8px; padding: 0.7rem 0.9rem; font-size: 0.82rem; line-height: 1.8; max-width: 440px; }
.print-bank h4 { margin: 0 0 0.35rem; font-size: 0.88rem; }
.print-bank-row { display: flex; gap: 0.5rem; align-items: baseline; }
.print-bank-row .print-label { margin: 0; min-width: 6.5em; }
.print-bank-row span[dir="ltr"] { font-weight: 700; letter-spacing: 0.02em; }
.portal-bank { border: 1px solid rgba(65,95,170,0.22); background: rgba(65,95,170,0.04); border-radius: 12px; padding: 0.9rem 1rem; margin-bottom: 1.1rem; }
.portal-bank-title { font-weight: 700; font-size: 0.9rem; margin-bottom: 0.6rem; }
.portal-bank-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 0.6rem 1rem; margin: 0; }
.portal-bank-grid dt { font-size: 0.74rem; color: var(--primary-gray); margin-bottom: 0.15rem; }
.portal-bank-grid dd { margin: 0; font-weight: 600; font-size: 0.9rem; }
.portal-bank-iban-row { grid-column: 1 / -1; }
.portal-bank-iban { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.portal-bank-iban span[dir="ltr"] { font-weight: 700; letter-spacing: 0.03em; word-break: break-all; }
.print-demo { margin-top: 1.25rem; text-align: center; font-size: 0.75rem; color: #b8790a; border: 1px dashed rgba(249,174,64,0.7); border-radius: 8px; padding: 0.4rem; }

@media (max-width: 900px) {
  .portal-quick-actions { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .portal-quick-action { padding: 0.75rem; font-size: 0.82rem; }
  .portal-quick-action-icon { width: 32px; height: 32px; font-size: 1rem; }
  .portal-alert { flex-wrap: wrap; }
  .portal-alert-actions { width: 100%; justify-content: space-between; }
  .portal-stepper { gap: 0.35rem; }
  .portal-step { flex-direction: column; text-align: center; padding: 0.6rem 0.4rem; gap: 0.35rem; }
  .portal-step-sub { display: none; }
  .portal-step-label { font-size: 0.78rem; }
  .portal-doc-row { flex-wrap: wrap; }
  .portal-doc-info { flex-basis: 100%; }
  .portal-drawer { width: 100%; }
  .portal-comment { max-width: 95%; }
  .print-parties { grid-template-columns: 1fr; }
  .print-head { flex-direction: column; }
  .print-head > div:last-child { text-align: start !important; }
  .portal-date-field { flex-basis: 100%; }
}

@media print {
  /* Only the print area (a direct child of <main>) prints; display:none
     (not visibility) so the hidden page doesn't add blank pages. */
  body.portal-printing > *:not(main),
  body.portal-printing main > *:not(.portal-print-area) { display: none !important; }
  body.portal-printing main { padding: 0 !important; margin: 0 !important; }
  body.portal-printing .portal-print-area { display: block !important; }
  body.portal-printing .print-table th,
  body.portal-printing .print-table td { font-size: 9.5pt; }
  body.portal-printing .print-doc { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  @page { margin: 14mm; }
}

.portal-main { flex: 1 1 0; min-width: 0; }
.portal-section-title { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem; }
.portal-section-title h1 { font-size: 1.4rem; }

.portal-stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1.75rem; }
.portal-stat-grid-3 { grid-template-columns: repeat(3, 1fr); }
.portal-stat-card { background: #fff; border: 1px solid var(--border-light); border-radius: 16px; padding: 1.1rem 1.25rem; }
.portal-stat-card-label { font-size: 0.8rem; color: var(--primary-gray); margin-bottom: 0.5rem; }
.portal-stat-card-value { font-size: 1.3rem; font-weight: 800; color: var(--primary-blue); display: flex; align-items: baseline; gap: 0.3rem; }

.portal-block { margin-bottom: 1.75rem; }
[x-cloak] { display: none !important; }
/* «الشراء بالآجل» card (home) */
.portal-credit-card { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.portal-credit-text h2 { font-size: 1.02rem; margin: 0 0 0.3rem; }
.portal-credit-text p { margin: 0; font-size: 0.86rem; color: var(--primary-gray); line-height: 1.6; }
.portal-credit-text .portal-credit-done { color: var(--primary-blue); font-weight: 600; }
/* Delivery row detail (receiver + planned items) */
.portal-table tbody tr.is-expanded { background: var(--surface); }
.portal-table tr.portal-row-detail td { background: var(--surface); border-top: 0; }
.portal-delivery-detail { display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.84rem; line-height: 1.6; }
.portal-detail-label { color: var(--primary-gray); }
.portal-detail-item { display: inline-block; margin-inline-end: 0.75rem; }
.portal-block-header { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 1rem; }
.portal-block-header h2 { font-size: 1.08rem; }

.portal-toolbar { display: flex; flex-wrap: wrap; gap: 0.65rem; margin-bottom: 1.1rem; }
.portal-toolbar .form-input,
.portal-toolbar .form-select { width: auto; flex: 1 1 200px; }
.portal-toolbar .form-input[type="search"] { flex: 2 1 240px; }

.portal-tabs { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.1rem; }
.portal-tab-btn {
  padding: 0.55rem 1.1rem;
  border-radius: 999px;
  border: 1.5px solid var(--border-light);
  background: var(--surface);
  color: var(--primary-gray);
  font-size: 0.85rem;
  font-weight: 600;
}
.portal-tab-btn:hover { border-color: var(--primary-blue); color: var(--primary-blue); }
.portal-tab-btn.active { border-color: var(--primary-blue); background: rgba(65,95,170,0.08); color: var(--primary-blue); }

.portal-table-wrap { overflow-x: auto; border: 1px solid var(--border-light); border-radius: 16px; }
.portal-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; min-width: 640px; }
.portal-table th { text-align: start; color: var(--primary-gray); font-weight: 600; font-size: 0.78rem; padding: 0.75rem 1rem; background: var(--surface); border-bottom: 1px solid var(--border-light); white-space: nowrap; }
.portal-table td { padding: 0.85rem 1rem; border-bottom: 1px solid var(--border-light); vertical-align: middle; }
.portal-table tbody tr:last-child td { border-bottom: none; }
.portal-table td[dir="ltr"] { white-space: nowrap; }
.portal-table.portal-table-wide { min-width: 980px; }
.portal-table tbody tr.is-clickable { cursor: pointer; }
.portal-table tbody tr.is-clickable:hover { background: var(--surface); }
.portal-row-detail { background: var(--surface); padding: 1.1rem 1.25rem; border-bottom: 1px solid var(--border-light); }
.portal-row-detail-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 0.85rem; margin-bottom: 1rem; }
.portal-chevron { display: inline-block; transition: transform 0.2s; color: var(--primary-gray); }
.portal-chevron.is-open { transform: rotate(180deg); }
/* Estimates («التقديرات الأولية») — small read-only cards above the quotes table. */
.portal-estimates { margin-bottom: 1.5rem; }
.portal-estimate-card { background: #fff; border: 1px solid var(--border-light); border-radius: 12px; padding: 0.8rem 1rem; margin-bottom: 0.6rem; }
.portal-estimate-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.75rem; font-size: 0.9rem; }
.portal-estimate-head.is-clickable { cursor: pointer; }
.portal-estimate-meta { font-size: 0.8rem; color: var(--primary-gray); }
.portal-estimate-total { margin-inline-start: auto; white-space: nowrap; }
.portal-estimate-card .portal-field-hint { margin: 0.4rem 1.6rem 0 0; color: #7a5209; }
.portal-estimate-body { margin-top: 0.75rem; padding-top: 0.75rem; border-top: 1px solid var(--border-light); }

.portal-card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1rem; }
.portal-project-card { background: #fff; border: 1px solid var(--border-light); border-radius: 16px; padding: 1.25rem; }
.portal-project-card h3 { font-size: 1rem; margin-bottom: 0.3rem; }
.portal-project-card .portal-project-loc { font-size: 0.82rem; color: var(--primary-gray); margin-bottom: 0.85rem; }
.portal-project-meta { display: flex; flex-wrap: wrap; gap: 1rem; margin-bottom: 1rem; font-size: 0.82rem; }
.portal-project-meta strong { display: block; font-size: 0.95rem; color: var(--secondary-black); }

.portal-bar-row { display: flex; align-items: center; gap: 0.85rem; margin-bottom: 0.85rem; font-size: 0.85rem; }
.portal-bar-row:last-child { margin-bottom: 0; }
.portal-bar-label { flex: 0 0 130px; color: var(--primary-gray); }
.portal-bar-track { flex: 1 1 auto; height: 10px; border-radius: 999px; background: var(--surface); overflow: hidden; }
.portal-bar-fill { height: 100%; border-radius: 999px; background: var(--primary-blue); }
.portal-bar-value { flex: 0 0 auto; font-weight: 700; font-size: 0.82rem; }

.portal-empty { text-align: center; color: var(--primary-gray); font-size: 0.88rem; padding: 2rem 0; }

@media (max-width: 900px) {
  .portal-stat-grid { grid-template-columns: repeat(2, 1fr); }
  .portal-layout { position: relative; }
  .portal-mobile-menu-btn { display: inline-flex; }
  .portal-sidebar {
    position: fixed;
    inset-inline-start: 0;
    top: 0;
    bottom: 0;
    width: 82vw;
    max-width: 300px;
    max-height: none;
    z-index: 300;
    border-radius: 0;
    /* Off-canvas toward the edge it's anchored to (inline-start = left in
       LTR, right in RTL), so the closed drawer is fully off-screen. */
    transform: translateX(-100%);
    transition: transform 0.25s ease;
  }
  [dir="rtl"] .portal-sidebar { transform: translateX(100%); }
  .portal-sidebar.is-open { transform: translateX(0); }
  .portal-sidebar-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.4);
    z-index: 290;
  }
  .portal-sidebar-backdrop.is-open { display: block; }
  .portal-sidebar-close { display: flex !important; }
}
@media (min-width: 901px) {
  .portal-sidebar-close { display: none; }
}
.portal-sidebar-close { align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background: var(--surface); margin-bottom: 0.5rem; }
@media (max-width: 640px) {
  .portal-stat-grid { grid-template-columns: 1fr 1fr; }
  .portal-row-detail-grid { grid-template-columns: 1fr 1fr; }
  .portal-bar-label { flex-basis: 90px; font-size: 0.78rem; }
}
.cart-row-stepper { order: 5; flex: 0 0 auto; }
.cart-row-stepper .qty-stepper { width: 126px; }
/* Read-only fallback for a cart entry whose category has the stepper
   switched off (STEPPER_DISABLED_CATEGORIES) — no +/- controls, just the
   existing quantity as text. */
.cart-row-qty-static { display: inline-flex; align-items: center; padding: 0.5rem 0.9rem; border-radius: 10px; background: var(--surface); border: 1.5px solid var(--border-light); color: var(--primary-gray); font-size: 0.82rem; font-weight: 600; white-space: nowrap; }
.cart-row-remove { order: 3; margin-inline-start: auto; flex: 0 0 auto; width: 34px; height: 34px; display: flex; align-items: center; justify-content: center; border-radius: 8px; color: var(--primary-gray); transition: background 0.15s, color 0.15s; }
.cart-row-remove:hover { background: rgba(229, 57, 53, 0.08); color: #E53935; }

/* Per-product note — button + floating popover, wrapped together in
   .cart-row-note (the position:relative anchor). Same treatment as
   .lang-switcher/.lang-dropdown elsewhere on the site: the panel is an
   absolutely positioned overlay that opens/closes via a class, never
   participates in row layout, so it can't push other rows down. */
.cart-row-note { order: 6; margin-inline-start: auto; position: relative; flex: 0 0 auto; }
/* position:relative + a fixed content shape (icon + constant-text label)
   means this button's own box width never varies row to row. The count
   badge is a position:absolute corner overlay (same technique as
   .cart-badge on the header cart icon) rather than a normal inline
   sibling — appending/removing it must NOT change the button's rendered
   width, or every row's .cart-row-stepper after it in flex order would
   shift out of alignment with rows that have no notes yet. */
.cart-row-note-btn {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.45rem 0.7rem;
  border-radius: 8px;
  border: 1px solid var(--border-light);
  background: var(--surface);
  color: var(--primary-gray);
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.cart-row-note-btn:hover { border-color: var(--primary-blue); color: var(--primary-blue); }
.cart-row-note-btn.has-notes { border-color: var(--primary-blue); background: rgba(65,95,170,0.06); color: var(--primary-blue); }
.cart-row-note.open .cart-row-note-btn { border-color: var(--primary-blue); background: rgba(65,95,170,0.08); color: var(--primary-blue); }
.cart-row-note-btn svg { width: 15px; height: 15px; flex-shrink: 0; }
.cart-row-note-count {
  position: absolute;
  top: -6px;
  inset-inline-end: -6px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--primary-blue);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 2px #fff;
}

/* Floating dropdown panel — closed by default (opacity/visibility, not
   [hidden], so it can animate); .cart-row-note.open reveals it. Anchored
   to the button's own end edge and grows toward the row's content, same
   corner-popover pattern as .lang-dropdown. */
.cart-row-note-panel {
  position: absolute;
  top: calc(100% + 8px);
  inset-inline-end: 0;
  width: 260px;
  max-width: calc(100vw - 2rem);
  max-height: 320px;
  overflow-y: auto;
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 4px 24px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.05);
  padding: 6px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px) scale(0.96);
  /* inset-inline-end:0 above anchors this panel to the end edge — left in
     rtl, right in ltr — so the scale-in origin (a physical property with
     no logical equivalent) must be set per direction to match. */
  transform-origin: top left;
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
  z-index: 20;
}
html[dir="ltr"] .cart-row-note-panel { transform-origin: top right; }
.cart-row-note.open .cart-row-note-panel { opacity: 1; visibility: visible; transform: translateY(0) scale(1); }
.cart-note-options { display: flex; flex-direction: column; gap: 1px; }
.cart-note-option {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.5rem 0.65rem;
  border-radius: 8px;
  cursor: pointer;
  font-size: 0.85rem;
  color: var(--secondary-black);
  transition: background 0.15s;
}
.cart-note-option:hover { background: var(--surface); }
.cart-note-option:has(input:checked) { background: rgba(65,95,170,0.08); color: var(--primary-blue); font-weight: 600; }
.cart-note-option input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--primary-blue); flex-shrink: 0; }
.cart-note-other-input { margin: 4px 6px 2px; width: calc(100% - 12px); }

@media (max-width: 480px) {
  .cart-rows { padding: 0.5rem 1rem; }
  /* The two-line split itself (thumb+name+remove / stepper+note) is now
     the unconditional .cart-row layout at every width — see the base
     rules above. Only genuine mobile-specific sizing/spacing lives here. */
  .cart-row { gap: 0.5rem; padding: 0.75rem 0; }
  .cart-row-thumb { width: 48px; height: 48px; }
  /* Hidden here for space — the narrower thumb+name+remove line on mobile
     doesn't have room for it too; it shows by default at wider widths
     now that line 1 isn't also sharing space with the stepper/note. */
  .cart-row-badge { display: none; }
  .cart-row-remove { width: 36px; height: 36px; }
  /* Hide the text label only — .cart-row-note-count (the badge) must stay
     visible; it's the one at-a-glance signal once the label is gone. */
  .cart-row-note-label { display: none; }
  .cart-row-note-btn { padding: 0.5rem; min-width: 36px; min-height: 36px; justify-content: center; }
  .cart-row-note-panel { inset-inline-end: -0.5rem; width: 240px; }
}

/* "Add more products" modal — built once by js/product-picker.js and
   appended to <body>, so it sits above everything regardless of which
   page opened it. Compact by design (not a full-screen takeover). */
.picker-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: rgba(34, 31, 31, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease, visibility 0.2s;
}
.picker-modal-overlay.open { opacity: 1; visibility: visible; }
.picker-modal {
  width: 100%;
  max-width: 640px;
  max-height: min(80vh, 720px);
  background: #fff;
  border-radius: 18px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.25);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transform: translateY(8px) scale(0.98);
  transition: transform 0.2s ease;
}
.picker-modal-overlay.open .picker-modal { transform: translateY(0) scale(1); }
.picker-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.1rem 1.35rem;
  border-bottom: 1px solid var(--border-light);
  flex-shrink: 0;
}
.picker-modal-header h3 { font-size: 1.1rem; }
.picker-modal-close {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--primary-gray);
  transition: background 0.15s;
}
.picker-modal-close:hover { background: var(--surface); }

/* Search + category chips — reuses .pm-search-box/.pm-pill/.pm-pills as-is
   (same classes as the Building Materials filter bar) so this matches that
   look exactly instead of inventing new controls. */
.picker-modal-toolbar { flex-shrink: 0; padding: 1rem 1.35rem 0; display: flex; flex-direction: column; gap: 0.85rem; }
.picker-modal-toolbar .pm-search-box { max-width: none; }
.picker-modal-toolbar .pm-pills { gap: 0.45rem; }
.picker-modal-toolbar .pm-pill { padding: 0.4rem 0.9rem; font-size: 0.8rem; }

/* flex:1 + min-height:0 is what makes THIS the one scrolling region — the
   header/toolbar/footer are all flex-shrink:0 and stay put; without
   min-height:0 a flex child with overflow content refuses to shrink
   below its natural size and the modal grows instead of scrolling. */
.picker-modal-body { flex: 1; min-height: 0; padding: 1rem 1.35rem 1.5rem; overflow-y: auto; }
.picker-modal-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 0.7rem; }

/* Thin custom scrollbar instead of the browser's native one — on Windows
   especially, the default non-overlay scrollbar renders visible up/down
   arrow buttons at each end of the track, which (mirrored to the left
   edge in this RTL modal) read as a stray/broken icon rather than an
   obviously-a-scrollbar control. This removes that chrome everywhere
   WebKit/Blink support it; scrollbar-width covers Firefox. */
.picker-modal-body {
  scrollbar-width: thin;
  scrollbar-color: var(--border-light) transparent;
}
.picker-modal-body::-webkit-scrollbar { width: 6px; }
.picker-modal-body::-webkit-scrollbar-track { background: transparent; }
.picker-modal-body::-webkit-scrollbar-thumb { background: var(--border-light); border-radius: 999px; }
.picker-modal-body::-webkit-scrollbar-thumb:hover { background: var(--primary-gray); }

/* Small quick-add cards inside the modal — same .pm-card look, scaled down. */
.picker-mini-card .pm-card-media { aspect-ratio: 1/1; }
.picker-mini-card .pm-card-body { padding: 0.65rem 0.6rem 0.75rem; }
.picker-mini-card .pm-card-body h3 {
  font-size: 0.76rem;
  margin-bottom: 0.55rem;
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: calc(1.3em * 2);
}
.picker-mini-card .qty-btn { width: 1.7rem; font-size: 0.85rem; }
.picker-mini-card .qty-input, .picker-mini-card .qty-input-custom { font-size: 0.75rem; padding: 0.3rem 0.15rem; }

@media (max-width: 480px) {
  .picker-modal-grid { grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); }
  /* Tighter overlay/panel padding so narrow phones (iPhone SE-class,
     ~320-375px) get real usable width instead of most of it eaten by
     padding — the modal box itself still gets a comfortable margin from
     the screen edge via the overlay's own reduced padding. */
  .picker-modal-overlay { padding: 0.75rem; }
  .picker-modal-header { padding: 0.9rem 1rem; }
  .picker-modal-toolbar { padding: 0.85rem 1rem 0; }
  .picker-modal-body { padding: 0.85rem 1rem 1.1rem; }
}

/* "لم تجد ما تبحث عنه؟" — part of the normal scrolling content inside
   .picker-modal-body (a sibling of #pickerModalGrid, NOT pinned/fixed),
   same behavior as the Building Materials page: it's simply the last
   thing in the list, only visible once the customer scrolls to the end.
   Reuses .pm-cta-box's exact markup/wording (heading, paragraph, button)
   at a smaller scale appropriate for sitting inside a compact modal
   rather than the full-page section it borrows from. */
.picker-modal-missing {
  margin-top: 1.25rem;
  border-width: 1.5px;
  border-radius: 14px;
  padding: 1.1rem 1.25rem;
}
.picker-modal-missing h2 { font-size: 1rem; margin-bottom: 0.3rem; }
.picker-modal-missing p { font-size: 0.82rem; margin-bottom: 0.9rem; }
.picker-modal-missing .btn { padding: 0.55rem 1.1rem; font-size: 0.85rem; }

.pm-bottom-cta { padding: 4rem 0 5rem; }
.pm-cta-box { border: 2px solid var(--border-light); border-radius: 20px; padding: 3rem 2rem; text-align: center; max-width: 640px; margin: 0 auto; }
.pm-cta-box h2 { font-size: 1.75rem; margin-bottom: 0.75rem; }
.pm-cta-box p { font-size: 1rem; color: var(--primary-gray); margin-bottom: 1.75rem; }

@media (max-width: 1024px) { .pm-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 768px) { .pm-grid { grid-template-columns: repeat(2, 1fr); } .pm-hero { padding: 6.5rem 0 2rem; } .pm-hero h1 { font-size: 1.85rem; } }
@media (max-width: 520px) { .pm-grid { grid-template-columns: 1fr; } .pm-filter-row { flex-direction: column; align-items: flex-start; } }

@media (max-width: 768px) {
  /* Put the WhatsApp/qty-stepper controls directly under the image instead
     of after the title/description, so they're reachable without scrolling
     past the copy first. .pm-card-body is already a flex column, so `order`
     alone moves it — no markup change needed. Scoped to the main grid only:
     .bm-related's related-products strips use a different layout (actions
     already pinned to the bottom via margin-top: auto) and must stay as-is. */
  .pm-grid:not(.bm-related) .pm-card-actions {
    order: -1;
    margin-bottom: 0.75rem;
  }
  /* Bigger +/- touch targets for thumbs. */
  .qty-btn { width: 2.75rem; }
}

/* Product-detail page main CTA row (WhatsApp order button, quantity
   stepper, "special item inquiry" ghost link). The two buttons share .btn's
   padding and render at the same height; the stepper's height comes from
   .qty-input's own (smaller) padding instead, so it renders visibly
   shorter at any width. .qty-stepper already stretches its own children
   (qty-btn/qty-input) to fill its box, so stretching the row itself makes
   the stepper match the buttons' height automatically. All 99 generated
   pages set align-items: center inline on this element, which normal
   specificity can't beat — !important is required here. */
.bm-main-cta { align-items: stretch !important; }

/* Desktop/tablet otherwise keep the inline flex-wrap layout as-is; only
   stack it below 768px, since wrapping three differently-sized inline-flex
   items at narrow widths squeezed and clipped them instead of wrapping
   cleanly. */
@media (max-width: 768px) {
  .bm-main-cta {
    flex-direction: column;
    align-items: stretch;
  }
  /* Quantity control first, same "primary action" ordering as the
     product-grid cards above. */
  .bm-main-cta .qty-stepper { order: -1; }
  /* align-items: stretch (above) only equalizes cross-axis size — which is
     height in the row layout above 768px, but becomes WIDTH once
     flex-direction switches to column here. Nothing then forces the
     button and stepper to the same height at this breakpoint, so the same
     icon-driven mismatch fixed on .bm-related can resurface here too
     (worse on en/zh, whose WhatsApp button here also carries .btn-lg on
     top of the icon). Pin both to the same explicit floor, same mechanism
     as .bm-related, sized generously enough for the larger .btn-lg case. */
  .bm-main-cta > .btn,
  .bm-main-cta .qty-stepper {
    width: 100%;
    min-height: 2.85rem;
  }
}

/* "Related products" grid on building-materials product-detail pages —
   reuses .pm-card/.pm-grid as-is (see build-materials.js's 'related' block),
   but this variant drops the description paragraph (badge+image+title+button
   only), so there's no .pm-card-body p left to flex:1 and absorb the extra
   space when one card's title is longer than another's. Scoped to
   .bm-related specifically so the catalog/hub pages using plain .pm-card
   (with a description) are completely unaffected. */
.bm-related .pm-card-body h3 {
  font-size: 0.85rem;
  line-height: 1.35;
  margin-bottom: 0;
  /* Clamp to 2 lines so a long product name can never grow the card taller
     than a short one — every card in the row stays the same height (grid's
     default align-items:stretch already equalizes the row; this just keeps
     the title from being the thing that makes one row taller than another). */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: calc(1.35em * 2);
}
/* No description paragraph here (see comment above) means there's no
   flex:1 element left in .pm-card-body to push the action-button row down —
   pin it to the bottom explicitly instead, same mechanism as .deal-card's
   own override further down. */
.bm-related .pm-card-actions { margin-top: auto; }
/* These narrow strip cards are too tight for the two buttons to sit
   side by side (labels were wrapping to 3 lines) — stack them instead,
   each spanning the card's full width, and shrink them so each label
   fits on one line in ar/en/zh. Scoped to .bm-related only — the wider
   catalog-page cards keep the side-by-side layout (see the base
   .pm-card-actions rule above). */
.bm-related .pm-card-actions { flex-direction: column; gap: 0.4rem; }
.bm-related .pm-card-actions .btn {
  padding: 0.42rem 0.5rem;
  font-size: 0.72rem;
  white-space: nowrap;
  /* Measured on staging: this button renders at ~34px tall, driven by its
     18px icon (.btn svg) plus padding — not by font-size — so shrinking
     the stepper's font-size/padding alone (below) can never reach the same
     height, since the stepper has no icon. An explicit min-height, matched
     on both elements, is the only thing that guarantees they agree
     regardless of what's driving either one's natural height. */
  min-height: 2.15rem;
}
/* The quantity stepper next to/below that shrunk button had no matching
   scale-down of its own, so it rendered at full default size — visibly
   heavier/bulkier than the compact WhatsApp button right next to it in the
   same narrow card. Scale it down proportionally so the pair reads as one
   consistent size. No separate mobile rule needed: this card is only ever
   this narrow (desktop and mobile alike), unlike the main catalog grid. */
.bm-related .qty-stepper .qty-btn {
  width: 1.7rem;
  font-size: 0.85rem;
}
.bm-related .qty-stepper .qty-input {
  font-size: 0.72rem;
  padding: 0.3rem 0.15rem;
}
/* Scale-down alone left the stepper's outer box shorter than the button's
   (see comment above) — match it explicitly. qty-stepper's own
   align-items: stretch (base rule) already makes qty-btn/qty-input fill
   whatever height the box ends up at, so they stay vertically centered
   rather than floating in extra space. */
.bm-related .qty-stepper {
  min-height: 2.15rem;
}
/* The category badge reads as oversized relative to these narrow cards —
   hide it here only; it's a simple absolutely-positioned overlay, so
   removing it doesn't disturb the image underneath. Catalog-page cards
   (plain .pm-card, no .bm-related) keep the badge. */
.bm-related .pm-badge { display: none; }
/* Mobile-only carousel shell — wraps a product grid (currently just
   .pm-grid.bm-related "related products" strips; the main catalog grid on
   /products/ deliberately stays a plain stacked grid on mobile, per product
   decision) plus two prev/next arrow buttons. On desktop this is a no-op:
   the grid inside stays display:grid and the arrows stay hidden. Only at
   the phone breakpoint below does the grid switch to a snapping horizontal
   scroller and the arrows appear. */
.carousel-shell { position: relative; }
.carousel-arrow {
  display: none;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  width: 34px;
  height: 34px;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: #fff;
  border: 1px solid var(--border-light);
  box-shadow: 0 2px 10px rgba(0,0,0,0.14);
  color: var(--primary-blue);
  transition: opacity 0.15s;
}
.carousel-arrow svg { width: 18px; height: 18px; }
.carousel-arrow-prev { inset-inline-start: 0.15rem; }
.carousel-arrow-next { inset-inline-end: 0.15rem; }
/* Icons are authored as literal left/right chevrons; mirroring them for RTL
   keeps each arrow pointing away from center after .carousel-arrow-prev/
   -next swap physical sides via the inset-inline-* logical properties above. */
[dir="rtl"] .carousel-arrow svg { transform: scaleX(-1); }
/* Toggled by js/main.js at the start/end of the scroll range — see the
   .carousel-shell wiring block for updateCarouselUI(). */
.carousel-arrow.is-disabled { opacity: 0.35; pointer-events: none; }

/* Small pagination dots row, injected above .carousel-track by js/main.js
   for every .carousel-shell (mobile only — see updateCarouselUI()/
   buildDots()). Not tied to .pm-grid/.fp-grid specifically, since it's a
   sibling of whichever grid the shell wraps, so one rule covers every
   carousel on the site. justify-content:flex-start aligns to the reading
   start (right in Arabic RTL, left in en/zh) without a separate RTL
   override, same trick .carousel-arrow's inset-inline-* positions use. */
.carousel-dots { display: none; }
.carousel-dot {
  width: 6px;
  height: 6px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--border-light);
  flex-shrink: 0;
  transition: background 0.15s, transform 0.15s;
}
.carousel-dot.active { background: var(--primary-blue); transform: scale(1.4); }

@media (max-width: 640px) {
  .carousel-arrow { display: flex; }
  .carousel-dots {
    display: flex;
    justify-content: flex-start;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.6rem;
  }
  .pm-grid.bm-related {
    display: flex;
    gap: 0.6rem;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .pm-grid.bm-related::-webkit-scrollbar { display: none; }
  .pm-grid.bm-related .pm-card {
    /* Slightly narrower than the full track so the next card's edge peeks
       into view, hinting there's more to scroll to. */
    flex: 0 0 88%;
    scroll-snap-align: start;
  }
}

/* Specifications box — themed card variant for the "المواصفات" block on
   building-materials product-detail pages (see build-materials.js's
   'specsbox' type): white card, blue title, gray label / bold blue value
   per row with a thin underline, generous spacing. New/scoped class (not a
   'table' override), so pages still using the plain 'table' type are
   unaffected. */
.specs-box {
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: 12px;
  padding: 1.5rem 1.75rem 0.25rem;
  margin: 0 0 2rem;
}
.specs-box-title {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--primary-blue);
  margin: 0 0 0.5rem;
}
/* Rows used to be independent flex containers, so each row's own label
   text set that row's own column split (justify-content:space-between with
   auto-width flex items) — a longer label in one row shifted its split
   point left of shorter labels in sibling rows, producing jagged, unaligned
   columns down the box. Fixed by making the grid itself own a single
   label/value track pair and having every .specs-box-row subgrid into
   those SAME parent tracks, so the label column width is computed once and
   shared by every row — not recomputed per row. fit-content(45%), not a
   bare max-content, caps how much a single unusually long label (e.g. the
   EN "Request a Sample Before Purchase") can claim: past the cap it wraps
   to a second line instead of crushing the value column down to one word
   per line, while short labels still hug their own natural width exactly
   as max-content would size them. */
.specs-box-grid {
  display: grid;
  grid-template-columns: fit-content(45%) 1fr;
  column-gap: 1rem;
}
/* Auto-applied by the generator once a specs box has 4+ rows (or via the
   b.twoCol / data.twoCol override) — two side-by-side label/value pairs per
   visual row, each pair subgridding its own 2 of these 4 tracks so labels
   stay aligned within a pair-column without being forced to match the
   other pair-column's (unrelated) label width. */
/* fit-content(45%) here (matching the single-column rule above) would let
   EACH of the two label tracks independently grow up to 45% of the whole
   box — together up to 90%, leaving almost nothing for both value tracks
   combined. A fixed cap keeps that in check regardless of box width. */
.specs-box-grid.is-two-col { grid-template-columns: fit-content(160px) 1fr fit-content(160px) 1fr; column-gap: 2rem; }
.specs-box-grid.is-two-col .specs-box-row { column-gap: 1rem; }
.specs-box-row {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / 3;
  align-items: baseline;
  padding: 1rem 0;
  border-bottom: 1px solid var(--border-light);
}
.specs-box-grid.is-two-col .specs-box-row:nth-child(even) { grid-column: 3 / 5; }
.specs-box-grid.is-two-col .specs-box-row:nth-last-child(-n+2) { border-bottom: none; }
.specs-box-grid:not(.is-two-col) .specs-box-row:last-child { border-bottom: none; }
.specs-box-label { color: var(--primary-gray); font-size: 0.88rem; text-align: left; }
.specs-box-value { color: var(--primary-blue); font-weight: 400; font-size: 0.8rem; text-align: left; }
.specs-box-note { color: var(--primary-gray); font-size: 0.85rem; line-height: 1.6; margin: 0.75rem 0 1rem; text-align: left; }
/* RTL override. NOTE: a previous version of this rule paired the above with
   a `:not([dir="rtl"]) .specs-box-x { text-align: left }` sibling instead of
   setting left as the base value here. That was the actual bug behind every
   earlier "still not aligned" report: :not([dir="rtl"]) is a descendant
   combinator, so it matches any element with *some* ancestor lacking the
   literal dir="rtl" attribute — which is every element except <html> itself
   (body/div/etc. never carry that attribute directly, they just inherit the
   direction). So on RTL pages BOTH rules matched .specs-box-note at equal
   specificity, and the :not() rule, being declared second, won the cascade
   and silently forced text-align:left even under dir="rtl". Confirmed via
   getComputedStyle() on the live page: textAlign was "left" despite
   direction being "rtl". Fixed by making left the plain base value (lowest
   specificity) and right a higher-specificity override scoped to [dir="rtl"]
   with nothing competing at the same specificity — right for Arabic, left
   for English/Chinese, regardless of which side of the row's flex layout a
   given column happens to sit on. */
[dir="rtl"] .specs-box-label,
[dir="rtl"] .specs-box-value,
[dir="rtl"] .specs-box-note { text-align: right; }
@media (max-width: 560px) {
  .specs-box-grid.is-two-col { grid-template-columns: fit-content(45%) 1fr; column-gap: 1rem; }
  /* Collapsed to a single label/value pair per row here (matches the base
     .specs-box-grid rule), so the 45% single-track cap is correct again —
     it's only the still-side-by-side desktop is-two-col above that needs
     the tighter fixed cap. */
  /* :nth-child(even) is repeated here (not just the plain .specs-box-row
     reset above) because the desktop rule that assigns even rows to the
     right-hand pair (grid-column: 3 / 5) has higher specificity than a
     plain .specs-box-row selector — being inside @media isn't enough to
     win the cascade, it has to match or exceed that specificity too. */
  .specs-box-grid.is-two-col .specs-box-row,
  .specs-box-grid.is-two-col .specs-box-row:nth-child(even) {
    grid-column: 1 / 3;
    border-bottom: 1px solid var(--border-light);
  }
  .specs-box-grid.is-two-col .specs-box-row:last-child { border-bottom: none; }
}

/* ── Blog index (cards, filters, pagination) — reuses .pm-hero / .pm-search-box /
   .pm-pills for the hero, search box, and category pills so the blog stays on
   the exact same blue theme as the Products Catalog / Latest Deals pages. ── */
.blog-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; padding-top: 0.5rem; }
.blog-card { background: #fff; border: 1px solid var(--border-light); border-radius: 16px; overflow: hidden; display: flex; flex-direction: column; text-decoration: none; color: inherit; transition: box-shadow 0.2s, transform 0.2s; }
.blog-card[hidden] { display: none; }
.blog-card:hover { box-shadow: 0 8px 24px rgba(0,0,0,0.08); transform: translateY(-2px); }
.blog-card-img { width: 100%; aspect-ratio: 16/10; object-fit: cover; background: rgba(65,95,170,0.07); display: block; }
.blog-card-img-empty { background: rgba(65,95,170,0.07); }
.blog-card-body { padding: 1.25rem; display: flex; flex-direction: column; flex: 1; }
.blog-card-cat { align-self: flex-start; background: rgba(65,95,170,0.1); color: var(--primary-blue); font-size: 0.72rem; font-weight: 700; padding: 0.3rem 0.7rem; border-radius: 999px; margin-bottom: 0.75rem; }
.blog-card-title { font-size: 1.1rem; font-weight: 700; margin-bottom: 0.5rem; line-height: 1.4; color: var(--secondary-black); }
.blog-card-excerpt { font-size: 0.9rem; color: var(--primary-gray); line-height: 1.6; margin-bottom: 0.75rem; flex: 1; }
.blog-card-meta { font-size: 0.78rem; color: var(--primary-gray); opacity: 0.75; margin-bottom: 0.85rem; }
.blog-card-more { font-size: 0.85rem; font-weight: 700; color: var(--primary-blue); }

.blog-empty { text-align: center; color: var(--primary-gray); padding: 4rem 1rem; font-size: 0.95rem; }
.blog-empty[hidden] { display: none; }

.blog-pagination { display: flex; justify-content: center; align-items: center; gap: 0.5rem; flex-wrap: wrap; padding: 3rem 0 1rem; }
.blog-page-link { min-width: 2.4rem; height: 2.4rem; padding: 0 0.75rem; display: inline-flex; align-items: center; justify-content: center; border: 1.5px solid var(--border-light); border-radius: 10px; color: var(--secondary-black); font-size: 0.9rem; font-weight: 600; text-decoration: none; transition: all 0.2s; }
a.blog-page-link:hover { border-color: var(--primary-blue); color: var(--primary-blue); }
.blog-page-link.active { background: var(--primary-blue); border-color: var(--primary-blue); color: #fff; }
.blog-page-link.disabled { opacity: 0.4; pointer-events: none; }

@media (max-width: 1024px) { .blog-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .blog-grid { grid-template-columns: 1fr; } }

/* ═══════════════════════════════════════════════════════════
   Promo Popup — marketing lead capture (see js/main.js initPromoPopup)
   Design: dark-green Saudi-heritage themed card (National Day campaign).
   The top banner (heritage pattern + glow curve, heading baked in for ar)
   comes from images/promo-bg-heading.jpg / promo-bg-plain.jpg — NOT css
   text — so the heading always renders in the exact source typeface.
   Every other text element uses the Neo Sans Arabic web font below.
   ═══════════════════════════════════════════════════════════ */
@font-face {
  font-family: 'Neo Sans Arabic';
  src: url('../fonts/NeoSansArabic-Regular.ttf') format('truetype');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Neo Sans Arabic';
  src: url('../fonts/NeoSansArabic-Medium.ttf') format('truetype');
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Neo Sans Arabic';
  src: url('../fonts/NeoSansArabic-Bold.ttf') format('truetype');
  font-weight: 700; font-style: normal; font-display: swap;
}
.promo-overlay {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; align-items: center; justify-content: center;
  background: rgba(4, 12, 8, 0.65);
  padding: 1rem; animation: promoFade 0.2s ease;
  font-family: 'Neo Sans Arabic', 'IBM Plex Sans Arabic', 'Inter', system-ui, sans-serif;
}
@keyframes promoFade { from { opacity: 0; } to { opacity: 1; } }
.promo-modal {
  width: 100%; max-width: 420px;
  background-color: #03130d;
  background-image: url('../images/promo-bg-plain.jpg');
  background-size: 100% auto;
  background-position: top center;
  background-repeat: no-repeat;
  border-radius: 20px; overflow: hidden;
  box-shadow: 0 24px 60px -12px rgba(0,0,0,0.5);
  animation: promoPop 0.25s cubic-bezier(0.16,1,0.3,1);
}
/* ar signup state only: swaps in the banner with "عزّنا بجودنا" baked in */
.promo-modal--heading { background-image: url('../images/promo-bg-heading.jpg'); }
@keyframes promoPop { from { transform: translateY(12px) scale(0.98); opacity: 0; } to { transform: none; opacity: 1; } }
.promo-header {
  position: relative; padding: 30% 1.5rem 0.5rem; text-align: center; color: #fff;
}
.promo-badge { font-size: 1.2rem; font-weight: 700; line-height: 1.4; }
.promo-amount { font-size: 1rem; font-weight: 500; margin-top: 0.35rem; color: rgba(255,255,255,0.95); }
/* Currency amounts (e.g. "96 ريال") auto-highlighted within badge/amount/
   fine-print/success copy — see elWithAmounts() in js/main.js. */
.promo-amount-highlight { color: #9ed67a; font-weight: 700; }
/* Success state: hide the leftover offer badge/amount, show the logo instead */
.promo-header--success .promo-badge,
.promo-header--success .promo-amount { display: none; }
.promo-header-logo { height: 106px; width: auto; display: inline-block; }
.promo-close {
  position: absolute; top: 0.75rem; inset-inline-end: 0.75rem;
  width: 34px; height: 34px; border: none; border-radius: 50%;
  background: rgba(255,255,255,0.12); color: #fff; font-size: 1rem; cursor: pointer;
  display: flex; align-items: center; justify-content: center; line-height: 1;
}
.promo-close:hover { background: rgba(255,255,255,0.22); }
.promo-body { position: relative; padding: 1.5rem; display: flex; flex-direction: column; gap: 0.85rem; }
.promo-text { margin: 0; color: rgba(255,255,255,0.75); font-size: 0.8rem; font-weight: 400; line-height: 1.6; text-align: center; }
.promo-input {
  width: 100%; background: #204917; border: 1.5px solid #6fae45; color: #fff;
  border-radius: 999px; font-weight: 700; font-family: inherit;
}
.promo-input::placeholder { color: #fff; opacity: 0.9; }
.promo-input:focus { border-color: #9ed67a; box-shadow: 0 0 0 3px rgba(111,174,69,0.25); }
/* Force both fields to match the popup's own direction — browsers silently
   force type=tel/email/number inputs to LTR by default, which otherwise
   makes the phone field misalign against the (correctly RTL) name field. */
.promo-overlay[dir="rtl"] .promo-input { direction: rtl; text-align: right; }
.promo-overlay[dir="ltr"] .promo-input { direction: ltr; text-align: left; }
.promo-consent { display: flex; align-items: flex-start; gap: 0.5rem; font-size: 0.85rem; color: #fff; cursor: pointer; line-height: 1.5; }
.promo-consent input { accent-color: #6fae45; width: 16px; height: 16px; flex: 0 0 auto; margin-top: 2px; }
.promo-link { color: #fff; text-decoration: underline; font-weight: 700; }
.promo-link:hover { color: #9ed67a; }
.promo-error { margin: 0; color: #ff8a80; font-size: 0.85rem; text-align: center; }
.promo-captcha { display: flex; justify-content: center; }
.promo-submit {
  width: 100%; background: #5b9446; color: #fff; font-weight: 700; font-size: 1.05rem;
  padding: 0.9rem 1rem; border: none; border-radius: 999px; cursor: pointer;
  font-family: inherit;
}
.promo-submit:hover { background: #4c8339; }
.promo-submit:disabled { opacity: 0.6; cursor: not-allowed; }
.promo-success { display: flex; flex-direction: column; align-items: center; gap: 0.85rem; text-align: center; padding: 0.5rem 0 0.25rem; }
.promo-success-heading { margin: 0; color: #fff; font-weight: 700; font-size: 1.9rem; line-height: 1.25; }
.promo-success-text { margin: 0; color: #fff; font-size: 0.95rem; font-weight: 400; line-height: 1.7; text-align: center; }

/* Persistent tab shown after one dismissal, instead of the full popup —
   opposite corner from .contact-widget so the two never overlap. */
.promo-tab {
  position: fixed; bottom: 1.5rem; left: 1.5rem; z-index: 95;
  display: inline-flex; align-items: center; gap: 0.5rem;
  background: #04140c; color: #fff; border: 1.5px solid #6fae45;
  border-radius: 999px; padding: 0.6rem 1rem; cursor: pointer;
  font-family: 'Neo Sans Arabic', 'IBM Plex Sans Arabic', 'Inter', system-ui, sans-serif;
  font-size: 0.85rem; font-weight: 700;
  box-shadow: 0 8px 24px -6px rgba(0,0,0,0.45);
  animation: promoTabIn 0.25s ease;
}
[dir="rtl"] .promo-tab { left: auto; right: 1.5rem; }
.promo-tab:hover { background: #0a2417; border-color: #9ed67a; }
@keyframes promoTabIn { from { transform: translateY(10px); opacity: 0; } to { transform: none; opacity: 1; } }
.promo-tab-dot { width: 8px; height: 8px; border-radius: 50%; background: #9ed67a; flex: 0 0 auto; }
.promo-tab-label { max-width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

@media (max-width: 480px) {
  .promo-success-heading { font-size: 1.5rem; }
  .promo-header-logo { height: 86px; }
  .promo-header { padding: 32% 1.25rem 0.5rem; }
}

/* ── AR product-detail pages: hero (image + purchase card) ─────────────
   Only on AR product pages listed in build-materials.js's
   PRODUCT_LAYOUT_PATHS (class="bm-product-page" on <main>). Container,
   specs box and section headings keep the original page styles. Image and
   purchase card are two equal-height boxes; the card reuses the
   .specs-box look (white, 1px border, 12px radius, no shadow). */
.bm-product-page .page-hero { padding-bottom: 0.75rem !important; }
.bm-product-page .page-hero nav { margin-bottom: 0 !important; }

.bm-product-page .bm-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.25rem;
  margin-bottom: 2rem;
}
.bm-product-page .bm-hero-media {
  aspect-ratio: 1 / 1;
  width: 100%;
  border-radius: 12px;
  overflow: hidden;
}
.bm-product-page .bm-hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.04);
  display: block;
}

.bm-product-page .bm-buy-card {
  height: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: 12px;
  padding: 1rem 1.5rem;
}
.bm-product-page .bm-buy-title {
  font-size: 1.35rem;
  line-height: 1.4;
  font-weight: 700;
  color: var(--secondary-black);
  margin: 0 0 0.25rem;
}
.bm-product-page .bm-buy-lead {
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--primary-gray);
  margin: 0 0 0.75rem;
}

/* Price band */
.bm-product-page .bm-price-box {
  margin: 0 -1.5rem 0.75rem;
  padding: 0.55rem 1.5rem;
  background: rgba(65, 95, 170, 0.04);
  border-top: 1px solid var(--border-light);
  border-bottom: 1px solid var(--border-light);
}
.bm-product-page .bm-price-caption {
  display: block;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--primary-gray);
  margin-bottom: 0.1rem;
}
.bm-product-page .bm-buy-panel .bm-purchase-price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 0.4rem;
  row-gap: 0.2rem;
  margin: 0;
  font-size: 1rem;
}
.bm-product-page .bm-buy-panel .ref-price {
  display: contents;
  font-size: 1.55rem;
  font-weight: 800;
  line-height: 1.2;
  color: var(--secondary-black);
}
.bm-product-page .bm-buy-panel .ref-price .riyal-icon { width: 0.8em; height: 0.8em; align-self: center; }
.bm-product-page .bm-buy-panel .ref-price-unit,
.bm-product-page .bm-buy-panel .bm-price-per {
  order: 1;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--primary-gray);
}
.bm-product-page .bm-buy-panel .ref-price-approx {
  order: 2;
  align-self: center;
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--primary-blue);
  background: rgba(65, 95, 170, 0.08);
  border: 1px solid rgba(65, 95, 170, 0.18);
  border-radius: 999px;
  padding: 0.05rem 0.5rem;
}
.bm-product-page .bm-buy-panel .ref-price-incl {
  order: 3;
  flex-basis: 100%;
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--primary-gray);
}
.bm-product-page .bm-buy-panel .ref-price-quote .ref-price-approx {
  order: 0;
  font-size: 0.95rem;
  font-weight: 700;
  background: #fff;
  border: 1px dashed rgba(65, 95, 170, 0.4);
  border-radius: 10px;
  padding: 0.45rem 0.8rem;
}

/* Unit select + qty stepper on one row */
.bm-product-page .bm-buy-controls { display: flex; gap: 0.6rem; margin-bottom: 0.75rem; }
.bm-product-page .bm-buy-field { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; flex: 1 1 45%; }
.bm-product-page .bm-buy-field-unit { flex-basis: 55%; }
.bm-product-page .bm-buy-label { font-size: 0.72rem; font-weight: 600; color: var(--primary-gray); }
.bm-product-page .bm-buy-panel .pm-unit-select {
  height: 40px;
  margin: 0;
  padding: 0 0.75rem;
  border: 1px solid var(--border-light);
  border-radius: 10px;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}
.bm-product-page .bm-buy-panel .pm-unit-select:hover,
.bm-product-page .bm-buy-panel .pm-unit-select:focus { border-color: var(--primary-blue); }
.bm-product-page .bm-buy-panel .qty-stepper { flex: none; height: 40px; border-radius: 10px; }

/* Buttons — regular site .btn size, full width */
.bm-product-page .bm-buy-wa,
.bm-product-page .bm-buy-inquiry { display: flex; width: 100%; padding: 0.55rem 1rem; font-size: 0.9rem; border-radius: 10px; }
.bm-product-page .bm-buy-wa svg { width: 18px; height: 18px; }
.bm-product-page .bm-buy-inquiry { margin-top: 0.4rem; }

/* Sticky purchase bar — mobile/tablet only */
.bm-sticky-bar { display: none; }

@media (min-width: 768px) {
  .bm-product-page .bm-hero { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1.25rem; align-items: stretch; }
}

@media (max-width: 991.98px) {
  body:has(.bm-sticky-bar) { padding-bottom: 68px; }
  .bm-sticky-bar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: 95;
    padding: 0.55rem 1rem calc(0.55rem + env(safe-area-inset-bottom));
    background: rgba(255, 255, 255, 0.97);
    border-top: 1px solid var(--border-light);
    box-shadow: 0 -6px 18px -12px rgba(34, 31, 31, 0.25);
    transform: translateY(110%);
    visibility: hidden;
    transition: transform 0.25s ease, visibility 0.25s;
  }
  .bm-sticky-bar.is-visible { transform: none; visibility: visible; }
  .bm-sticky-info { flex: 1; min-width: 0; display: flex; flex-direction: column; }
  .bm-sticky-name { font-size: 0.72rem; font-weight: 600; color: var(--primary-gray); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .bm-sticky-price { display: flex; align-items: baseline; column-gap: 0.3rem; min-width: 0; white-space: nowrap; overflow: hidden; }
  .bm-sticky-price .ref-price { display: contents; font-size: 1.1rem; font-weight: 800; color: var(--secondary-black); }
  .bm-sticky-price .ref-price .riyal-icon { width: 0.8em; height: 0.8em; align-self: center; }
  .bm-sticky-price .ref-price-incl,
  .bm-sticky-price .ref-price-approx { display: none; }
  .bm-sticky-price .ref-price-unit,
  .bm-sticky-price .bm-price-per { font-size: 0.72rem; font-weight: 500; color: var(--primary-gray); }
  .bm-sticky-price .ref-price-quote .ref-price-approx { display: inline; font-size: 0.85rem; color: var(--primary-blue); }
  .bm-sticky-wa { flex: none; padding: 0.55rem 1rem; font-size: 0.85rem; border-radius: 10px; }
  .bm-sticky-wa svg { width: 18px; height: 18px; }
  .contact-widget, .promo-tab { transition: bottom 0.25s ease; }
  body.bm-sticky-on .contact-widget,
  body.bm-sticky-on .promo-tab { bottom: 5rem; }
}

/* ── "Added to cart" toast (js/cart.js showCartToast) ──────────────────
   One reused element, fixed bottom-LEFT in every language (physical left,
   deliberately not inset-inline-start). Never intercepts clicks/scroll.
   z-index 2100: above .picker-modal-overlay (2000), below .promo-overlay
   (9999). Bottom offsets clear the other fixed bottom-left/bottom items:
   desktop — .contact-widget (56px circle at bottom 1.5rem → top edge at
   5rem) → 6.5rem. Below 992px — same 56px widget at 1.5rem down to 481px
   (48px at 1rem below that, both ≤5rem tall), and once the product-page
   sticky bar shows (body.bm-sticky-on) the widget / .promo-tab are lifted
   to bottom 5rem, so the widget's top edge reaches 5rem + 56px = 8.5rem
   (the ~68px .bm-sticky-bar itself only reaches ~4.25rem) → 9.25rem. */
.cart-toast {
  position: fixed;
  left: 1.5rem;
  bottom: 6.5rem;
  z-index: 2100;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  max-width: min(340px, calc(100vw - 2rem));
  padding: 0.6rem 0.9rem;
  border-radius: 8px;
  background: #dcfce7;
  color: #166534;
  border: 1px solid #86efac;
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.4;
  box-shadow: 0 6px 18px -8px rgba(22, 101, 52, 0.25);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s, bottom 0.25s ease;
}
.cart-toast.is-visible { opacity: 1; visibility: visible; transform: none; }
.cart-toast-icon { width: 16px; height: 16px; flex: none; color: #16a34a; }
.cart-toast-text { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 991.98px) {
  .cart-toast { left: 1rem; bottom: calc(6.5rem + env(safe-area-inset-bottom)); }
  body.bm-sticky-on .cart-toast { bottom: calc(9.25rem + env(safe-area-inset-bottom)); }
}
@media (prefers-reduced-motion: reduce) {
  .cart-toast, .cart-toast.is-visible { transform: none; transition: opacity 0.2s ease, visibility 0.2s; }
}

/* ── Copied verbatim from the website's css/style.css (2026-10-19, T-150 picker parity):
   the stepper-only action row, the gold unit select and the unit pill radios the
   «إضافة منتجات» modal uses. Do not edit here — re-copy from the website. ── */
/* Catalog page cards (build-products-catalog.js renderCard): the stepper is
   the only action — it takes the whole row, at the height the row had when
   it shared it with the one-line WhatsApp button. */
.pm-card-actions--stepper-only { justify-content: center; }
.pm-card-actions--stepper-only .qty-stepper { flex: 1 1 100%; min-height: 2.25rem; }
/* Order page only (cart list + "add products" modal): unit selects in the
   brand dark yellow, and the cement bag/trailer pill radios (see
   js/product-picker.js's cementUnitRadiosHtml). */
#cartItemsList .pm-unit-select,
.picker-modal .pm-unit-select {
  background-color: var(--secondary-gold);
  color: var(--secondary-black);
  border-color: #D98E1F;
  font-weight: 600;
}
#cartItemsList .pm-unit-select:hover,
.picker-modal .pm-unit-select:hover { border-color: #B87410; }
#cartItemsList .pm-unit-select:focus-visible,
.picker-modal .pm-unit-select:focus-visible { border-color: #B87410; outline: 2px solid var(--secondary-black); outline-offset: 1px; }
#cartItemsList .pm-unit-select:disabled,
.picker-modal .pm-unit-select:disabled { background-color: #FBE5C2; color: #6B5B40; border-color: #EBC283; }
#cartItemsList .pm-unit-select option,
.picker-modal .pm-unit-select option { background: #fff; color: var(--secondary-black); font-weight: 500; }
.pm-unit-radios { position: relative; z-index: 2; min-width: 0; margin: 0 0 0.5rem; padding: 0; border: 0; }
.pm-unit-radios-label { padding: 0; margin-bottom: 0.3rem; font-size: 0.68rem; font-weight: 600; color: var(--primary-gray); }
.pm-unit-radios-opts { display: flex; flex-wrap: wrap; gap: 0.3rem; }
/* Equal-width pills on one row; a label never wraps or clips — if the card
   is too narrow for both, the second pill moves to its own row. */
.pm-unit-radio { position: relative; flex: 1 1 0; min-width: min(max-content, 100%); }
.pm-unit-radio input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.pm-unit-radio span { display: block; padding: 0.3rem 0.45rem; border: 1px solid #D98E1F; border-radius: 999px; background: #fff; color: var(--secondary-black); font-size: 0.72rem; font-weight: 600; line-height: 1.3; text-align: center; white-space: nowrap; }
.pm-unit-radio input:checked + span { background: var(--secondary-gold); border-color: #B87410; }
.pm-unit-radio input:focus-visible + span { outline: 2px solid var(--secondary-black); outline-offset: 2px; }
/* ── end of the copied website block ── */

/* ── Portal Phase 1 (2026-10-13): home actions, quote-request picker,
   projects (delivery sites), company records ─────────────────────── */
/* Four primary actions on the home page — larger than the generic quick-action tiles. */
.portal-quick-actions-lg { margin-bottom: 1.75rem; }
.portal-quick-actions-lg .portal-quick-action { flex-direction: column; justify-content: center; text-align: center; gap: 0.6rem; padding: 1.35rem 1rem; font-size: 1rem; }
.portal-quick-actions-lg .portal-quick-action-icon { width: 52px; height: 52px; font-size: 1.6rem; border-radius: 14px; }

/* Quote-request modal: wider than a plain form, scrolls as one body; the
   product grid inside has its own bounded scroll so the project block
   stays reachable. */
.picker-modal.quote-modal { max-width: 820px; max-height: 94vh; }
.quote-step { padding: 1rem 0 0.25rem; border-top: 1px solid var(--border-light); margin-top: 0.75rem; }
.quote-step:first-of-type { border-top: 0; margin-top: 0; padding-top: 0; }
.quote-step-head { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.85rem; flex-wrap: wrap; }
.quote-step-head h4 { margin: 0; font-size: 0.98rem; }
.quote-step-head .pm-count { margin-inline-start: auto; }
.quote-step-num { width: 26px; height: 26px; border-radius: 50%; background: var(--primary-blue); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 0.8rem; font-weight: 800; flex-shrink: 0; }
.picker-inline-toolbar { padding: 0; margin-bottom: 0.85rem; }
.picker-inline-grid { max-height: 340px; overflow-y: auto; padding: 2px 2px 6px; scrollbar-width: thin; scrollbar-color: var(--border-light) transparent; }
.picker-mini-card.is-chosen { border-color: var(--primary-blue); box-shadow: 0 0 0 2px rgba(65,95,170,0.12); }
.picker-mini-meta { display: block; font-size: 0.72rem; color: var(--primary-gray); margin: 0 0 0.55rem; line-height: 1.4; }
/* The modal is wider than the website's (820px): the same minmax(120px) grid would pack
   seven cards a row; 150px keeps a card wide enough for the family select + unit pills. */
.picker-inline-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.picker-inline-grid .picker-custom-card .pm-card-media { aspect-ratio: 2/1; }
.picker-chosen { margin-top: 1rem; padding: 0.75rem 0.9rem; border: 1px solid rgba(65,95,170,0.22); background: rgba(65,95,170,0.04); border-radius: 12px; }
.picker-line { display: flex; align-items: center; gap: 0.75rem; padding: 0.45rem 0; border-bottom: 1px solid var(--border-light); }
.picker-line:last-child { border-bottom: 0; }
.picker-line-name { flex: 1 1 auto; min-width: 0; font-size: 0.88rem; }
.picker-line-name .picker-mini-meta { margin: 0.1rem 0 0; }
.picker-line .qty-stepper { flex: 0 0 126px; }
.picker-line-remove { width: 32px; height: 32px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; color: var(--primary-gray); font-size: 1.3rem; line-height: 1; }
.picker-line-remove:hover { background: rgba(229,57,53,0.08); color: #c62828; }

/* Radio row (existing project / new site) */
.portal-radio-row { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 0.9rem; }
.portal-radio { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.55rem 1rem; border: 1.5px solid var(--border-light); border-radius: 999px; font-size: 0.88rem; font-weight: 600; cursor: pointer; transition: border-color 0.15s, background 0.15s; }
.portal-radio:has(input:checked) { border-color: var(--primary-blue); background: rgba(65,95,170,0.08); color: var(--primary-blue); }
.portal-radio input { accent-color: var(--primary-blue); margin: 0; }
.portal-radio.is-disabled { opacity: 0.55; cursor: default; }

/* Projects (delivery sites) */
.portal-project-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1rem; margin-bottom: 1.5rem; }
.portal-project-card { display: flex; flex-direction: column; }
.portal-project-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.6rem; margin-bottom: 0.75rem; }
.portal-project-head h2 { font-size: 1.05rem; margin: 0; }
.portal-project-meta { margin: 0 0 0.85rem; display: grid; gap: 0.5rem; }
.portal-project-meta div { display: flex; gap: 0.5rem; font-size: 0.86rem; }
.portal-project-meta dt { color: var(--primary-gray); flex: 0 0 5.5em; }
.portal-project-meta dd { margin: 0; font-weight: 600; min-width: 0; }
.portal-project-meta-inline { padding: 0.7rem 0.9rem; border: 1px solid rgba(65,95,170,0.22); background: rgba(65,95,170,0.04); border-radius: 12px; margin-bottom: 1rem; }
.portal-project-card .portal-actions { margin-top: auto; }

/* Read-only account number chip on «بياناتي» */
.portal-account-no { display: inline-flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0.9rem; border-radius: 999px; background: rgba(65,95,170,0.08); color: var(--primary-blue); font-size: 0.85rem; }
.portal-account-no strong { font-size: 1rem; letter-spacing: 0.03em; }

@media (max-width: 640px) {
  .portal-stat-grid-3 { grid-template-columns: 1fr; }
  .portal-quick-actions-lg .portal-quick-action { padding: 1rem 0.75rem; font-size: 0.9rem; }
  .picker-inline-grid { max-height: 300px; }
  .picker-line { flex-wrap: wrap; }
  .picker-line .qty-stepper { flex-basis: 110px; }
}

/* ── Account mode (T-164 / T-165 / T-167, 2026-10-20) ───────────────────
   Inside the account the header carries the account nav only; the blue
   hero band is replaced by a slim top bar on desktop and nothing on phones
   (the section titles do the job there). The menu button in the header
   opens the off-canvas sidebar (window event `portal-menu`). */
.portal-header-nav a.is-current { color: var(--primary-blue); font-weight: 700; }
.portal-topbar { background: var(--primary-blue); color: #fff; padding: calc(var(--header-height) + 0.9rem) 0 0.9rem; }
.portal-topbar-inner { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.portal-topbar .pm-breadcrumb { margin: 0; }
.portal-topbar-greeting { margin: 0; font-size: 0.95rem; opacity: 0.95; }
.portal-topbar-account { opacity: 0.85; font-size: 0.85rem; }
.site-header .portal-menu-toggle { display: none; }
.portal-nav-item.portal-nav-external { color: var(--primary-gray); text-decoration: none; }
.portal-quick-actions-3 { grid-template-columns: repeat(3, 1fr); }
.portal-credit-line { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; margin-top: 1.25rem; padding: 0.75rem 1rem; border: 1px dashed var(--border-light); border-radius: 12px; font-size: 0.9rem; color: var(--primary-gray); }
.portal-credit-status { margin-top: 0.9rem; }
@media (max-width: 900px) {
  .site-header .portal-menu-toggle { display: inline-flex; }
  .portal-topbar { display: none; }
  .portal-shell { padding-top: calc(var(--header-height) + 1rem); }
  .portal-quick-actions-3 { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 480px) {
  .portal-quick-actions-3 .portal-quick-action { padding: 0.85rem 0.5rem; font-size: 0.82rem; }
  .portal-quick-actions-3 .portal-quick-action-icon { width: 40px; height: 40px; font-size: 1.25rem; }
}

/* ── Stage B (T-170 / T-171 / T-172, 2026-10-20): next step, order drawer timeline,
   collapsed filters, card rows at phone width ───────────────────────────── */
.portal-sub { font-size: 0.78rem; color: var(--primary-gray); margin-top: 0.15rem; }
.portal-sub-action { color: #b8790a; font-weight: 600; }
.portal-toolbar-compact { align-items: center; }
.portal-toolbar-compact .form-input[type="search"] { flex: 1 1 240px; }
.portal-filter-toggle { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 0.4rem; white-space: nowrap; }
.portal-filter-toggle.is-active { border-color: var(--primary-blue); color: var(--primary-blue); }
.portal-filter-count { display: inline-flex; align-items: center; justify-content: center; min-width: 1.3em; height: 1.3em; padding: 0 0.3em; border-radius: 999px; background: var(--primary-blue); color: #fff; font-size: 0.7rem; font-weight: 700; }
.portal-filters { margin-top: -0.4rem; padding: 0.75rem; background: var(--surface); border-radius: 12px; }
.portal-next-step { display: inline-block; font-size: 0.82rem; font-weight: 600; line-height: 1.5; }
.portal-next-step.is-action { color: #b8790a; }
.portal-next-step.is-wait { color: var(--primary-gray); }
.portal-next-step.is-done { color: #0d6e3a; }
.portal-next-step.is-muted { color: #7b7c80; }
.portal-next-banner { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; padding: 0.85rem 1rem; border-radius: 12px; margin-bottom: 1.25rem; background: var(--surface); border: 1px solid var(--border-light); }
.portal-next-banner strong { display: block; font-size: 0.95rem; margin-top: 0.15rem; }
.portal-next-banner.is-action { background: rgba(249,174,64,0.12); border-color: rgba(249,174,64,0.5); }
.portal-next-banner.is-done { background: rgba(13,110,58,0.08); border-color: rgba(13,110,58,0.3); }
.portal-timeline { list-style: none; margin: 0 0 1.5rem; padding: 0; }
.portal-timeline li { display: flex; gap: 0.75rem; padding-bottom: 1rem; position: relative; }
.portal-timeline li:not(:last-child)::before { content: ''; position: absolute; top: 1rem; inset-inline-start: 7px; width: 2px; height: 100%; background: var(--border-light); }
.portal-timeline li.is-done:not(:last-child)::before { background: #0d6e3a; }
.portal-timeline-dot { flex-shrink: 0; width: 16px; height: 16px; border-radius: 50%; margin-top: 0.15rem; border: 2px solid var(--border-light); background: #fff; z-index: 1; }
.portal-timeline li.is-done .portal-timeline-dot { background: #0d6e3a; border-color: #0d6e3a; }
.portal-timeline li.is-current .portal-timeline-dot { border-color: var(--primary-blue); background: var(--primary-blue); box-shadow: 0 0 0 4px rgba(65,95,170,0.18); }
.portal-timeline li.is-muted { opacity: 0.5; }
.portal-timeline-label { font-size: 0.9rem; font-weight: 600; }
.portal-timeline li.is-todo .portal-timeline-label { color: var(--primary-gray); font-weight: 500; }
.portal-timeline li.is-current .portal-timeline-label { color: var(--primary-blue); }
.portal-timeline-detail { font-size: 0.78rem; color: var(--primary-gray); margin-top: 0.15rem; }
.portal-line-row { display: flex; justify-content: space-between; gap: 0.75rem; font-size: 0.88rem; padding: 0.35rem 0; border-bottom: 1px solid var(--border-light); }
.portal-inv-due strong { font-size: 0.95rem; }
.portal-inv-iban span[dir="ltr"] { font-weight: 700; letter-spacing: 0.02em; }
.portal-table tbody tr.is-due td:first-child { box-shadow: inset 3px 0 0 #f9ae40; }
[dir="rtl"] .portal-table tbody tr.is-due td:first-child { box-shadow: inset -3px 0 0 #f9ae40; }
@media (max-width: 640px) {
  .portal-table-wrap:has(.portal-cards) { border: 0; border-radius: 0; overflow: visible; }
  .portal-table.portal-cards { min-width: 0; display: block; }
  .portal-table.portal-cards thead { display: none; }
  .portal-table.portal-cards tbody { display: flex; flex-direction: column; gap: 0.75rem; }
  .portal-table.portal-cards tr { display: block; border: 1px solid var(--border-light); border-radius: 14px; padding: 0.35rem 0.9rem; background: #fff; }
  .portal-table.portal-cards tbody tr:hover { background: #fff; }
  .portal-table.portal-cards td { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; padding: 0.5rem 0; border-bottom: 1px dashed var(--border-light); text-align: end; }
  .portal-table.portal-cards td:last-child { border-bottom: 0; }
  .portal-table.portal-cards td::before { content: attr(data-label); color: var(--primary-gray); font-size: 0.76rem; font-weight: 600; flex-shrink: 0; text-align: start; }
  .portal-table.portal-cards td[data-label=""]::before, .portal-table.portal-cards td:not([data-label])::before { content: none; }
  .portal-table.portal-cards td[data-label=""] { justify-content: flex-end; }
  .portal-table.portal-cards td[data-label=""] .portal-actions { justify-content: flex-end; }
  .portal-table.portal-cards tr.portal-row-detail { padding: 0.5rem 0.9rem; background: var(--surface); }
  .portal-table.portal-cards tr.portal-row-detail td { display: block; }
  .portal-table tbody tr.is-due td:first-child { box-shadow: none; }
  .portal-table.portal-cards tr.is-due { border-color: #f9ae40; }
  .portal-next-banner { flex-direction: column; align-items: stretch; }
  .portal-next-banner .btn { width: 100%; }
}

/* ── Stage C (T-173 / T-174, 2026-10-20): home inbox + «منذ زيارتك الأخيرة» ── */
.portal-count-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 1.5em; height: 1.5em; padding: 0 0.4em; margin-inline-start: 0.5rem; border-radius: 999px; background: #f9ae40; color: #fff; font-size: 0.75rem; font-weight: 700; vertical-align: middle; }
.portal-inbox { display: flex; flex-direction: column; gap: 0.6rem; }
.portal-inbox-item { display: flex; align-items: center; gap: 0.85rem; padding: 0.85rem 1rem; border: 1px solid var(--border-light); border-radius: 14px; background: #fff; }
.portal-inbox-item.is-quote { border-color: rgba(249,174,64,0.6); background: rgba(249,174,64,0.07); }
.portal-inbox-item.is-invoice { border-color: rgba(229,57,53,0.35); background: rgba(229,57,53,0.04); }
.portal-inbox-item.is-delivery { border-color: rgba(65,95,170,0.4); background: rgba(65,95,170,0.05); }
.portal-inbox-item.is-wait { background: var(--surface); }
.portal-inbox-icon { font-size: 1.35rem; flex-shrink: 0; }
.portal-inbox-text { flex: 1 1 auto; min-width: 0; font-size: 0.92rem; }
.portal-inbox-text .portal-sub { white-space: normal; }
.portal-inbox-item .btn { flex-shrink: 0; }
.portal-inbox-empty { margin: 0; padding: 0.9rem 1rem; border: 1px dashed var(--border-light); border-radius: 14px; font-size: 0.9rem; color: var(--primary-gray); }
.portal-since { margin-bottom: 1.5rem; padding: 0.8rem 1rem; border-radius: 14px; background: rgba(65,95,170,0.06); border: 1px solid rgba(65,95,170,0.2); }
.portal-since-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; font-size: 0.9rem; margin-bottom: 0.35rem; }
.portal-since-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.portal-since-list li { display: flex; align-items: center; gap: 0.5rem; font-size: 0.86rem; }
.portal-since-link { background: none; border: 0; padding: 0; color: var(--primary-blue); font: inherit; cursor: pointer; text-align: start; text-decoration: underline; text-decoration-color: rgba(65,95,170,0.35); }
.portal-stat-card.is-due .portal-stat-card-value { color: #c62828; }
@media (max-width: 640px) {
  .portal-inbox-item { flex-wrap: wrap; }
  .portal-inbox-item .btn { width: 100%; }
  .portal-stat-grid-3 { grid-template-columns: 1fr; }
}

/* ── Stage D (T-175 / T-116, 2026-10-20): official documents, delivery confirmation ── */
.portal-invoice-docs { padding: 0 1.35rem 0.5rem; }
.portal-doc-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.portal-doc-list li { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding: 0.55rem 0.75rem; border: 1px solid var(--border-light); border-radius: 10px; font-size: 0.88rem; }
.portal-doc-list li.is-previous { opacity: 0.7; }
.portal-doc-list li .portal-sub { display: block; }

/* T-183: BOQ upload file list */
.portal-boq-files { list-style: none; margin: 0.5rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.portal-boq-files li { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; padding: 0.4rem 0.75rem; border: 1px solid var(--border-light); border-radius: 8px; font-size: 0.85rem; }
.portal-boq-remove { border: 0; background: none; color: #b42318; font-size: 1.1rem; cursor: pointer; line-height: 1; }
.portal-credit-status .btn { margin-top: 0.4rem; }
