/* =====================================================================
   RENTIQUE — central design system (customer-facing + shared components)
   Premium/luxury spec. Single source of truth via CSS custom properties.
   Owner/platform admin keep their own vendor theme.
   ===================================================================== */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800&family=Playfair+Display:wght@600;700;800&display=swap');

:root {
  /* brand + surfaces (from the Figma design tokens) */
  --brand-primary: #9955FF;
  --brand-strong:  #8532EC;
  --brand-weak:    #F2EAFF;
  --logo-r:        #8532EC;
  --logo-word:     #944EE9;
  --text-strong:   rgba(0,0,0,0.87);
  --text-muted:    rgba(0,0,0,0.55);
  --text-on-dark:  #FFFFFF;
  --surface:       #FFFFFF;
  --surface-alt:   #F5F3F8;
  --border:        #E8E3F0;

  /* status (kept for existing components) */
  --ok: #2E7D5B; --ok-bg: #E8F3EC;
  --warn: #8A6100; --warn-bg: #FBF1D8;
  --danger: #B23A48; --danger-bg: #F8E7E9;
  --info: #6A3FB0; --info-bg: #F0E9FB;

  /* spacing (8px base) */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 16px; --sp-4: 24px; --sp-5: 32px; --sp-6: 48px; --sp-7: 64px;

  /* Phase 3: card radius tightened 10px -> 8px and --r-sm 8px -> 6px so
     panels, inputs and cards share one restrained radius language. */
  --r-pill: 999px; --r-card: 8px; --r-sm: 6px;
  --shadow-card: 0 1px 2px rgba(20,10,40,.04), 0 10px 30px rgba(20,10,40,.07);
  --shadow-hover: 0 4px 8px rgba(20,10,40,.06), 0 18px 44px rgba(20,10,40,.13);
  --shadow-menu: 0 16px 44px rgba(20,10,40,.16);
  --transition: 240ms ease;
  --maxw: 1240px;

  --font-serif: 'Poppins', system-ui, -apple-system, Segoe UI, sans-serif;
  --font-nav:   'Poppins', system-ui, sans-serif;
  --font-body:  'Poppins', system-ui, -apple-system, Segoe UI, sans-serif;
  --font-tag:   'Poppins', system-ui, sans-serif;
  --font-brand: 'Playfair Display', Georgia, serif;

  /* Reference (Desktop/land) reads left-to-right, not diagonally: a solid
     plum field holds the copy, then clears entirely so the photography stays
     bright. The old 135deg version kept .73-.76 alpha all the way to 36%,
     which laid a flat veil over the model's face and killed the depth.
     The 96deg tilt matches the light falling in from the window. */
  --hero-gradient: linear-gradient(96deg,
      rgba(19,4,36,.97) 0%,  rgba(20,4,38,.93) 24%,
      rgba(22,5,42,.74) 40%, rgba(24,6,46,.36) 55%,
      rgba(26,7,50,.08) 68%, rgba(26,7,50,0)   80%);
  /* Narrow viewports stack the copy over the photo, so the scrim turns
     vertical. Anchored to the BOTTOM in fixed px, not percentages: the copy
     block is a known height and sits at the bottom, so px stops keep the text
     on solid ground and the model's face in the clear upper band no matter
     how tall the hero ends up. Percentage stops drifted with hero height and
     let the fade land across her face. */
  --hero-gradient-stacked: linear-gradient(to top,
      rgba(15,3,29,.985) 0,     rgba(15,3,29,.975) 330px,
      rgba(17,4,32,.78)  430px, rgba(18,4,34,.30)  530px,
      rgba(18,4,34,.14)  610px, rgba(18,4,34,.12)  100%);
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  font-family: var(--font-body);
  background: var(--surface);
  color: var(--text-strong);
  margin: 0; font-size: 16px; line-height: 1.6; font-variant-numeric: lining-nums;
  -webkit-font-smoothing: antialiased;
}
h1,h2,h3,h4 { font-family: var(--font-serif); font-weight: 700; line-height: 1.15; margin: 0 0 var(--sp-3); }
p { margin: 0 0 var(--sp-3); }
small { color: var(--text-muted); }
a { color: var(--brand-primary); text-decoration: none; transition: color var(--transition); }
a:hover { color: var(--brand-strong); }
img { max-width: 100%; }
hr { border: none; border-top: 1px solid var(--border); margin: var(--sp-4) 0; }
:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } * { transition: none !important; animation: none !important; } }

/* ---------------- NAVBAR (shared, luxury) ---------------- */
.site-nav {
  position: sticky; top: 0; z-index: 100; background: rgba(255,255,255,.92);
  backdrop-filter: saturate(1.3) blur(10px); border-bottom: 1px solid var(--border);
}
.site-nav__bar { max-width: var(--maxw); margin: 0 auto; padding: 12px var(--sp-4);
  display: flex; align-items: center; gap: var(--sp-4); }
/* Wordmark, matched to the logo reference: an oversized bold ITALIC "R"
   leading a lighter-weight "entique". Both were rendering at the same
   weight/size before, which flattened the mark. line-height:0 keeps the
   taller R from pushing the navbar height around. */
.brand { font-family: var(--font-brand); font-size: 1.5rem; font-weight: 600;
  letter-spacing: .2px; white-space: nowrap; display: inline-flex; align-items: baseline; }
.brand .r { color: var(--logo-r); font-size: 1.38em; font-weight: 700;
  font-style: italic; line-height: 0; letter-spacing: -.01em; }
.brand .word { color: var(--logo-word); font-weight: 500; }
.nav-links { display: flex; align-items: center; gap: 2px; flex: 1 1 auto; flex-wrap: wrap; }
.nav-links > li { list-style: none; position: relative; }
.nav-links a, .nav-links .navlink {
  font-family: var(--font-nav); font-size: clamp(.8rem, .78rem + .2vw, .92rem); font-weight: 500; letter-spacing: .03em;
  color: var(--text-strong); text-transform: uppercase; padding: 8px 9px; display: inline-flex; align-items: center; gap: 4px;
  background: none; border: none; cursor: pointer; white-space: nowrap;
}
.nav-links a:hover, .nav-links .navlink:hover, .nav-links li:hover > .navlink { color: var(--brand-primary); }
.nav-icons { display: flex; align-items: center; gap: var(--sp-4); margin-left: var(--sp-5); padding-left: var(--sp-4); border-left: 1px solid var(--border); }
.nav-icons a { color: var(--text-strong); display: inline-flex; position: relative; }
.nav-icons a:hover { color: var(--brand-primary); }
.nav-icons svg { width: 23px; height: 23px; fill: none; stroke: currentColor; stroke-width: 1.6; }
.nav-badge { position: absolute; top: -6px; right: -8px; background: var(--brand-primary); color: #fff;
  font-family: var(--font-body); font-size: .62rem; min-width: 15px; height: 15px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center; padding: 0 3px; }
.hamburger { display: none; background: none; border: none; cursor: pointer; color: var(--text-strong); }
.hamburger svg { width: 26px; height: 26px; }

/* dropdowns */
.dropdown__panel {
  position: absolute; top: 100%; left: 0; min-width: 220px; background: #fff;
  border: 1px solid var(--border); border-radius: var(--r-card); box-shadow: var(--shadow-menu);
  padding: var(--sp-3); opacity: 0; visibility: hidden; transform: translateY(6px);
  transition: opacity var(--transition), transform var(--transition), visibility var(--transition); z-index: 120;
}
.dropdown:hover > .dropdown__panel, .dropdown__panel.open { opacity: 1; visibility: visible; transform: translateY(0); }
.dropdown__panel a { display: block; font-family: var(--font-body); text-transform: none; letter-spacing: 0;
  color: var(--text-strong); padding: 7px 10px; border-radius: var(--r-sm); font-size: .9rem; }
.dropdown__panel a:hover { background: var(--brand-weak); color: var(--brand-strong); }
.dropdown__panel.mega { display: grid; grid-template-columns: repeat(3, minmax(150px,1fr)); gap: var(--sp-3); min-width: 520px; }
.dropdown__col h4 { font-family: var(--font-nav); font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); margin: 0 0 6px; }

/* mobile overlay */
.mobile-menu { position: fixed; inset: 0; background: #fff; z-index: 200; transform: translateX(100%);
  transition: transform 300ms ease; display: flex; flex-direction: column; padding: var(--sp-4); overflow-y: auto; }
.mobile-menu.open { transform: translateX(0); }
.mobile-menu__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--sp-3); }
.mobile-menu a { display: block; font-family: var(--font-serif); font-size: 1.25rem; color: var(--text-strong); padding: 12px 4px; border-bottom: 1px solid var(--border); }
.mobile-menu a:hover { color: var(--brand-primary); }
.mobile-close { background: none; border: none; cursor: pointer; color: var(--text-strong); }
.mobile-close svg { width: 28px; height: 28px; }

/* ---------------- BUTTONS ---------------- */
button, .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-family: var(--font-body); font-size: .95rem; font-weight: 600; cursor: pointer;
  padding: 12px 24px; border-radius: var(--r-pill); border: 1.5px solid var(--brand-primary);
  background: var(--brand-primary); color: #fff; text-decoration: none;
  transition: background var(--transition), transform var(--transition), box-shadow var(--transition), color var(--transition);
}
button:hover, .btn:hover { background: var(--brand-strong); border-color: var(--brand-strong); color: #fff; transform: translateY(-1px); box-shadow: var(--shadow-card); }
/* A disabled button had NO disabled look anywhere in the design system, so
   it stayed full brand-purple and read as clickable. Both places that use
   the attribute -- the image-search modal's "Find Matches" and the same
   button on image_search.php -- looked enabled while doing nothing.
   :disabled beats the :hover rule above by being later + equal specificity. */
button:disabled, .btn:disabled, button[disabled], .btn[disabled] {
  opacity: .45; cursor: not-allowed;
}
button:disabled:hover, .btn:disabled:hover, button[disabled]:hover, .btn[disabled]:hover {
  background: var(--brand-primary); border-color: var(--brand-primary);
  transform: none; box-shadow: none;
}
.btn-outline { background: transparent; color: var(--brand-primary); }
.btn-outline:hover { background: var(--brand-primary); color: #fff; }
.btn-secondary { background: var(--surface-alt); color: var(--text-strong); border-color: var(--border); }
.btn-secondary:hover { background: #ece7f4; color: var(--text-strong); border-color: var(--border); }
.btn-danger { background: var(--danger); border-color: var(--danger); }
.btn-danger:hover { background: #8f2c38; border-color: #8f2c38; }
.btn svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.9; }

/* ---------------- FORMS ---------------- */
label { display: block; margin-top: var(--sp-3); margin-bottom: 4px; font-weight: 600; font-size: .88rem; }
input, textarea, select {
  width: 100%; padding: 11px 14px; border: 1px solid var(--border); border-radius: var(--r-sm);
  font-size: .95rem; font-family: inherit; color: var(--text-strong); background: #fff;
  transition: border-color var(--transition), box-shadow var(--transition);
}
input:focus, textarea:focus, select:focus { outline: none; border-color: var(--brand-primary); box-shadow: 0 0 0 3px var(--brand-weak); }
input[type="file"] { padding: 9px; background: var(--surface-alt); cursor: pointer; }
.form-group { margin-bottom: var(--sp-2); }
/* Checkbox with an inline label. Purely additive -- new class names only, so
   nothing existing changes appearance. Needed because the bare `label` rule
   above is display:block and `input` is width:100%, which turns a checkbox
   row into a full-width block. owner/create_store.php uses this shell (it is
   pre-panel onboarding, deliberately outside the admin theme), and the owner
   panel defines the same two classes in owner/assets/css/store-admin.css so
   the markup reads identically in both systems. */
.rq-checkgroup { padding: 10px 14px; }
.rq-check {
  display: inline-flex; align-items: center; gap: 7px;
  width: auto; margin-right: 14px; margin-top: 0; font-weight: 400;
}
.rq-check input[type="checkbox"] { width: auto; margin: 0; }
.rq-check--block { display: flex; margin-top: var(--sp-3); margin-right: 0; }
/* Checkbox group heading with a Select All / Deselect All control beside it
   (owner/create_store.php). The control is a stock .btn-secondary, only
   shrunk to sit on the label's line. */
.rq-checkgroup__head { display: flex; align-items: flex-end; justify-content: space-between; flex-wrap: wrap; gap: var(--sp-2); }
.rq-checkgroup__toggle { padding: 6px 14px; font-size: .85rem; }
.rq-checkgroup__toggle[hidden] { display: none; }

/* ---------------- CONTAINERS / SHARED ---------------- */
.container { max-width: var(--maxw); margin: var(--sp-5) auto; padding: 0 var(--sp-4); }
.back-link { display: inline-flex; align-items: center; gap: 6px; margin-bottom: var(--sp-3); color: var(--text-muted); font-size: .9rem; }
.back-link:hover { color: var(--brand-primary); }
.auth-container { max-width: 440px; margin: var(--sp-7) auto; background: #fff; padding: var(--sp-6);
  border: 1px solid var(--border); border-radius: var(--r-card); box-shadow: var(--shadow-card); }
.auth-container h2 { font-size: 1.7rem; }
.auth-container input { padding: 12px 14px; }
.auth-container button { width: 100%; margin-top: 22px; padding: 13px; font-size: 1rem; }
.auth-container > p { margin-top: 16px; text-align: center; }

.alert { padding: 12px 16px; border-radius: var(--r-sm); margin-top: var(--sp-3); font-size: .92rem; border: 1px solid transparent; }
.alert-success { background: var(--ok-bg); color: var(--ok); border-color: #cfe6d8; }
.alert-error { background: var(--danger-bg); color: var(--danger); border-color: #eccdd1; }
.alert-info { background: var(--info-bg); color: var(--info); border-color: #ded0f2; }

.badge { display: inline-block; padding: 4px 11px; border-radius: var(--r-pill); font-size: .74rem; font-weight: 600; }
.badge-pending { background: var(--warn-bg); color: var(--warn); }
.badge-active { background: var(--ok-bg); color: var(--ok); }
.badge-rejected, .badge-disabled { background: var(--danger-bg); color: var(--danger); }
.badge-closed { background: #EEECF0; color: #514B59; }
.badge-info { background: var(--info-bg); color: var(--info); }

.data-table, .detail-table { width: 100%; border-collapse: separate; border-spacing: 0; margin-top: var(--sp-3);
  background: #fff; border: 1px solid var(--border); border-radius: var(--r-card); overflow: hidden; }
.data-table th, .data-table td, .detail-table th, .detail-table td { border-bottom: 1px solid var(--border); padding: 11px 14px; text-align: left; font-size: .9rem; }
.data-table tr:last-child td, .detail-table tr:last-child td { border-bottom: none; }
.data-table th { background: var(--surface-alt); font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em; font-size: .74rem; }
.data-table tbody tr:hover { background: var(--brand-weak); }
.detail-table th { width: 200px; background: var(--surface-alt); color: var(--text-muted); }

.request-card, .stat-card { background: #fff; border: 1px solid var(--border); border-radius: var(--r-card); box-shadow: var(--shadow-card); }
.request-card { padding: var(--sp-4); margin-top: var(--sp-3); }
.stat-cards { display: flex; gap: var(--sp-3); margin: var(--sp-3) 0; flex-wrap: wrap; }
.stat-card { padding: var(--sp-4); flex: 1 1 160px; }
.stat-card strong { display: block; font-size: 1.7rem; color: var(--brand-primary); font-family: var(--font-serif); }
.stat-card span { font-size: .85rem; color: var(--text-muted); }
.notification-list { list-style: none; padding: 0; }
.notification-list li { background: #fff; border: 1px solid var(--border); padding: 13px 16px; border-radius: var(--r-sm); margin-bottom: var(--sp-2); display: flex; justify-content: space-between; gap: var(--sp-3); }
.filter-bar { display: flex; gap: var(--sp-3); align-items: end; background: #fff; border: 1px solid var(--border); padding: var(--sp-4); border-radius: var(--r-card); margin-top: var(--sp-3); flex-wrap: wrap; }
.filter-bar label { margin-top: 0; } .filter-bar > div { flex: 1 1 160px; }

/* ---------------- SHOP ALL FILTERS ----------------
   The faceted filter panel on catalog.php. .filter-bar is untouched and
   still serves owner/products.php; this is its own block because the
   layout genuinely differs (a search row + a facet grid + active chips)
   rather than being a wider version of the same bar.

   Built entirely from existing tokens -- no new colours, radii or
   shadows -- so it reads as the same application. */
.shopfilter {
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r-card); padding: var(--sp-4); margin-top: var(--sp-3);
}
.shopfilter__search { display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; }
.shopfilter__search input { flex: 1 1 240px; min-width: 0; margin: 0; }
.shopfilter__search .btn { flex: 0 0 auto; }
.shopfilter__cam { display: inline-flex; align-items: center; gap: 7px; }
.shopfilter__cam svg { width: 19px; height: 19px; }
/* Below 480px the row is already tight; the icon alone still reads. */
@media (max-width: 480px) { .shopfilter__camlabel { display: none; } }

.shopfilter__more { margin-top: var(--sp-3); }
.shopfilter__toggle {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 2px; cursor: pointer; list-style: none;
  font-size: var(--fs-sm); font-weight: var(--fw-semi); color: var(--brand-strong);
}
.shopfilter__toggle::-webkit-details-marker { display: none; }
.shopfilter__toggle:focus-visible {
  outline: 2px solid var(--brand-primary); outline-offset: 3px; border-radius: 4px;
}
.shopfilter__chev { width: 16px; height: 16px; transition: transform var(--dur-fast) var(--ease-out); }
.shopfilter__more[open] .shopfilter__chev { transform: rotate(180deg); }

/* auto-fit + minmax(0,...) is what keeps this from overflowing at 320px:
   the track may shrink below the 180px preference rather than forcing
   the page wider. */
.shopfilter__grid {
  display: grid; gap: var(--sp-3); margin-top: var(--sp-2);
  grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
}
.shopfilter__field { min-width: 0; }
.shopfilter__field > label {
  display: block; margin: 0 0 6px;
  font-size: var(--fs-xs); font-weight: var(--fw-medium); color: var(--on-surface-muted);
}
.shopfilter__field select, .shopfilter__field input { width: 100%; margin: 0; }
.shopfilter__range { display: flex; align-items: center; gap: 6px; }
.shopfilter__range input { min-width: 0; }          /* flex items default to min-content */
.shopfilter__range span { color: var(--on-surface-subtle); flex: 0 0 auto; }
.shopfilter__field--check { display: flex; flex-direction: column; justify-content: center; gap: 2px; }
.shopfilter__field--check .check { margin: 0; }

.shopfilter__actions {
  display: flex; gap: var(--sp-2); flex-wrap: wrap;
  margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--border);
}
.shopfilter__active { display: flex; flex-wrap: wrap; gap: 8px; margin-top: var(--sp-3); }
/* .chip is a <button> component; as an <a> it needs the link underline off
   and the same no-lift reset the button version carries. */
.shopfilter__active .chip { text-decoration: none; transform: none; box-shadow: none; }
.shopfilter__count { margin-top: var(--sp-3); color: var(--on-surface-muted); font-size: var(--fs-sm); }

@media (max-width: 480px) {
  .shopfilter { padding: var(--sp-3); }
  .shopfilter__search .btn { width: 100%; }
}

/* ---------------- AI IMAGE SEARCH ----------------
   The modal builds on the existing .modal component; only the picture
   stage, the footer arrangement and the results layout are new. */
.imgsearch__lead { margin: 0 0 var(--sp-3); font-size: var(--fs-sm); color: var(--on-surface-muted); }

/* THE STAGE IS A FIXED BOX -- 456x220 at the panel's 520px max-width (520
   minus the panel's 2x32px padding). Its height is fixed, NOT min-height,
   and the preview is object-fit:contain inside it, so the image scales down
   to fit rather than the box growing to fit the image. That is what keeps
   the dialog exactly the same size before and after a photo is chosen; with
   min-height + max-height on the image, picking a tall portrait shot made
   the whole overlay jump taller. */
.imgsearch__stage {
  position: relative; display: flex; align-items: center; justify-content: center;
  height: 220px; padding: var(--sp-2);
  border: 1.5px dashed var(--border); border-radius: var(--r-card);
  background: var(--surface-sunken); overflow: hidden;
}
.imgsearch__empty { text-align: center; color: var(--on-surface-subtle); }
.imgsearch__empty svg { width: 40px; height: 40px; }
.imgsearch__empty p { margin: var(--sp-1) 0 0; font-size: var(--fs-sm); }
.imgsearch__preview {
  display: none;
  max-width: 100%; max-height: 100%;      /* never larger than the fixed box */
  width: auto; height: auto; object-fit: contain;
  border-radius: var(--r-sm);
}
.imgsearch__stage[data-state="ready"] .imgsearch__empty { display: none; }
.imgsearch__stage[data-state="ready"] .imgsearch__preview { display: block; }
.imgsearch__stage[data-state="loading"] .imgsearch__empty p::after { content: " — reading…"; }

/* Search in flight: veil the stage with the shared .spinner and a line of
   text. Also inside the fixed box, so nothing moves when it appears. */
.imgsearch__busy {
  position: absolute; inset: 0; z-index: 2;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-2); padding: var(--sp-3); text-align: center;
  background: rgba(255,255,255,.88);
}
.imgsearch__busy[hidden] { display: none; }
.imgsearch__busy p { margin: 0; max-width: 34ch; font-size: var(--fs-sm); font-weight: var(--fw-semi); color: var(--on-surface); }
.imgsearch__choose.is-disabled { opacity: .5; pointer-events: none; cursor: default; }

/* Crop / Undo crop chips, laid OVER the stage (absolute) so showing them
   never changes the fixed dialog size above. */
.imgsearch__tools { position: absolute; right: var(--sp-2); bottom: var(--sp-2); display: flex; gap: 6px; }
.imgsearch__tools[hidden], .imgsearch__tool[hidden] { display: none; }
.imgsearch__stage:not([data-state="ready"]) .imgsearch__tools { display: none; }
.imgsearch__tool {
  width: auto; min-height: 32px; margin: 0; padding: 5px 12px;
  border: 1px solid var(--border); border-radius: var(--r-pill);
  background: rgba(255,255,255,.94); color: var(--on-surface);
  font: inherit; font-size: var(--fs-xs); font-weight: var(--fw-semi);
  box-shadow: 0 1px 3px rgba(0,0,0,.12);
}
/* Restate background/transform: the global button:hover would otherwise
   flood the chip brand-purple under brand-purple text. */
.imgsearch__tool:hover { background: #fff; border-color: var(--brand-primary); color: var(--brand-primary);
  transform: none; box-shadow: 0 1px 3px rgba(0,0,0,.12); }


/* Reserved line: the filename appears here without shifting anything below,
   because the row keeps its height whether or not it has text. */
/* FIXED height, not min-height: with min-height the row grew ~4px the moment
   it received a filename, which nudged the whole dialog. Empty and filled
   must measure the same. */
.imgsearch__filename {
  height: 1.35em; line-height: 1.35em; margin: var(--sp-2) 0 0;
  font-size: var(--fs-xs); color: var(--on-surface-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.imgsearch__error {
  margin: var(--sp-2) 0 0; padding: 9px 12px; border-radius: var(--r-sm);
  background: var(--danger-bg); color: var(--danger); font-size: var(--fs-sm);
}

/* Footer: "Change photo" sits left, the decision pair sits right, all on one
   baseline. Every control is the same height -- a <label> styled as .btn does
   not centre its text the way a <button> does, which is what left it sitting
   low next to Cancel / Find Matches. */
/* .modal__foot { justify-content: flex-end } is defined LATER in this file, so
   a bare `.imgsearch__foot` (equal specificity, earlier) silently lost and the
   whole row stayed right-aligned. Doubling the class out-specifies it. Third
   time this trap has bitten -- see the "Specificity trap" note in
   .ai/reference/design-system.md. */
.modal__foot.imgsearch__foot { justify-content: space-between; align-items: center; gap: var(--sp-2); }
.imgsearch__actions { display: flex; align-items: center; gap: var(--sp-2); }
.imgsearch__foot .btn, .imgsearch__choose {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 11px 20px; margin: 0; line-height: 1.1;
}
.imgsearch__choose { cursor: pointer; }
.imgsearch__note { margin: var(--sp-3) 0 0; font-size: var(--fs-xs); color: var(--on-surface-subtle); }

@media (max-width: 480px) {
  /* Stack, but keep the decision pair together and full width so the two
     buttons stay equal -- three unequal buttons wrapping is what looks broken. */
  .modal__foot.imgsearch__foot { flex-direction: column-reverse; align-items: stretch; }
  .imgsearch__actions { width: 100%; }
  .imgsearch__actions .btn { flex: 1 1 0; }
  .imgsearch__choose { width: 100%; }
  /* Claw back some width from the overlay's own gutter: at 320px the panel
     was only 254px wide, which is what squeezed the buttons in the first place. */
  .modal.imgsearch { padding: var(--sp-2); }
}
@media (max-width: 420px) {
  /* Below this there is not enough room for two buttons side by side --
     "Find Matches" wrapped to a second line and the row grew to 56px while
     its neighbour stayed 44px. Stacking keeps every control one line tall.
     column-reverse puts the primary action (last in the DOM) on top. */
  .imgsearch__actions { flex-direction: column-reverse; }
  .imgsearch__actions .btn { width: 100%; flex: 0 0 auto; }
}

/* ---- results mode on catalog.php ---- */
.imgres__head { display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-2); }
.imgres__title { margin: 0; }
.imgres__sub { margin: 4px 0 0; color: var(--on-surface-muted); font-size: var(--fs-sm); }
.imgres__stub { margin-top: var(--sp-3); font-size: var(--fs-sm); }

/* Query image left, matches right. minmax(0,1fr) on the second track is what
   stops a wide product grid from pushing the page sideways. */
.imgres { display: grid; gap: var(--sp-4); margin-top: var(--sp-4);
  grid-template-columns: 280px minmax(0, 1fr); align-items: start; }
.imgres__query {
  position: sticky; top: var(--sp-4);
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r-card); padding: var(--sp-3);
}
.imgres__qtitle { margin: 0 0 var(--sp-2); font-size: var(--fs-base); }
.imgres__qimg { display: block; width: 100%; aspect-ratio: 3/4; object-fit: cover;
  border-radius: var(--r-sm); background: var(--surface-sunken); }
.imgres__qimg--none { display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 6px; color: var(--on-surface-subtle); }
.imgres__qimg--none svg { width: 34px; height: 34px; }
.imgres__qimg--none p { margin: 0; font-size: var(--fs-xs); }
.imgres__qnote { margin: var(--sp-2) 0 var(--sp-3); font-size: var(--fs-xs); color: var(--on-surface-subtle); }
.imgres__again { width: 100%; text-align: center; }
.imgres__matches { min-width: 0; }
.imgres__matches .product-grid { margin-top: var(--sp-2); }

/* Compact grid for match results: the SAME .pcard, just laid out on a
   smaller track so a handful of results don't render as huge tiles next to
   the query image. Shared with image_search.php so both results views match.
   The class is DOUBLED to out-specify `.store-grid, .product-grid`, which is
   defined further down this file and would otherwise win on order alone. */
.product-grid.product-grid--compact {
  grid-template-columns: repeat(auto-fill, minmax(min(168px, 100%), 1fr));
  gap: var(--sp-3);
}
@media (max-width: 700px) {
  .product-grid.product-grid--compact {
    grid-template-columns: repeat(auto-fill, minmax(min(148px, 100%), 1fr));
  }
}
@media (max-width: 480px) {
  /* The base rule pins every grid to exactly 2 columns here; keep that for
     compact too, or the cards become unreadably narrow on a phone. */
  .product-grid.product-grid--compact { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Stack below the nav breakpoint: a 280px rail plus a card grid stops
   fitting long before the phone widths. Image first, as specified. */
@media (max-width: 860px) {
  .imgres { grid-template-columns: minmax(0, 1fr); }
  .imgres__query { position: static; }
  /* A full-width 3/4 query image would fill a phone screen on its own. */
  .imgres__qimg { max-width: 240px; margin-inline: auto; }
  .imgres__again { max-width: 240px; margin-inline: auto; display: block; }
}

/* generic product/store grids used by catalog etc. */
.store-grid, .product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: var(--sp-4); margin-top: var(--sp-4); }
@media (max-width: 700px) { .store-grid, .product-grid { grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); gap: var(--sp-3); } }
/* Phase 3: products now use .pcard (includes/product_card.php). This class
   is left for the STORE cards in browse.php, lightened to match -- less
   framing, smaller radius, no drop shadow. */
.product-card { background: none; border: none; border-radius: 6px; padding: 0;
  box-shadow: none; transition: none; display: flex; flex-direction: column; }
.product-card:hover { box-shadow: none; }
.product-card h4 { font-size: var(--fs-sm); margin: 8px 0 2px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: 2.5em; }
.product-card img, .product-card .img-placeholder { width: 100%; aspect-ratio: 3/4; object-fit: cover; border-radius: 6px;
  background: var(--brand-weak); display: flex; align-items: center; justify-content: center; color: var(--text-muted); font-size: .8rem; margin-bottom: var(--sp-2);
  transition: opacity var(--dur-base) var(--ease-out); }
/* No zoom here: this markup has no overflow-hidden media wrapper, so a
   scaled image would spill over the caption. A soft fade reads as premium
   and cannot break layout. */
.product-card:hover img { opacity: .88; }
.img-placeholder { background: var(--brand-weak); color: var(--text-muted); display: flex; align-items: center; justify-content: center; border-radius: var(--r-sm); }

/* ============ LANDING PAGE ============ */
/* ---- HERO ----
   One full-bleed photograph, composed like the Desktop/land reference: the
   plum wall on the left of the artwork carries the copy, the model owns the
   right half. Nothing overlaps her, so the image reads as the subject rather
   than as a backdrop behind a headline.

   `background-position: 70% 16%` is doing real work. The artwork is ~1.88:1,
   heroes are wider, so `cover` scales to width and crops vertically -- a
   plain `center` sliced the top of her head off against the navbar. Holding
   the frame high keeps her headroom; 70% X keeps her and the dress rack in
   view as the viewport narrows. */
.hero {
  /* The copy column, as one token. Every child below reads it (including
     .hero-search), and each breakpoint retunes it in one place -- so the
     headline can never grow into the model. */
  --hero-col: min(560px, 50%);
  position: relative; display: flex; align-items: center; overflow: hidden;
  min-height: clamp(560px, 86vh, 940px);
  background: #1a0730 no-repeat;
  background-size: cover;
  background-position: 70% 16%;
  /* Path is relative to THIS stylesheet (assets/css/), not to the page. */
  background-image: url('../img/hero.jpg'); }
.hero::after { content: ''; position: absolute; inset: 0; background: var(--hero-gradient); }
.hero__inner { position: relative; z-index: 2; max-width: var(--maxw); margin: 0 auto; padding: var(--sp-7) var(--sp-4); width: 100%; }
.hero__tag, .hero__title, .hero__sub, .hero__ctas { max-width: var(--hero-col); }
/* Kicker: uppercase, bold and letter-spaced per the reference -- it reads as
   a kicker above the headline now, not as a second heading competing with it. */
.hero__tag { font-family: var(--font-tag); color: var(--brand-primary);
  font-size: clamp(.86rem, 1.1vw, 1.05rem); font-weight: 700;
  text-transform: uppercase; letter-spacing: .18em;
  margin-bottom: var(--sp-3); }
/* Headline: Playfair (--font-brand, the logo face) exactly as the reference
   sets it -- the editorial serif is what makes the hero read premium. Note
   --font-serif is Poppins in this project despite the name, so it is the
   wrong token here. */
.hero__title { font-family: var(--font-brand); color: var(--text-on-dark);
  font-weight: 600; font-size: clamp(2.9rem, 7.4vw, 6.4rem);
  line-height: .98; letter-spacing: .02em; margin: 0;
  /* Caps come from CSS, not the markup, so the copy stays readable to screen
     readers and keeps its natural casing if it's ever reused. */
  text-transform: uppercase; }
.hero__line { display: block; }
.hero__title .accent { color: var(--brand-primary); }
.hero__sub { color: rgba(255,255,255,.85); font-size: 1.1rem; margin: var(--sp-4) 0 var(--sp-5); }
.hero__ctas { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-4); }
.hero__ctas .btn-outline { color: #fff; border-color: rgba(255,255,255,.7); }
.hero__ctas .btn-outline:hover { background: #fff; color: var(--brand-strong); border-color: #fff; }
.hero-search { display: flex; align-items: center; gap: 8px; background: #fff; border-radius: var(--r-pill);
  padding: 6px 6px 6px 20px; width: 100%; max-width: var(--hero-col, 640px); box-sizing: border-box; box-shadow: var(--shadow-card); margin: 0 0 var(--sp-5); }
.hero-search input { border: none; box-shadow: none !important; flex: 1; padding: 10px 0; background: transparent; }
.hero-search .cam { color: var(--text-muted); display: inline-flex; }
.hero-search .cam svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.7; }
.hero-search .btn { padding: 11px 20px; white-space: nowrap; }

.section { max-width: var(--maxw); margin: var(--sp-7) auto; padding: 0 var(--sp-4); scroll-margin-top: 84px; }
.section__head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: var(--sp-4); }
.section__head h2 { font-family: var(--font-body); font-weight: 700; font-size: 1.35rem; letter-spacing: .01em; margin: 0; }
.section__head a { font-family: var(--font-nav); text-transform: uppercase; font-size: .8rem; letter-spacing: .05em; }
.rail { display: grid; grid-auto-flow: column; grid-auto-columns: 230px; gap: var(--sp-4); overflow-x: auto; padding-bottom: var(--sp-2); scroll-snap-type: x mandatory; }
@media (min-width: 1000px) { .rail { grid-auto-columns: 246px; } }
@media (max-width: 700px) { .rail { grid-auto-columns: 168px; } }
.rail > * { scroll-snap-align: start; }
.rail::-webkit-scrollbar { height: 8px; } .rail::-webkit-scrollbar-thumb { background: var(--border); border-radius: 99px; }

/* Phase 3 refinement: the card was a heavy framed box (1px border + 10px
   radius + drop shadow + a purple gradient meta strip). A premium fashion
   marketplace leads with the garment, so the frame is gone: no border, no
   card shadow, small 6px radius on the image only, and the photo carries
   the hover. */
.pcard { display: block; background: none; border: none; border-radius: 0;
  color: inherit; transition: none; }
.pcard:hover { box-shadow: none; color: inherit; }
.pcard__media { position: relative; overflow: hidden; border-radius: 6px;
  background: var(--brand-weak); }
.pcard__img { width: 100%; aspect-ratio: 3/4; object-fit: cover; display: block;
  background: var(--brand-weak);
  transition: transform 520ms var(--ease-out); }
.pcard-wrap:hover .pcard__img { transform: scale(1.04); }
.pcard__img.ph { display: flex; align-items: center; justify-content: center;
  color: var(--text-muted); font-size: var(--fs-sm); aspect-ratio: 3/4; }
/* Match-confidence badge (image search). Top-left, matching the reference.
   On a scored card the availability flag drops to the BOTTOM of the image:
   side by side, "82% match" + "Fully booked" ran into each other on the
   compact grid, and there is no card width where both comfortably fit on
   one row. Two corners never collide at any size. */
.pcard__match { position: absolute; top: 8px; left: 8px; z-index: 2; pointer-events: none; }
.pcard-wrap--scored .pcard__flag { top: auto; bottom: 8px; left: 8px; }
.pcard__flag { position: absolute; top: 8px; left: 8px; z-index: 2;
  background: rgba(20,6,36,.78); color: #fff; font-size: var(--fs-xs);
  font-weight: var(--fw-semi); padding: 3px 9px; border-radius: 4px; }
.pcard__body { padding: 10px 2px 0; }
/* min-height keeps prices aligned across a row, but 2.6em left a visible
   gap under one-line titles. 2.3em still reserves two lines at this size. */
.pcard__title { font-family: var(--font-body); font-size: var(--fs-sm); color: var(--text-strong);
  margin: 0 0 4px; line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: 2.3em; }
.pcard__price { display: flex; align-items: baseline; gap: 6px; }
.pcard__now { font-weight: var(--fw-semi); color: var(--text-strong); font-size: var(--fs-sm); }
.pcard__unit { color: var(--text-muted); font-size: var(--fs-xs); }
.pcard__was { color: var(--text-muted); text-decoration: line-through; font-size: .82rem; }
.pcard__store { margin: 0 0 4px; font-size: var(--fs-xs); color: var(--text-muted);
  text-transform: uppercase; letter-spacing: .05em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pcard__rating { margin: 4px 0 0; font-size: var(--fs-xs); color: var(--text-muted); }
.pcard__star { color: var(--star); }
/* Kept for any card still using the old meta strip markup. */
.pcard__meta { display: flex; align-items: center; justify-content: space-between;
  padding: 6px 2px 0; margin-top: 4px; background: none; }
.pcard__size { font-size: .72rem; font-weight: 700; color: var(--brand-strong); border: 1px solid var(--brand-primary); border-radius: 6px; padding: 1px 7px; }
.pcard__rate { font-size: var(--fs-xs); color: var(--text-muted); }

/* ---- Hover-only actions (Shop All / rails) ----
   Sibling of the card anchor, overlaid on the image. Revealed on hover and
   on keyboard focus, so it is not mouse-only. On touch devices there is no
   hover, so it stays visible. */
/* The actions are a sibling of .pcard (no nested anchors), so they anchor to
   .pcard-wrap -- which also contains the caption. Pinning them to the wrap's
   bottom put the button on top of the title/price. Instead the overlay spans
   the MEDIA box: same width, same 3/4 aspect ratio, buttons aligned to its
   bottom edge. */
.pcard__actions { position: absolute; left: 0; right: 0; top: 0; z-index: 3;
  aspect-ratio: 3/4; padding: 8px;
  display: flex; align-items: flex-end; gap: 6px;
  pointer-events: none;                      /* only the buttons are clickable */
  opacity: 0;
  /* The rise animates on the BUTTON, not this container: translating the
     container left its box 6px into the caption area while at rest. */
  transition: opacity var(--dur-base) var(--ease-out); }
.pcard__actions > * { pointer-events: auto; }
.pcard-wrap:hover .pcard__actions,
.pcard-wrap:focus-within .pcard__actions { opacity: 1; }
.pcard__action { flex: 1; display: inline-flex; align-items: center; justify-content: center;
  gap: 6px; padding: 9px 12px; border-radius: var(--r-sm);
  background: rgba(255,255,255,.96); color: var(--text-strong);
  font-size: var(--fs-xs); font-weight: var(--fw-semi); letter-spacing: .02em;
  box-shadow: 0 4px 14px rgba(20,10,40,.18); backdrop-filter: blur(4px);
  transform: translateY(6px);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out),
              transform var(--dur-base) var(--ease-out); }
.pcard-wrap:hover .pcard__action,
.pcard-wrap:focus-within .pcard__action { transform: translateY(0); }
.pcard__action:hover { background: var(--brand-primary); color: #fff; }
.pcard__action svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.8; }
@media (hover: none) {
  /* No hover on touch -- show the action inline under the caption instead. */
  .pcard__actions { opacity: 1; transform: none; position: static;
    aspect-ratio: auto; padding: 0; margin-top: 8px; }
  .pcard__action { box-shadow: none; background: var(--surface-2); }
}
/* Quick-add is a CSRF-protected POST form now, so its trigger is a <button>
   rather than an <a>. The global `button` rule would hand it a pill border
   and a different hover shadow; .pcard__action already owns the look, so
   undo only those. The card's own hover rules still drive the rise. */
form.pcard__actions { margin: 0; }
button.pcard__action { border: 0; font-family: inherit; }
button.pcard__action:hover { box-shadow: 0 4px 14px rgba(20,10,40,.18); }
/* Product page: keep "Add to Cart" sitting inline beside "Book Now". */
.pdp__addform { display: inline-block; margin: 0; vertical-align: middle; }

/* ---- CATEGORY + SHOP TILES ----
   Replaces the old .catpill, which put the label INSIDE the image behind a
   dark scrim. That fought two ways: the scrim dimmed the very photography the
   section is selling, and an overlaid uppercase label belonged to a different
   card language than the product grid right above it.

   These tiles are the same FAMILY as .pcard -- identical frame, 8px/6px radius
   pair, the same 520ms image zoom, the same title face -- but they carry only
   an image and a name. Everything reuses existing tokens; no new palette, no
   second card system. Markup: includes/tile_card.php. */
.tilegrid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--sp-4);
}
/* Shops are few for now, and a lone 1fr card left a card-sized tile stranded
   against five empty tracks. Capping the track at 224px and centring the row
   makes one or two shops read as deliberate instead of orphaned, while a full
   directory still fills the row exactly like the category grid. auto-fit was
   the wrong tool -- it collapses the empty tracks and stretches the single
   card into a billboard. */
.tilegrid--shops {
  /* Left-aligned, same reading edge as the category grid and the product
     rails above it. The 224px max (rather than 1fr) is what keeps a lone shop
     card at tile size instead of stretching into a billboard -- the usual
     reason auto-fit gets avoided. */
  grid-template-columns: repeat(auto-fit, minmax(180px, 224px));
  justify-content: start;
}

/* Frame matched to .pcard-wrap so a tile and a product card sit on the same
   surface with the same hover. */
.tile-wrap {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px;
  transition: border-color var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
.tile-wrap:hover {
  border-color: #DCD3EA;
  box-shadow: 0 6px 20px rgba(20,10,40,.07);
}
.tile { display: block; color: inherit; }
.tile__media { position: relative; overflow: hidden; border-radius: 6px;
  background: var(--brand-weak); }
.tile__img { width: 100%; aspect-ratio: 3/4; object-fit: cover; display: block;
  transition: transform 520ms var(--ease-out); }
.tile-wrap:hover .tile__img { transform: scale(1.04); }
/* Square frame for brand marks. */
.tile__media--square .tile__img { aspect-ratio: 1/1; }
/* `contain` never crops a logo -- it letterboxes instead, which is the right
   trade for a wordmark. Padding keeps it off the frame edge. */
.tile__img--contain { object-fit: contain; padding: 12px; background: var(--surface-1); }
/* Placeholders speak the product card's language (--brand-weak ground, muted
   ink) rather than a loud gradient. This is the state that actually renders
   until category images are uploaded in the admin panel. */
.tile__img--ph { display: flex; align-items: center; justify-content: center;
  background: var(--brand-weak); color: var(--brand-strong); }
.tile__img--ph svg { width: 34%; height: 34%; opacity: .5; }
.tile__img--initial { font-family: var(--font-brand);
  font-size: clamp(2rem, 4.4vw, 2.9rem); line-height: 1; letter-spacing: .03em;
  opacity: .62; }
.tile__body { padding: 10px 2px 0; }
.tile__title { font-family: var(--font-body); font-size: var(--fs-sm);
  color: var(--text-strong); margin: 0; line-height: 1.35; text-align: center;
  /* Two lines reserved so a long shop name can't shove the grid out of
     alignment, and so hovering never shifts layout. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-height: 2.3em; }
/* The whole tile is one link, so the frame carries the focus ring -- a ring
   drawn tight around the inline <a> would have cut through the image. */
.tile:focus-visible { outline: none; }
.tile-wrap:focus-within { border-color: var(--brand-primary);
  outline: 2px solid var(--brand-primary); outline-offset: 2px; }

@media (min-width: 1600px) {
  /* Hold the category grid at five columns on very wide screens. auto-fill
     went to six, which split the seven live categories as 6 + 1 and left a
     single tile alone on the second row. Five gives 5 + 2 and lets each tile
     breathe a little wider without becoming a panel.
     :not() matters -- the shops grid carries .tilegrid too, and without the
     exclusion this later rule overwrote its centred, size-capped tracks. */
  .tilegrid:not(.tilegrid--shops) { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}
@media (max-width: 700px) {
  .tilegrid, .tilegrid--shops {
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: var(--sp-3); }
}
@media (max-width: 480px) {
  /* Two up: below this, a third column makes the imagery too small to read. */
  .tilegrid, .tilegrid--shops {
    grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }
  .tile-wrap { padding: 8px; }
}

.pcard__now, .pcard__was, .pcard__price, .pdp__price, .price, .rent, .stat-card strong, .cart-summary .total, .bdetails, .booking-card .badge { font-family: var(--font-body); font-variant-numeric: tabular-nums lining-nums; }

.hero-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-3); margin-top: var(--sp-3); max-width: 940px; }
.hero-stat { display: flex; align-items: center; gap: 12px; background: rgba(255,255,255,.10); border: 1px solid rgba(255,255,255,.16); border-radius: var(--r-card); padding: 14px; backdrop-filter: blur(6px); }
.hero-stat .ico { width: 40px; height: 40px; flex: 0 0 auto; border-radius: 50%; background: rgba(153,85,255,.35); display: flex; align-items: center; justify-content: center; color: #fff; }
.hero-stat .ico svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.hero-stat b { color: #fff; font-size: 1.05rem; display: block; }
.hero-stat span { color: rgba(255,255,255,.72); font-size: .78rem; }
@media (max-width: 900px) { .hero-stats { grid-template-columns: 1fr 1fr; } }
@media (max-width: 480px) { .hero-stats { grid-template-columns: 1fr; } }

/* ---------------- FOOTER ---------------- */
.site-footer { background: #14061f; color: rgba(255,255,255,.8); margin-top: var(--sp-7); }
.site-footer__inner { max-width: var(--maxw); margin: 0 auto; padding: var(--sp-6) var(--sp-4);
  display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: var(--sp-5); }
.site-footer h3 { font-family: var(--font-brand); color: #fff; font-size: 1.4rem; margin: 0 0 8px; }
.site-footer h3 .r { color: var(--brand-primary); }
.site-footer h4 { font-family: var(--font-nav); text-transform: uppercase; font-size: .78rem; letter-spacing: .06em; color: #fff; margin: 0 0 10px; }
.site-footer ul { list-style: none; padding: 0; margin: 0; }
.site-footer li { margin-bottom: 8px; }
.site-footer a { color: rgba(255,255,255,.72); font-size: .9rem; }
.site-footer a:hover { color: var(--brand-primary); }
.site-footer .socials { display: flex; gap: 12px; margin-top: 8px; }
.site-footer .socials a svg { width: 20px; height: 20px; fill: currentColor; }
.site-footer__bottom { border-top: 1px solid rgba(255,255,255,.12); text-align: center; padding: 16px; font-size: .82rem; color: rgba(255,255,255,.55); }

/* ---------------- RESPONSIVE ---------------- */
@media (max-width: 1024px) { .site-footer__inner { grid-template-columns: 1fr 1fr; } }
@media (max-width: 1080px) {
  /* Phase 2: raised from 780px. Between 800-1024px the six top-level items
     wrapped onto two rows, making the bar 135px tall. The overlay menu is
     cleaner than a stacked bar, and one row returns at >=1100px. */
  .nav-links { display: none; }            /* collapse to hamburger */
  .nav-icons { margin-left: auto; }
  .hamburger { display: inline-flex; }
}
@media (max-width: 1024px) {
  /* Tablet: the copy needs more than the desktop 50% column, and the bright
     middle of the photo creeps leftward as the frame narrows, so give the
     column room and pull the crop slightly left to keep her clear of it. */
  .hero { --hero-col: min(520px, 64%); background-position: 66% 14%; }
}
@media (max-width: 780px) {
  /* RECOMPOSED, not squeezed. Side-by-side stops working once the viewport is
     portrait -- `cover` then scales to height, and the copy would sit straight
     across her face. So the composition rotates: the photograph owns the upper
     band (face, bodice and the dress rack all readable), the copy sits on the
     dark base below it, and the crop is biased to her so the subject is never
     the part that gets cropped away.

     The copy is also tightened here -- the CTAs share a row and the vertical
     rhythm closes up -- because every pixel the text block saves is a pixel of
     photograph, which is the whole point of the section. */
  .hero { align-items: flex-end; min-height: min(94vh, 820px);
    background-position: 72% top; --hero-col: 100%; }
  .hero::after { background: var(--hero-gradient-stacked); }
  .hero__inner { padding-bottom: var(--sp-5); }
  .hero__tag { margin-bottom: var(--sp-2); }
  .hero__title { font-size: clamp(2.5rem, 12vw, 3.6rem); }
  .hero__sub { font-size: 1rem; margin: var(--sp-3) 0 var(--sp-4); }
  .hero__ctas { gap: var(--sp-2); margin-bottom: var(--sp-3); }
  .hero__ctas .btn { padding: 11px 18px; }
}
@media (max-width: 640px) {
  .container, .section { padding: 0 var(--sp-3); }
  .hero { min-height: min(86vh, 680px); }
  .hero-search { flex-wrap: wrap; padding: 12px; border-radius: var(--r-card); }
  .hero-search input { width: 100%; }
  .site-footer__inner { grid-template-columns: 1fr; gap: var(--sp-4); }
  .stat-cards { flex-direction: column; }
}

/* ---- MINIMALIST CARD FRAME ----
   Phase 3 stripped the frame entirely; this puts a restrained one back --
   white surface, hairline border, 8px radius, quiet hover. Enough to give
   each product its own "tile" like other premium stores, without the old
   heavy border + drop shadow + gradient strip. */
.pcard-wrap {
  --pcard-pad: 10px;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: var(--pcard-pad);
  transition: border-color var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
.pcard-wrap:hover {
  border-color: #DCD3EA;
  box-shadow: 0 6px 20px rgba(20,10,40,.07);
}
/* The overlay is positioned against the wrap's PADDING box, so it has to be
   inset by the frame padding to line up with the image exactly. */
.pcard__actions { top: var(--pcard-pad); left: var(--pcard-pad); right: var(--pcard-pad); }

/* hover "+" quick add-to-cart on product cards */
.pcard-wrap, .product-card { position: relative; }
.card-add { position: absolute; top: 8px; right: 8px; z-index: 3; width: 34px; height: 34px; border-radius: 50%;
  background: var(--brand-primary); color: #fff; display: flex; align-items: center; justify-content: center;
  font-size: 22px; line-height: 1; font-weight: 600; text-decoration: none; box-shadow: 0 2px 8px rgba(0,0,0,.22);
  opacity: 0; transform: scale(.8); transition: opacity var(--transition), transform var(--transition), background var(--transition); }
.pcard-wrap:hover .card-add, .product-card:hover .card-add { opacity: 1; transform: scale(1); }
.card-add:hover { background: var(--brand-strong); color: #fff; }
@media (hover: none) { .card-add { opacity: 1; transform: scale(1); } }

/* =====================================================================
   PHASE 1 — DESIGN SYSTEM EXTENSION
   Purely additive. Nothing above this line was modified: every rule here
   is a NEW token or a NEW class, so no existing page changes appearance
   until it opts in. Older tokens are kept and aliased, never renamed.
   ===================================================================== */

:root {
  /* ---- type scale (fluid where it earns it) ---- */
  --fs-xs:   .75rem;
  --fs-sm:   .875rem;
  --fs-base: 1rem;
  --fs-lg:   1.125rem;
  --fs-xl:   1.375rem;
  --fs-2xl:  1.75rem;
  --fs-3xl:  clamp(2rem, 1.6rem + 1.8vw, 3rem);
  --lh-tight: 1.15; --lh-snug: 1.35; --lh-normal: 1.6;
  --fw-normal: 400; --fw-medium: 500; --fw-semi: 600; --fw-bold: 700;

  /* ---- semantic surfaces (dark-mode readiness: a later theme swaps
         THESE four, not the 200 rules that consume them) ---- */
  --surface-1:      #FFFFFF;              /* cards, panels, menus */
  --surface-2:      #F5F3F8;              /* page tint, table head */
  --surface-3:      #EEEAF4;              /* hover / pressed rows  */
  --surface-sunken: #E8E3F0;              /* wells, tracks         */
  --on-surface:        rgba(0,0,0,0.87);
  --on-surface-muted:  rgba(0,0,0,0.55);
  --on-surface-subtle: rgba(0,0,0,0.38);

  /* ---- colours the pages were hardcoding ---- */
  --star: #E6A700;                        /* was #E6A700 inline x5+ */
  --chart-1: #9955FF; --chart-2: #2E7D5B; --chart-3: #E6A700;
  --chart-4: #B23A48; --chart-5: #3B82F6; --chart-6: #8B8697;

  /* ---- elevation ---- */
  --e-0: none;
  --e-1: 0 1px 2px rgba(20,10,40,.05);
  --e-2: 0 2px 10px rgba(20,10,40,.08);
  --e-3: var(--shadow-card);
  --e-4: var(--shadow-menu);

  /* ---- motion ---- */
  --dur-fast: 120ms; --dur-base: 240ms; --dur-slow: 360ms;
  --ease-out: cubic-bezier(.22,.61,.36,1);

  /* ---- radii (extends --r-sm/--r-card/--r-pill) ---- */
  --r-lg: 14px; --r-xl: 20px;

  /* ---- z-index scale (documents the magic numbers already in use) ---- */
  --z-nav: 100; --z-dropdown: 120; --z-overlay: 200; --z-modal: 300; --z-toast: 400;
}

/* ---------------- A11Y: SKIP LINK ---------------- */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: var(--z-toast);
  background: var(--brand-strong); color: #fff; padding: 10px 18px;
  border-radius: 0 0 var(--r-sm) 0; font-weight: var(--fw-semi);
}
.skip-link:focus { left: 0; color: #fff; }

/* ---------------- RESPONSIVE TABLE WRAPPER ----------------
   Customer-side tables had no scroll container, so .detail-table's
   fixed 200px header column forced page-wide horizontal scroll on
   small screens. Wrap a table in this and the TABLE scrolls, not the page. */
.table-wrap { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-wrap > table { min-width: 520px; }

/* ---------------- STATUS DOT ---------------- */
.status-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: var(--on-surface-subtle); margin-right: 6px; flex: 0 0 auto; }
.status-dot--ok { background: var(--ok); }
.status-dot--warn { background: var(--warn); }
.status-dot--danger { background: var(--danger); }
.status-dot--info { background: var(--brand-primary); }

/* ---------------- AVATAR ---------------- */
.avatar { width: 36px; height: 36px; border-radius: 50%; object-fit: cover;
  background: var(--brand-weak); color: var(--brand-strong); flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--fs-sm); font-weight: var(--fw-semi); text-transform: uppercase; }
.avatar--sm { width: 28px; height: 28px; font-size: var(--fs-xs); }
.avatar--lg { width: 54px; height: 54px; font-size: var(--fs-lg); }

/* ---------------- CHIP / FILTER TAG ---------------- */
.chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px;
  border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--surface-1); color: var(--on-surface);
  font-size: var(--fs-sm); font-weight: var(--fw-medium); cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
/* reset the global button:hover lift -- a filter chip shouldn't bounce */
.chip:hover { background: var(--surface-2); transform: none; box-shadow: none; }
.chip[aria-pressed="true"], .chip.is-active {
  background: var(--brand-primary); border-color: var(--brand-primary); color: #fff; }
.chip__x { font-size: var(--fs-base); line-height: 1; opacity: .7; }
.chip__x:hover { opacity: 1; }

/* ---------------- TABS ---------------- */
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border);
  overflow-x: auto; scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
.tab {
  appearance: none; background: none; border: none; cursor: pointer;
  padding: 10px 16px; font-size: var(--fs-sm); font-weight: var(--fw-medium);
  color: var(--on-surface-muted); white-space: nowrap; border-radius: 0;
  border-bottom: 2px solid transparent; transition: color var(--dur-fast) var(--ease-out);
}
.tab:hover { color: var(--on-surface); background: none; transform: none; box-shadow: none; }
.tab[aria-selected="true"], .tab.is-active {
  color: var(--brand-strong); border-bottom-color: var(--brand-primary);
  background: none; box-shadow: none; }

/* ---------------- BREADCRUMBS ---------------- */
.breadcrumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  list-style: none; padding: 0; margin: 0 0 var(--sp-3);
  font-size: var(--fs-sm); color: var(--on-surface-muted); }
.breadcrumbs li { display: inline-flex; align-items: center; gap: 6px; }
.breadcrumbs li + li::before { content: '/'; color: var(--on-surface-subtle); }
.breadcrumbs [aria-current="page"] { color: var(--on-surface); font-weight: var(--fw-medium); }

/* ---------------- PAGINATION ----------------
   catalog.php hand-rolls this inline today; this is its replacement. */
.pagination { display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  justify-content: center; margin-top: var(--sp-5); list-style: none; padding: 0; }
.pagination a, .pagination span {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 38px; height: 38px; padding: 0 10px; border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--surface-1);
  color: var(--on-surface); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.pagination a:hover { background: var(--brand-weak); color: var(--brand-strong); }
.pagination [aria-current="page"] { background: var(--brand-primary);
  border-color: var(--brand-primary); color: #fff; font-weight: var(--fw-semi); }
.pagination .is-disabled { opacity: .45; pointer-events: none; }

/* ---------------- EMPTY STATE ----------------
   Replaces the `.request-card` + inline text-align hack. */
.empty-state { text-align: center; padding: var(--sp-6) var(--sp-4);
  border: 1px dashed var(--border); border-radius: var(--r-card);
  background: var(--surface-1); color: var(--on-surface-muted); }
.empty-state__icon { width: 56px; height: 56px; margin: 0 auto var(--sp-3);
  border-radius: 50%; background: var(--brand-weak); color: var(--brand-primary);
  display: flex; align-items: center; justify-content: center; }
.empty-state__icon svg { width: 26px; height: 26px; fill: none; stroke: currentColor; stroke-width: 1.7; }
.empty-state__title { font-size: var(--fs-lg); font-weight: var(--fw-semi);
  color: var(--on-surface); margin: 0 0 6px; }
.empty-state p { margin: 0 auto var(--sp-3); max-width: 46ch; font-size: var(--fs-sm); }

/* ---------------- SPINNER ---------------- */
.spinner { display: inline-block; width: 20px; height: 20px; border-radius: 50%;
  border: 2px solid var(--surface-sunken); border-top-color: var(--brand-primary);
  animation: rq-spin 700ms linear infinite; vertical-align: middle; }
.spinner--lg { width: 34px; height: 34px; border-width: 3px; }
@keyframes rq-spin { to { transform: rotate(360deg); } }

/* Submitting form -- driven by assets/js/form-busy.js.
   Reuses .spinner above rather than defining a second one; only the size
   and the colours change, so there is still ONE spinner in the project. */
button.is-busy, input.is-busy { cursor: progress; opacity: .8; }
button.is-busy { display: inline-flex; align-items: center; justify-content: center; }
/* currentColor, not the brand token: this rides on filled buttons (purple),
   outline buttons (white) and danger buttons alike, and a fixed colour
   would vanish on at least one of them. */
.btn__spinner { width: 14px; height: 14px; margin-right: 8px; flex: none;
  border-width: 2px; border-color: currentColor; border-top-color: transparent;
  vertical-align: baseline; }

/* Someone who has asked the OS for less motion should not get a spinner
   whirling at them; the dimmed, disabled button still reads as "working". */
@media (prefers-reduced-motion: reduce) {
  .spinner, .btn__spinner { animation-duration: 2.4s; }
}

/* ---------------- SKELETON ---------------- */
.skeleton { position: relative; overflow: hidden; border-radius: var(--r-sm);
  background: var(--surface-2); }
.skeleton::after { content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.65), transparent);
  transform: translateX(-100%); animation: rq-shimmer 1.4s infinite; }
@keyframes rq-shimmer { to { transform: translateX(100%); } }
.skeleton-text { height: .8em; margin: .4em 0; }
.skeleton-text--short { width: 55%; }
.skeleton-img { width: 100%; aspect-ratio: 2/3; border-radius: var(--r-sm); }

/* ---------------- METER / PROGRESS ---------------- */
.meter { height: 8px; border-radius: var(--r-pill); background: var(--surface-sunken);
  overflow: hidden; }
.meter__fill { height: 100%; border-radius: inherit; background: var(--brand-primary);
  transition: width var(--dur-slow) var(--ease-out); }

/* ---------------- TOOLTIP ---------------- */
.tooltip { position: relative; }
.tooltip::after {
  content: attr(data-tip); position: absolute; bottom: calc(100% + 7px); left: 50%;
  transform: translateX(-50%) translateY(4px); white-space: nowrap;
  background: #1c1029; color: #fff; font-size: var(--fs-xs); font-weight: var(--fw-normal);
  padding: 5px 9px; border-radius: 6px; box-shadow: var(--e-4);
  opacity: 0; visibility: hidden; pointer-events: none; z-index: var(--z-dropdown);
  transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.tooltip:hover::after, .tooltip:focus-visible::after {
  opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }

/* ---------------- TOAST ---------------- */
.toast-stack { position: fixed; right: var(--sp-4); bottom: var(--sp-4);
  z-index: var(--z-toast); display: flex; flex-direction: column; gap: var(--sp-2);
  max-width: min(360px, calc(100vw - 2 * var(--sp-4))); }
.toast { display: flex; align-items: flex-start; gap: 10px; padding: 12px 14px;
  border-radius: var(--r-card); background: var(--surface-1);
  border: 1px solid var(--border); border-left: 4px solid var(--brand-primary);
  box-shadow: var(--e-4); font-size: var(--fs-sm); color: var(--on-surface);
  animation: rq-toast-in var(--dur-base) var(--ease-out); }
.toast--success { border-left-color: var(--ok); }
.toast--error   { border-left-color: var(--danger); }
.toast--warn    { border-left-color: var(--warn); }
.toast__close { margin-left: auto; background: none; border: none; padding: 0 2px;
  color: var(--on-surface-muted); cursor: pointer; font-size: var(--fs-lg); line-height: 1; }
.toast__close:hover { background: none; color: var(--on-surface);
  transform: none; box-shadow: none; }
@keyframes rq-toast-in { from { opacity: 0; transform: translateY(10px); } }
/* Message + optional action (Add to Cart's "View cart", assets/js/cart-add.js). */
.toast { align-items: center; }
.toast__msg { flex: 1 1 auto; }
.toast__action { flex: 0 0 auto; font-weight: var(--fw-medium); color: var(--brand-strong);
  text-decoration: underline; text-underline-offset: 2px; white-space: nowrap; }
.toast__action:hover { color: var(--brand-primary); }
@media (max-width: 480px) {
  .toast-stack { left: var(--sp-3); right: var(--sp-3); bottom: var(--sp-3); max-width: none; }
}

/* ---------------- MODAL ---------------- */
.modal { position: fixed; inset: 0; z-index: var(--z-modal);
  display: flex; align-items: center; justify-content: center; padding: var(--sp-4);
  opacity: 0; visibility: hidden; transition: opacity var(--dur-base) var(--ease-out),
  visibility var(--dur-base) var(--ease-out); }
.modal.is-open { opacity: 1; visibility: visible; }
.modal__backdrop { position: absolute; inset: 0; background: rgba(16,3,30,.55);
  backdrop-filter: blur(2px); }
.modal__panel { position: relative; z-index: 1; width: 100%; max-width: 520px;
  max-height: calc(100vh - 2 * var(--sp-4)); overflow-y: auto;
  background: var(--surface-1); border-radius: var(--r-lg); box-shadow: var(--e-4);
  padding: var(--sp-5); transform: translateY(8px) scale(.99);
  transition: transform var(--dur-base) var(--ease-out); }
.modal.is-open .modal__panel { transform: translateY(0) scale(1); }
.modal__head { display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-3); margin-bottom: var(--sp-3); }
.modal__title { font-size: var(--fs-xl); margin: 0; }
.modal__close { background: none; border: none; cursor: pointer; padding: 2px;
  color: var(--on-surface-muted); line-height: 1; font-size: var(--fs-xl); }
.modal__close:hover { background: none; color: var(--on-surface);
  transform: none; box-shadow: none; }
.modal__foot { display: flex; justify-content: flex-end; gap: var(--sp-2);
  margin-top: var(--sp-4); flex-wrap: wrap; }

/* ---------------- CHART PANEL (owner/admin dashboards) ---------------- */
.chart-panel { background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r-card); box-shadow: var(--e-2); padding: var(--sp-4); }
.chart-panel__head { display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-3); margin-bottom: var(--sp-3); flex-wrap: wrap; }
.chart-panel__title { font-size: var(--fs-base); font-weight: var(--fw-semi); margin: 0; }
.chart-panel__body { position: relative; width: 100%; }
.chart-legend { list-style: none; padding: 0; margin: var(--sp-3) 0 0;
  display: flex; flex-direction: column; gap: 8px; font-size: var(--fs-sm); }
.chart-legend li { display: flex; align-items: center; gap: 8px; }
.chart-legend .swatch { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }
.chart-legend .val { margin-left: auto; color: var(--on-surface-muted);
  font-variant-numeric: tabular-nums; }

/* ---------------- FORM CONTROLS: SELECT / CHECK / RADIO ---------------- */
.select-wrap { position: relative; display: block; }
.select-wrap select { appearance: none; padding-right: 38px; }
.select-wrap::after { content: ''; position: absolute; right: 14px; top: 50%;
  width: 8px; height: 8px; margin-top: -6px; pointer-events: none;
  border-right: 2px solid var(--on-surface-muted); border-bottom: 2px solid var(--on-surface-muted);
  transform: rotate(45deg); }
.check, .radio { display: flex; align-items: center; gap: 9px; cursor: pointer;
  font-size: var(--fs-sm); font-weight: var(--fw-normal); margin-top: var(--sp-2); }
.check input, .radio input { width: 18px; height: 18px; flex: 0 0 auto;
  accent-color: var(--brand-primary); cursor: pointer; margin: 0; }

/* ---------------- SMALL UTILITIES (sparingly) ---------------- */
.u-flow > * + * { margin-top: var(--sp-3); }
.u-row { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.u-between { display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap; }
.u-muted { color: var(--on-surface-muted); }
.u-nums { font-variant-numeric: tabular-nums lining-nums; }
.u-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.u-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* =====================================================================
   PHASE 2 — SHARED CUSTOMER SHELL
   Nav / mobile nav / footer polish + the 320px overflow fix.
   ===================================================================== */

/* ---- THE 320px OVERFLOW FIX ----------------------------------------
   Root cause: the global `button` rule applies `padding: 12px 24px` to
   EVERY button. `.hamburger` and `.mobile-close` only reset background
   and border, so each was 74px wide instead of ~26px. Combined with the
   24px .nav-icons gaps that pushed the bar to 414px on a 320px screen.
   These buttons are icon-only -- they get icon-sized boxes. */
.hamburger, .mobile-close, .navlink {
  padding: 6px; border-radius: var(--r-sm); min-width: 0;
}
.hamburger:hover, .mobile-close:hover {
  background: var(--surface-2); color: var(--brand-strong);
  transform: none; box-shadow: none;                 /* kill the global lift */
}
.navlink:hover { transform: none; box-shadow: none; }
/* Phase 3: eight top-level items are back (Home / Browse Stores / Shop All /
   Best Sellers / New Arrivals / Categories / About / Contact). Slightly
   tighter type + padding keeps them on ONE row at >=1100px; below that the
   overlay menu takes over. */
.nav-links { gap: 0; }
.nav-links a, .nav-links .navlink {
  padding: 8px 7px;
  font-size: clamp(.74rem, .68rem + .18vw, .82rem);
  letter-spacing: .045em;
}

/* ---- NAV ICONS: fluid, never crowded ---- */
.nav-icons { gap: clamp(10px, 1.4vw, 20px); }
@media (max-width: 900px) {
  .nav-icons { margin-left: auto; padding-left: 0; border-left: none; }
}
@media (max-width: 420px) {
  .site-nav__bar { padding: 10px var(--sp-3); gap: var(--sp-2); }
  .brand { font-size: 1.25rem; }
  .nav-icons { gap: 8px; }
  .nav-icons svg { width: 21px; height: 21px; }
  /* Profile + owner/register shortcuts also live in the overlay menu,
     so on the narrowest phones drop them from the bar rather than
     letting the row overflow. Selector must out-specify `.nav-icons a`
     (0,1,1) -- a bare `.nav-icon-hideable` (0,1,0) silently loses. */
  .nav-icons a.nav-icon-hideable { display: none; }
}

/* Unread-notification indicator. public_nav.php has always rendered
   <span class="nav-dot"> but the class was never defined, so the dot
   was invisible on every page. */
.nav-dot {
  position: absolute; top: -2px; right: -2px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--danger); border: 2px solid var(--surface-1);
}

/* ---- ACTIVE STATE ----
   .active was set on 5 nav links but never styled -- there was no active
   state anywhere in the customer UI. */
.nav-links a.active, .nav-links a[aria-current="page"] {
  color: var(--brand-strong); font-weight: var(--fw-semi); position: relative;
}
.nav-links a.active::after, .nav-links a[aria-current="page"]::after {
  content: ''; position: absolute; left: 9px; right: 9px; bottom: 2px;
  height: 2px; border-radius: 2px; background: var(--brand-primary);
}
.nav-icons a.active, .nav-icons a[aria-current="page"] { color: var(--brand-primary); }

/* ---- HOVER / FOCUS POLISH ---- */
.nav-links a, .nav-links .navlink, .nav-icons a {
  transition: color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.site-nav a:focus-visible, .site-nav button:focus-visible,
.mobile-menu a:focus-visible, .mobile-menu button:focus-visible,
.site-footer a:focus-visible {
  outline: 2px solid var(--brand-primary); outline-offset: 3px; border-radius: 4px;
}
.site-footer a:focus-visible { outline-color: #fff; }
.dropdown__panel a { transition: background var(--dur-fast) var(--ease-out),
                                 color var(--dur-fast) var(--ease-out); }

/* Categories dropdown is data-driven now, so let it size to its content
   instead of the old fixed 3-column 520px mega panel. */
.dropdown__panel.mega {
  /* Cap as well as floor it: with only a min-width, auto-fit let a long
     category list expand the panel to 642px and push past the viewport
     at ~820px. Fixed width + wrapping rows keeps it on screen. */
  min-width: min(520px, calc(100vw - 32px));
  max-width: min(520px, calc(100vw - 32px));
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
}

/* Category names are entered by shop owners, so their casing is
   inconsistent in the database ("ballgowns", "Barong", "designer
   dresses"). Normalise it visually -- `capitalize` only raises the first
   letter of each word, so an acronym like "USA" survives intact. The
   stored data is untouched. */
.dropdown__panel.mega a, .mobile-menu a { text-transform: capitalize; }

/* ---- MOBILE OVERLAY ---- */
.mobile-menu { gap: 0; }
.mobile-menu a { transition: color var(--dur-fast) var(--ease-out),
                             padding-left var(--dur-fast) var(--ease-out); }
.mobile-menu a:hover, .mobile-menu a:focus-visible { padding-left: 10px; }
.mobile-menu__head { position: sticky; top: 0; background: var(--surface-1);
  padding-bottom: var(--sp-2); z-index: 1; }

/* ---- MOBILE COLLECTIONS ACCORDION ----
   Seven category links used to sit flat in the overlay and push the real
   destinations off-screen. <details>/<summary> gives a native, no-JS,
   keyboard-operable disclosure; these rules only make it match the
   surrounding links (same serif face, size, divider and 44px+ target). */
.mobile-acc { border-bottom: 1px solid var(--border); }
.mobile-acc__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-2); padding: 12px 4px; cursor: pointer;
  font-family: var(--font-serif); font-size: 1.25rem; color: var(--text-strong);
  list-style: none;                      /* kill the default disclosure marker */
  transition: color var(--dur-fast) var(--ease-out);
}
.mobile-acc__head::-webkit-details-marker { display: none; }   /* Safari/older Chrome */
.mobile-acc__head:hover { color: var(--brand-primary); }
.mobile-acc__head:focus-visible {
  outline: 2px solid var(--brand-primary); outline-offset: 3px; border-radius: 4px;
}
.mobile-acc__chev {
  width: 20px; height: 20px; flex: 0 0 auto;
  transition: transform var(--dur-fast) var(--ease-out);
}
.mobile-acc[open] .mobile-acc__chev { transform: rotate(180deg); }
/* Nested links: indented, one step smaller, and the last one drops its
   divider so it doesn't double up with .mobile-acc's own bottom border. */
.mobile-menu .mobile-acc__body a { padding-left: var(--sp-3); font-size: 1.05rem; }
.mobile-menu .mobile-acc__body a:last-child { border-bottom: none; }
.mobile-menu .mobile-acc__body a:hover,
.mobile-menu .mobile-acc__body a:focus-visible { padding-left: calc(var(--sp-3) + 10px); }

/* ---- FOOTER ----
   Replaces the inline styles the markup used to carry. */
.site-footer p { color: rgba(255,255,255,.72); font-size: var(--fs-sm); }
.site-footer__note { margin-top: 12px; color: rgba(255,255,255,.6);
  font-size: var(--fs-xs); }
.site-footer .socials a { display: inline-flex; padding: 6px; border-radius: var(--r-sm);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
.site-footer .socials a:hover { background: rgba(255,255,255,.10); }

/* ---- SMALL-SCREEN SPACING CONSISTENCY (320-480) ---- */
@media (max-width: 480px) {
  .container, .section { padding-left: var(--sp-3); padding-right: var(--sp-3); }
  .container { margin-top: var(--sp-4); margin-bottom: var(--sp-4); }
  h2 { font-size: var(--fs-xl); }
  h3 { font-size: var(--fs-lg); }
  .request-card { padding: var(--sp-3); }
  /* 2-up product grid instead of one oversized card per row */
  .store-grid, .product-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-2); }
  .product-card { padding: var(--sp-2); }
  .product-card h4, .pcard__title { font-size: var(--fs-sm); }
  .btn, button { padding: 11px 18px; }
}

/* ---- LARGE SCREENS (1600 / 1920 / 4K) ----
   Content stays centred at --maxw; only the hero gets more breathing room
   so it doesn't look letterboxed on very wide displays. */
@media (min-width: 1440px) {
  /* a little more room so the eight nav items breathe */
  .site-nav__bar { max-width: 1360px; }
}
@media (min-width: 1600px) {
  /* Very wide displays: hold the frame taller so the photograph stays
     immersive instead of letterboxing, and let the copy column grow with the
     wider container rather than stranding it in the far-left corner. */
  .hero { min-height: clamp(640px, 88vh, 1040px); --hero-col: min(660px, 46%); }
  .hero__inner, .section, .container, .site-footer__inner, .site-nav__bar {
    max-width: 1440px; }
}

/* =====================================================================
   ACCOUNT EXPERIENCE  —  auth/account.php + includes/auth_panel.php
   ---------------------------------------------------------------------
   One card, two sides, three stages. The whole interaction is driven by
   [data-stage] on .acct, so the animation is pure CSS and the markup
   never moves — no reflow, no layout shift, nothing to re-render.

   Desktop expands/collapses grid COLUMNS (1fr 1fr -> 1fr 0fr), which
   animates smoothly and keeps both sides in the DOM for screen readers
   and for the no-JS fallback. Below 860px the card stacks and the same
   stages drive max-height instead, since a 0fr column would leave the
   forms unreadably narrow on a phone.
   ===================================================================== */

.acct-page {
  min-height: 100vh; display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: var(--sp-3); padding: var(--sp-4) var(--sp-3);
  background:
    radial-gradient(1100px 520px at 12% -12%, rgba(153,85,255,.13), transparent 62%),
    radial-gradient(900px 460px at 92% 108%, rgba(133,50,236,.10), transparent 60%),
    var(--surface-2);
}
.acct-brand { font-size: 1.9rem; }
.acct-flash { max-width: 520px; width: 100%; text-align: center; margin: 0; }
.acct-foot { margin: 0; font-size: var(--fs-sm); color: var(--on-surface-muted); }

.acct { width: 100%; max-width: 1000px; }
.acct-card {
  display: grid; grid-template-columns: 1fr 1fr;
  min-height: 540px; overflow: hidden;
  border: 1px solid var(--border); border-radius: 20px;
  background: var(--surface-1); box-shadow: var(--e-4);
  transition: grid-template-columns var(--dur-slow) var(--ease-out);
}
.acct[data-stage="store"]    .acct-card { grid-template-columns: 1fr 0fr; }
.acct[data-stage="customer"] .acct-card { grid-template-columns: 0fr 1fr; }

.acct-side {
  position: relative; min-width: 0; overflow: hidden;
  display: flex; flex-direction: column; justify-content: center;
  transition: opacity var(--dur-base) var(--ease-out),
              transform var(--dur-slow) var(--ease-out);
}
/* The welcome side keeps the brand gradient; the renter side stays light,
   so the two account types read as different places at a glance. */
.acct-side--store {
  color: var(--text-on-dark);
  background: linear-gradient(150deg, #2A0B4E 0%, var(--brand-strong) 55%, #A466FF 100%);
}
.acct-side--customer { background: var(--surface-1); }

/* The side that is NOT chosen slides out of the way. */
.acct[data-stage="customer"] .acct-side--store    { opacity: 0; transform: translateX(-24px); }
.acct[data-stage="store"]    .acct-side--customer { opacity: 0; transform: translateX(24px);  }

/* ---- stage: choose ---- */
.acct-side__intro  { padding: var(--sp-6) var(--sp-5); }
/* The expanded side owns the full card, but a login form stretched to
   1000px reads as a spreadsheet -- keep the fields at a comfortable
   measure and centre them in the space instead. */
.acct-side__forms  {
  display: none; padding: var(--sp-5); overflow-y: auto; max-height: 100%;
  width: 100%; max-width: 440px; margin-inline: auto;
}
.acct[data-stage="store"]    .acct-side--store    .acct-side__intro,
.acct[data-stage="customer"] .acct-side--customer .acct-side__intro { display: none; }
.acct[data-stage="store"]    .acct-side--store    .acct-side__forms,
.acct[data-stage="customer"] .acct-side--customer .acct-side__forms {
  display: block; animation: acctIn var(--dur-slow) var(--ease-out) both;
}
@keyframes acctIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

.acct-eyebrow {
  display: inline-block; margin-bottom: var(--sp-2);
  font-size: var(--fs-xs); font-weight: var(--fw-semi);
  letter-spacing: .14em; text-transform: uppercase; opacity: .72;
}
.acct-side__title { font-size: var(--fs-2xl); margin-bottom: var(--sp-2); }
.acct-side__copy  { font-size: var(--fs-sm); line-height: var(--lh-normal); margin-bottom: var(--sp-4); opacity: .84; }
.acct-side--customer .acct-side__copy { color: var(--on-surface-muted); opacity: 1; }

.acct-choose {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: 12px 22px; border-radius: var(--r-pill);
  font-size: var(--fs-sm); font-weight: var(--fw-semi);
  border: 1px solid transparent; cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              gap var(--dur-fast) var(--ease-out);
}
.acct-choose svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 2; }
.acct-choose:hover { gap: 14px; }
.acct-side--store .acct-choose {
  background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.38); color: #fff;
}
.acct-side--store .acct-choose:hover { background: rgba(255,255,255,.24); color: #fff; }
.acct-side--customer .acct-choose { background: var(--brand-primary); color: #fff; }
.acct-side--customer .acct-choose:hover { background: var(--brand-strong); color: #fff; }

/* ---- stage: expanded ---- */
.acct-back {
  display: inline-flex; align-items: center; gap: 7px;
  margin-bottom: var(--sp-4); padding: 7px 12px 7px 8px;
  background: none; border: 1px solid transparent; border-radius: var(--r-pill);
  font-family: inherit; font-size: var(--fs-xs); font-weight: var(--fw-medium);
  color: inherit; opacity: .72; cursor: pointer;
  transform: none; box-shadow: none;              /* reset the global button lift */
}
.acct-back:hover { opacity: 1; transform: none; box-shadow: none; background: rgba(127,127,127,.10); }
.acct-back svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; }

.acct-switch {
  display: inline-flex; gap: 2px; padding: 3px; margin-bottom: var(--sp-4);
  border-radius: var(--r-pill); background: rgba(127,127,127,.12);
}
.acct-switch__btn {
  padding: 7px 18px; border-radius: var(--r-pill);
  font-size: var(--fs-xs); font-weight: var(--fw-semi); color: inherit; opacity: .66;
  transition: background var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.acct-switch__btn:hover { opacity: 1; color: inherit; }
.acct[data-mode-store="login"]       .acct-side--store    [data-mode-to="login"].acct-switch__btn,
.acct[data-mode-store="register"]    .acct-side--store    [data-mode-to="register"].acct-switch__btn,
.acct[data-mode-customer="login"]    .acct-side--customer [data-mode-to="login"].acct-switch__btn,
.acct[data-mode-customer="register"] .acct-side--customer [data-mode-to="register"].acct-switch__btn {
  opacity: 1; background: var(--surface-1); color: var(--on-surface); box-shadow: var(--e-1);
}

/* Only the active form of the active side is shown. */
.acct-form { display: none; }
.acct[data-mode-store="login"]       .acct-side--store    [data-form="login"],
.acct[data-mode-store="register"]    .acct-side--store    [data-form="register"],
.acct[data-mode-customer="login"]    .acct-side--customer [data-form="login"],
.acct[data-mode-customer="register"] .acct-side--customer [data-form="register"] {
  display: block; animation: acctIn var(--dur-base) var(--ease-out) both;
}

.acct-form__title { font-size: var(--fs-xl); margin-bottom: 4px; }
.acct-form__sub   { font-size: var(--fs-sm); opacity: .74; margin-bottom: var(--sp-4); }

.acct-field { margin-bottom: var(--sp-3); }
.acct-field label {
  display: block; margin-bottom: 6px;
  font-size: var(--fs-xs); font-weight: var(--fw-medium); letter-spacing: .01em; opacity: .82;
}
.acct-field input {
  width: 100%; padding: 12px 14px; margin: 0;
  border-radius: var(--r-sm); font-size: var(--fs-sm);
  border: 1px solid var(--border); background: var(--surface-1); color: var(--on-surface);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.acct-side--store .acct-field input {
  background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.26); color: #fff;
}
.acct-side--store .acct-field input::placeholder { color: rgba(255,255,255,.5); }
.acct-field input:focus {
  outline: none; border-color: var(--brand-primary);
  box-shadow: 0 0 0 3px rgba(153,85,255,.22);
}
.acct-side--store .acct-field input:focus {
  border-color: #fff; box-shadow: 0 0 0 3px rgba(255,255,255,.18);
}
/* Persistent format hint under a field (phone). Inherits the side's colour
   the same way .acct-field label does, so it reads on both the light
   customer panel and the dark store panel without a second rule. */
.acct-hint {
  display: block; margin-top: 6px;
  font-size: var(--fs-xs); line-height: var(--lh-snug); opacity: .7;
}
/* Register-form email check (script in includes/auth_panel.php). Same
   colours as .acct-errors on each side, so it reads on both panels. */
.acct-field-error {
  display: block; margin-top: 6px;
  font-size: var(--fs-xs); line-height: var(--lh-snug); color: var(--danger);
}
.acct-field-error[hidden] { display: none; }
.acct-side--store .acct-field-error { color: #FFD9DE; }
.acct-field input[aria-invalid="true"] { border-color: var(--danger); }
.acct-side--store .acct-field input[aria-invalid="true"] { border-color: #FFB3BD; }

.acct-submit {
  width: 100%; margin-top: var(--sp-3); padding: 13px;
  border: none; border-radius: var(--r-pill);
  font-size: var(--fs-sm); font-weight: var(--fw-semi); cursor: pointer;
}
.acct-side--customer .acct-submit { background: var(--brand-primary); color: #fff; }
.acct-side--customer .acct-submit:hover { background: var(--brand-strong); }
.acct-side--store .acct-submit { background: #fff; color: var(--brand-strong); }
.acct-side--store .acct-submit:hover { background: rgba(255,255,255,.88); }

.acct-alt { margin: var(--sp-3) 0 0; text-align: center; font-size: var(--fs-xs); opacity: .8; }
.acct-side--store .acct-alt a { color: #fff; text-decoration: underline; }
/* A submit button that reads as an inline link (resend verification). */
.acct-linkbtn {
  padding: 0; border: none; background: none; cursor: pointer;
  font: inherit; color: var(--brand-primary); text-decoration: underline;
}

.acct-note, .acct-errors {
  padding: 10px 13px; margin-bottom: var(--sp-3);
  border-radius: var(--r-sm); font-size: var(--fs-xs); line-height: var(--lh-snug);
}
.acct-note   { background: var(--info-bg); color: var(--info); }
.acct-errors { background: var(--danger-bg); color: var(--danger); }
.acct-errors p { margin: 0; }
.acct-errors p + p { margin-top: 4px; }

/* ---------------- PASSWORD RECOVERY (auth/forgot.php, auth/reset.php) ----------------
   These pages reuse .acct-card, which is a two-column grid built for the sign-in
   panels -- on a single card it split the title from the form. --single makes it one
   column. Buttons use the brand colour directly because the .acct-side--* colour
   rules do not apply outside the sign-in panels. */
.acct-card--single {
  display: block; min-height: 0; max-width: 440px; margin: 0 auto; padding: 32px;
}
.acct-card--single .acct-submit { display: block; text-align: center; text-decoration: none;
  background: var(--brand-primary); color: #fff; }
.acct-card--single .acct-submit:hover { background: var(--brand-strong); color: #fff; }
.acct-icon {
  display: flex; align-items: center; justify-content: center;
  width: 52px; height: 52px; margin-bottom: var(--sp-3);
  border-radius: 50%; background: var(--brand-weak); color: var(--brand-strong);
}
.acct-icon svg { width: 26px; height: 26px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
/* "Sent to ab***@gmail.com" -- the masked address the visitor typed, with a way out. */
.acct-sentto {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px var(--sp-2);
  padding: 10px 13px; margin-bottom: var(--sp-3);
  border: 1px solid var(--border); border-radius: var(--r-sm); font-size: var(--fs-xs);
}
/* The link drops to its own line before the address is split; a very long
   address may still break, but only as a last resort. */
.acct-sentto strong { overflow-wrap: anywhere; }
.acct-sentto a { flex: 0 0 auto; }
/* Secondary action (Resend). Resets the global purple button look. */
.acct-card--single .acct-resend {
  width: 100%; margin-top: var(--sp-2); padding: 11px;
  border: 1px solid var(--border); border-radius: var(--r-pill);
  background: transparent; color: var(--brand-strong);
  font-size: var(--fs-sm); font-weight: var(--fw-semi); cursor: pointer;
  transform: none; box-shadow: none;
}
.acct-card--single .acct-resend:hover { background: var(--brand-weak); color: var(--brand-strong);
  border-color: var(--brand-primary); transform: none; box-shadow: none; }
.acct-card--single .acct-resend:disabled,
.acct-card--single .acct-resend[disabled] { opacity: .6; cursor: default; background: transparent; }

/* One-time code: ONE real input (paste, one-time-code autofill, backspace and screen
   readers all work natively) laid transparently over six decorative boxes. */
.acct-otp { position: relative; }
/* Two classes (+ :focus) so this outranks the generic `.acct-field input` look. */
.acct-otp .acct-otp__input,
.acct-otp .acct-otp__input:focus {
  position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%;
  margin: 0; padding: 0; border: 0; outline: none; box-shadow: none; cursor: text;
  background: transparent; color: transparent; caret-color: transparent;
  font-size: 16px;   /* 16px stops iOS zooming the page on focus */
}
.acct-otp .acct-otp__input::selection { background: transparent; }
.acct-otp__boxes { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; }
.acct-otp__boxes span {
  display: flex; align-items: center; justify-content: center; height: 56px;
  border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--surface-1);
  font-size: 24px; font-weight: var(--fw-semi); font-variant-numeric: tabular-nums;
  color: var(--on-surface);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.acct-otp__boxes span.is-filled { border-color: var(--brand-primary); }
.acct-otp:focus-within .acct-otp__boxes span.is-active {
  border-color: var(--brand-primary); box-shadow: 0 0 0 3px rgba(153,85,255,.22);
}
.acct-otp.is-invalid .acct-otp__boxes span { border-color: var(--danger); }
@media (max-width: 480px) {
  .acct-card--single { padding: 24px 18px; border-radius: 16px; }
  .acct-otp__boxes { gap: 6px; }
  .acct-otp__boxes span { height: 48px; font-size: 20px; }
}
.acct-side--store .acct-note,
.acct-side--store .acct-errors { background: rgba(0,0,0,.26); color: #FFD9DE; }
.acct-side--store .acct-note   { color: #EADFFF; }

/* ---- stacked layout: the fr-column trick doesn't work on a phone ---- */
@media (max-width: 860px) {
  .acct-page { justify-content: flex-start; padding-top: var(--sp-5); }
  .acct-card,
  .acct[data-stage="store"] .acct-card,
  .acct[data-stage="customer"] .acct-card {
    grid-template-columns: 1fr; min-height: 0;
  }
  .acct-side {
    max-height: 70vh;
    transition: max-height var(--dur-slow) var(--ease-out),
                opacity var(--dur-base) var(--ease-out);
  }
  /* Collapse rather than slide sideways — a horizontal exit would push
     the page wider than the viewport. */
  .acct[data-stage="customer"] .acct-side--store,
  .acct[data-stage="store"] .acct-side--customer {
    max-height: 0; opacity: 0; transform: none;
  }
  .acct[data-stage="store"] .acct-side--store,
  .acct[data-stage="customer"] .acct-side--customer { max-height: none; }
  .acct-side__intro { padding: var(--sp-5) var(--sp-4); }
  .acct-side__forms { padding: var(--sp-4); }
  .acct-side__title { font-size: var(--fs-xl); }
}
@media (max-width: 420px) {
  .acct-page { padding: var(--sp-4) 12px; }
  .acct-card { border-radius: 14px; }
  .acct-side__intro, .acct-side__forms { padding: var(--sp-4) var(--sp-3); }
  .acct-switch__btn { padding: 7px 14px; }
}

/* =====================================================================
   NOTIFICATIONS  —  includes/notification_card.php, the navbar dropdown,
   and customer/notifications.php
   ---------------------------------------------------------------------
   One card component serves both the dropdown and the inbox; [data-accent]
   picks the icon tint. Accents are five muted families (positive /
   attention / negative / info / neutral) rather than a colour per type —
   seventeen bright colours in one feed is the opposite of premium.
   ===================================================================== */

/* ---- accent families: only the icon carries colour ---- */
.ncard {
  --nc-fg: var(--on-surface-muted);
  --nc-bg: var(--surface-3);
}
.ncard[data-accent="positive"]  { --nc-fg: var(--ok);            --nc-bg: var(--ok-bg); }
.ncard[data-accent="attention"] { --nc-fg: var(--warn);          --nc-bg: var(--warn-bg); }
.ncard[data-accent="negative"]  { --nc-fg: var(--danger);        --nc-bg: var(--danger-bg); }
.ncard[data-accent="info"]      { --nc-fg: var(--info);          --nc-bg: var(--info-bg); }
.ncard[data-accent="neutral"]   { --nc-fg: var(--on-surface-muted); --nc-bg: var(--surface-3); }

/* ---- the card ---- */
.ncard {
  display: grid; grid-template-columns: auto 1fr auto; gap: var(--sp-3);
  align-items: start; padding: var(--sp-3);
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r-card);
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.ncard:hover { border-color: #DCD3EC; box-shadow: var(--e-2); }

/* Unread: a tinted surface plus a left rail. Deliberately not a bright
   badge on every row — in a long feed that reads as noise. */
.ncard.is-unread {
  background: linear-gradient(90deg, var(--brand-weak) 0 3px, rgba(153,85,255,.045) 3px);
  border-color: #E3D6FA;
}

.ncard__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--nc-bg); color: var(--nc-fg);
}
.ncard__icon svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.9;
                   stroke-linecap: round; stroke-linejoin: round; }

.ncard__body { min-width: 0; }
.ncard__label {
  margin: 0 0 2px; font-size: var(--fs-xs); font-weight: var(--fw-semi);
  letter-spacing: .04em; text-transform: uppercase; color: var(--nc-fg);
}
.ncard__msg {
  margin: 0; font-size: var(--fs-sm); line-height: var(--lh-snug); color: var(--on-surface);
  overflow-wrap: anywhere;                 /* long product names can't overflow */
}
.ncard.is-unread .ncard__msg { font-weight: var(--fw-medium); }
.ncard__meta {
  display: flex; align-items: center; gap: var(--sp-2);
  margin: 6px 0 0; font-size: var(--fs-xs); color: var(--on-surface-subtle);
}
.ncard__new {
  padding: 1px 7px; border-radius: var(--r-pill);
  background: var(--brand-primary); color: #fff;
  font-size: 10px; font-weight: var(--fw-semi); letter-spacing: .04em; text-transform: uppercase;
}

.ncard__actions { display: flex; align-items: center; gap: var(--sp-2); white-space: nowrap; }
.ncard__actions form { margin: 0; }
.ncard__open, .ncard__mark {
  padding: 6px 12px; border-radius: var(--r-pill);
  font-family: inherit; font-size: var(--fs-xs); font-weight: var(--fw-medium);
  border: 1px solid var(--border); background: var(--surface-1); color: var(--on-surface-muted);
  cursor: pointer; transform: none; box-shadow: none;   /* reset global button lift */
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.ncard__open:hover, .ncard__mark:hover {
  background: var(--surface-2); color: var(--on-surface); transform: none; box-shadow: none;
}
.ncard__open { border-color: var(--brand-primary); color: var(--brand-primary); }
.ncard__open:hover { background: var(--brand-weak); color: var(--brand-strong); }

/* ---- clickable card: a booking notification opens My Bookings ----
   The card is the hit area, not a button inside it. The anchor wraps the
   message (so its accessible name is the message) and its ::after stretches
   across the whole card -- which is how the card becomes clickable without
   nesting the Mark-as-read <form> inside an <a>. That form stays usable
   because .ncard__actions is positioned and comes later in the DOM, so it
   paints above the overlay.

   No new look: the card keeps the same border, padding and hover it already
   had. What is added is the pointer cursor and an underline on hover -- the
   ordinary signals that text is a link. */
.ncard--clickable { position: relative; cursor: pointer; }
.ncard__link { color: inherit; text-decoration: none; }
.ncard__link::after { content: ""; position: absolute; inset: 0; }
.ncard--clickable:hover .ncard__link { text-decoration: underline; }
.ncard--clickable .ncard__actions { position: relative; }

/* ---- compact variant: the dropdown ---- */
.ncard--compact {
  grid-template-columns: auto 1fr; gap: var(--sp-2);
  padding: 10px var(--sp-3); border: none; border-radius: 0; background: none;
}
.ncard--compact:hover { box-shadow: none; border-color: transparent; }
.ncard--compact.is-unread { border: none; }
.ncard--compact .ncard__icon { width: 32px; height: 32px; }
.ncard--compact .ncard__icon svg { width: 16px; height: 16px; }
.ncard--compact .ncard__msg {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;                        /* two lines max, no ragged panel */
}

/* =====================================================================
   NAVBAR DROPDOWN
   The feed is server-rendered with the page, so opening it is a class
   toggle: no fetch, no spinner, no layout shift.
   ===================================================================== */
.notif-menu { position: relative; display: inline-flex; }
.notif-menu__btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  padding: 0; width: 34px; height: 34px;
  background: none; border: none; color: inherit; cursor: pointer;
  transform: none; box-shadow: none;
}
.notif-menu__btn:hover { transform: none; box-shadow: none; color: var(--brand-primary); background: none; }
.notif-menu__btn svg { width: 21px; height: 21px; fill: none; stroke: currentColor; stroke-width: 1.6; }
.notif-menu__btn.active { color: var(--brand-primary); }
.notif-menu__badge { top: -2px; right: -2px; }

.notif-menu__panel {
  position: absolute; top: calc(100% + 12px); right: 0; width: 370px;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r-lg, 14px); box-shadow: var(--shadow-menu);
  z-index: var(--z-dropdown, 120);
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity var(--dur-fast) var(--ease-out),
              transform var(--dur-base) var(--ease-out),
              visibility var(--dur-base) var(--ease-out);
  overflow: hidden;
}
.notif-menu.open .notif-menu__panel { opacity: 1; visibility: visible; transform: translateY(0); }

.notif-menu__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  padding: var(--sp-3); border-bottom: 1px solid var(--border);
}
.notif-menu__head form { margin: 0; }
.notif-menu__title { font-size: var(--fs-sm); font-weight: var(--fw-semi); }
.notif-menu__allread {
  padding: 0; background: none; border: none; cursor: pointer;
  font-family: inherit; font-size: var(--fs-xs); color: var(--brand-primary);
  transform: none; box-shadow: none;
}
.notif-menu__allread:hover { color: var(--brand-strong); background: none; transform: none; box-shadow: none; text-decoration: underline; }

.notif-menu__list { max-height: 380px; overflow-y: auto; }
/* Prefixed with .nav-icons on purpose: the panel lives INSIDE .nav-icons,
   so `.nav-icons a { display: inline-flex }` (0,1,1) beats a bare
   `.notif-menu__item { display: block }` (0,1,0) and collapses these to
   content width. Same trap as .nav-icon-hideable above. */
.nav-icons .notif-menu__item { display: block; color: inherit; border-bottom: 1px solid var(--surface-2); }
.notif-menu__item:last-child { border-bottom: none; }
a.notif-menu__item:hover { background: var(--surface-2); color: inherit; }
.notif-menu__empty {
  margin: 0; padding: var(--sp-5) var(--sp-3);
  text-align: center; font-size: var(--fs-sm); color: var(--on-surface-muted);
}
.nav-icons .notif-menu__all {
  display: block; padding: 12px; text-align: center;
  border-top: 1px solid var(--border); background: var(--surface-2);
  font-size: var(--fs-xs); font-weight: var(--fw-semi);
}

/* =====================================================================
   INBOX PAGE  —  customer/notifications.php
   ===================================================================== */
.ninbox { padding-top: var(--sp-5); padding-bottom: var(--sp-6); max-width: 780px; }

.ninbox__head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-4);
}
.ninbox__head form { margin: 0; }
.ninbox__title { font-size: var(--fs-2xl); margin: 0; }
.ninbox__sub { margin: 4px 0 0; font-size: var(--fs-sm); color: var(--on-surface-muted); }
.ninbox__markall { width: auto; padding: 9px 16px; font-size: var(--fs-xs); }

.ninbox__tools {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-4);
}
.ninbox__filters { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.ninbox__filters .chip.is-active {
  background: var(--brand-primary); border-color: var(--brand-primary); color: #fff;
}

.ninbox__search { position: relative; display: flex; align-items: center; margin: 0; }
.ninbox__search svg {
  position: absolute; left: 11px; width: 16px; height: 16px;
  fill: none; stroke: var(--on-surface-subtle); stroke-width: 2; pointer-events: none;
}
.ninbox__search input {
  width: 230px; max-width: 100%; margin: 0; padding: 9px 12px 9px 34px;
  font-size: var(--fs-sm); border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--surface-1);
}
.ninbox__search input:focus {
  outline: none; border-color: var(--brand-primary); box-shadow: 0 0 0 3px rgba(153,85,255,.18);
}

.ninbox__group {
  margin: var(--sp-4) 0 var(--sp-2); font-size: var(--fs-xs); font-weight: var(--fw-semi);
  letter-spacing: .1em; text-transform: uppercase; color: var(--on-surface-subtle);
}
.ninbox__group:first-of-type { margin-top: 0; }
.ninbox__list { display: flex; flex-direction: column; gap: var(--sp-2); }

.ninbox__empty { padding: var(--sp-6) var(--sp-4); text-align: center; }
.ninbox__empty-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 62px; height: 62px; margin-bottom: var(--sp-3);
  border-radius: 50%; background: var(--surface-2); color: var(--on-surface-subtle);
}
.ninbox__empty-icon svg { width: 28px; height: 28px; fill: none; stroke: currentColor; stroke-width: 1.6; }
.ninbox__empty h2 { font-size: var(--fs-lg); margin-bottom: 6px; }
.ninbox__empty p { font-size: var(--fs-sm); color: var(--on-surface-muted); margin-bottom: var(--sp-4); }

.ninbox__pager {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-3);
  margin-top: var(--sp-5); font-size: var(--fs-sm);
}

/* ---- responsive ---- */
@media (max-width: 780px) {
  /* The panel is wider than a small phone; pin it to the viewport instead
     of letting an absolutely-positioned 370px box push the page sideways. */
  .notif-menu__panel {
    position: fixed; top: 64px; left: 12px; right: 12px; width: auto;
  }
  .notif-menu__list { max-height: 60vh; }
}
@media (max-width: 640px) {
  .ninbox__tools { align-items: stretch; }
  .ninbox__search, .ninbox__search input { width: 100%; }
  /* Actions drop under the message rather than squeezing it to a sliver. */
  .ncard { grid-template-columns: auto 1fr; }
  .ncard__actions { grid-column: 2; margin-top: var(--sp-2); }
}
@media (max-width: 420px) {
  .ninbox__head { align-items: flex-start; }
  .ninbox__title { font-size: var(--fs-xl); }
  .ncard { padding: 12px; gap: 10px; }
  .ncard__icon { width: 34px; height: 34px; }
}

/* =====================================================================
   ACCOUNT MENU (navbar, signed-in users)
   ---------------------------------------------------------------------
   Nothing on the public site used to say WHICH account you were signed in
   as, and Log Out lived only inside the mobile overlay. So a store owner or
   admin browsing the shop saw a plain profile icon that kept opening their
   dashboard, with no way to tell why and no way out. The icon now shows
   their initial and opens a menu naming the account, with Log Out in it.
   ===================================================================== */
.acctmenu { position: relative; display: inline-flex; }
.acctmenu__btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0;
  border: none; border-radius: 50%; cursor: pointer;
  background: var(--brand-primary); color: #fff;
  font-family: inherit; font-size: var(--fs-xs); font-weight: var(--fw-semi);
  transform: none; box-shadow: none;
  transition: background var(--dur-fast) var(--ease-out);
}
.acctmenu__btn:hover { background: var(--brand-strong); transform: none; box-shadow: none; }
.acctmenu__initial { line-height: 1; }

.acctmenu__panel {
  position: absolute; top: calc(100% + 12px); right: 0; min-width: 216px;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r-lg, 14px); box-shadow: var(--shadow-menu);
  z-index: var(--z-dropdown, 120); overflow: hidden;
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity var(--dur-fast) var(--ease-out),
              transform var(--dur-base) var(--ease-out),
              visibility var(--dur-base) var(--ease-out);
}
.acctmenu.open .acctmenu__panel { opacity: 1; visibility: visible; transform: translateY(0); }

.acctmenu__who {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--sp-3); border-bottom: 1px solid var(--border); background: var(--surface-2);
}
.acctmenu__name { font-size: var(--fs-sm); font-weight: var(--fw-semi); }
.acctmenu__role {
  font-size: 10px; font-weight: var(--fw-semi); letter-spacing: .08em;
  text-transform: uppercase; color: var(--on-surface-subtle);
}
/* .nav-icons prefix for the same specificity reason as .notif-menu__item. */
.nav-icons .acctmenu__panel a {
  display: block; padding: 11px var(--sp-3);
  font-size: var(--fs-sm); color: var(--on-surface);
  border-bottom: 1px solid var(--surface-2);
}
.acctmenu__panel a:last-child { border-bottom: none; }
.acctmenu__panel a:hover { background: var(--surface-2); color: var(--on-surface); }
.acctmenu__out { color: var(--danger) !important; font-weight: var(--fw-medium); }
.acctmenu__out:hover { background: var(--danger-bg) !important; color: var(--danger) !important; }

/* Same identity line at the top of the mobile overlay. */
.mobile-menu__who {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--sp-3); margin-bottom: var(--sp-2);
  background: var(--surface-2); border-radius: var(--r-card);
}
.mobile-menu__who strong { font-size: var(--fs-base); }
.mobile-menu__who span {
  font-size: 10px; font-weight: var(--fw-semi); letter-spacing: .08em;
  text-transform: uppercase; color: var(--on-surface-subtle);
}

@media (max-width: 420px) {
  /* The account avatar stays -- it is the only sign-out route on a phone
     outside the overlay, so it must not be hidden with the other icons. */
  .nav-icons .acctmenu { display: inline-flex; }
  .acctmenu__panel { right: -8px; }
}

/* ---------------- SHOW / HIDE PASSWORD ----------------
   The wrapper and button assets/js/password-toggle.js adds around every
   password field. The button sits inside the field; the field keeps its own
   look and gains room on the right. The global button rule above (purple
   pill, lift on hover) is reset here so the toggle reads as part of the
   input. Colour follows the field's surroundings, so it works on the light
   customer panel and the dark store panel alike. */
.rq-pw { position: relative; display: block; }
.rq-pw > input { padding-right: 46px; }
.rq-pw > input::-ms-reveal { display: none; }   /* Edge's own eye would double up */
.rq-pw .rq-pw__toggle {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  width: 36px; height: 36px; padding: 0; margin: 0;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--r-sm); background: none; box-shadow: none;
  color: inherit; cursor: pointer;   /* full strength: a faded thin outline was easy to miss */
}
.rq-pw .rq-pw__toggle:hover {
  transform: translateY(-50%); box-shadow: none; border: 0;
  background: rgba(127,127,127,.14); color: inherit; opacity: 1;
}
.rq-pw .rq-pw__toggle svg {
  width: 20px; height: 20px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
/* The brand-purple focus ring disappears on the purple store panel; white
   reads there, the same fix the footer links use. */
.acct-side--store .rq-pw .rq-pw__toggle:focus-visible { outline-color: #fff; }
/* Saved logins: the browser repaints an autofilled field light with dark text,
   overriding the store panel's translucent purple -- so the white eye (and its
   white ring) vanish. Follow the field instead. Separate rules on purpose: an
   unsupported pseudo-class would void a combined selector list. */
.acct-side--store .rq-pw > input:autofill + .rq-pw__toggle { color: var(--on-surface); }
.acct-side--store .rq-pw > input:-webkit-autofill + .rq-pw__toggle { color: var(--on-surface); }
.acct-side--store .rq-pw > input:autofill + .rq-pw__toggle:focus-visible { outline-color: var(--brand-primary); }
.acct-side--store .rq-pw > input:-webkit-autofill + .rq-pw__toggle:focus-visible { outline-color: var(--brand-primary); }

/* ---------------- SHOP CONTACT CHIPS ----------------
   includes/store_contact.php -- a store's phone + its own social links, on
   the Store page and the Product Detail page. Only set methods render. */
.store-contact { list-style: none; margin: var(--sp-2) 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.store-contact__link { display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--surface-1); color: var(--on-surface); font-size: var(--fs-sm);
  line-height: 1.2; text-decoration: none; font-variant-numeric: tabular-nums;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
.store-contact__link:hover { border-color: var(--brand-primary); color: var(--brand-strong); }
.store-contact__link svg { width: 16px; height: 16px; flex: 0 0 auto; fill: currentColor; }
.store-contact__link svg.is-stroke { fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round; }
.store-contact--compact { margin: 6px 0 8px; gap: 6px; }
.store-contact--compact .store-contact__link { padding: 4px 10px; font-size: var(--fs-xs); }

/* ---------------- REQUIREMENT DOCUMENT UPLOADS (owner/create_store.php) ----------------
   Slots enhanced by assets/js/doc-upload.js: preview card per file with
   Replace / Remove. Without the script the native file inputs still show. */
.rq-docgroup { margin: var(--sp-3) 0 0; padding: var(--sp-3); border: 1px solid var(--border);
  border-radius: var(--r-card); background: var(--surface); min-width: 0; }
.rq-docgroup legend { padding: 0 6px; font-weight: 600; font-size: .95rem; }
.rq-docgroup__hint { margin: 0 0 var(--sp-2); }
.rq-docgroup__grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
.rq-upload { min-width: 0; }
.rq-upload__label { margin-top: 0; }
.rq-upload__label small { font-weight: 400; }
/* Hidden but still in the form: it carries the files that get submitted. */
.rq-upload.is-enhanced .rq-upload__real { position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0); border: 0; opacity: 0; }
.rq-upload__count { margin: 0 0 var(--sp-2); font-size: .82rem; color: var(--text-muted); font-variant-numeric: tabular-nums lining-nums; }
.rq-upload__count.is-full { color: var(--brand-strong); font-weight: 600; }
.rq-upload__list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-2); }
.rq-upload__card { display: flex; align-items: center; gap: 12px; padding: 10px; border: 1px solid var(--border);
  border-radius: var(--r-sm); background: var(--surface-alt); min-width: 0; }
.rq-upload__thumb { flex: 0 0 56px; width: 56px; height: 56px; border-radius: var(--r-sm); overflow: hidden;
  background: #fff; border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; }
.rq-upload__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rq-upload__thumb.is-pdf { background: var(--danger-bg); color: var(--danger); font-weight: 700; font-size: .8rem; letter-spacing: .04em; }
.rq-upload__meta { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.rq-upload__name { font-size: .88rem; }
.rq-upload__file { font-size: .82rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rq-upload__info { font-size: .76rem; color: var(--text-muted); font-variant-numeric: tabular-nums lining-nums; }
.rq-upload__actions { flex: 0 0 auto; display: flex; gap: 6px; }
.rq-upload__btn { padding: 6px 12px; font-size: .8rem; gap: 6px; background: #fff; color: var(--text-strong); border: 1px solid var(--border); }
.rq-upload__btn:hover { background: var(--brand-weak); color: var(--brand-strong); border-color: var(--brand-primary); transform: none; box-shadow: none; }
.rq-upload__btn.is-danger { color: var(--danger); }
.rq-upload__btn.is-danger:hover { background: var(--danger-bg); color: var(--danger); border-color: var(--danger); }
.rq-upload__empty { width: 100%; flex-direction: column; gap: 4px; padding: var(--sp-3); border-radius: var(--r-sm);
  border: 1.5px dashed var(--border); background: var(--surface-alt); color: var(--brand-strong); font-weight: 600; text-align: center; }
.rq-upload__empty small { font-weight: 400; font-size: .78rem; }
.rq-upload__empty:hover { background: var(--brand-weak); color: var(--brand-strong); border-color: var(--brand-primary); transform: none; box-shadow: none; }
.rq-upload__add { margin-top: var(--sp-2); padding: 8px 16px; font-size: .85rem; background: transparent; color: var(--brand-primary); }
.rq-upload__add:hover { transform: none; }
.rq-upload__empty[hidden], .rq-upload__add[hidden] { display: none; }
.rq-upload__msg { margin: 6px 0 0; font-size: .8rem; }
.rq-upload__msg:empty { display: none; }
.rq-upload__msg.is-warn { color: var(--warn); }
.rq-upload__msg.is-error { color: var(--danger); font-weight: 600; }
.rq-upload.is-invalid .rq-upload__empty { border-color: var(--danger); background: var(--danger-bg); color: var(--danger); }
@media (max-width: 640px) {
  .rq-docgroup__grid { grid-template-columns: 1fr; }
  .rq-upload__card { flex-wrap: wrap; }
  .rq-upload__actions { width: 100%; }
  .rq-upload__btn { flex: 1 1 0; }
}
