/* Chère — base styles. Brand colors come in via CSS custom properties set inline per-request from Settings. */

:root {
  --color-primary: #b76e79;
  --color-primary-dark: #8f4e58;
  --color-accent: #e8c4c0;
  --color-bg: #fdf8f6;
  --color-text: #2b2320;
  --color-muted: #7a6f6a;
  --color-border: #ece1de;
  --radius: 10px;
  --radius-lg: 18px;
  /* Newsreader (display/headings) + Red Hat Text (body/UI) replace the previous
     Fraunces/Inter pairing sitewide (2026-09-30, Ian: wanted the site's
     typography to read closer to eviamalta.com's — a more luxurious feel).
     --font-logo keeps the ORIGINAL Fraunces for the Chère wordmark specifically
     (.brand/.footer-brand below) — Ian asked for the logo to be left alone. */
  --font-display: 'Newsreader', Georgia, 'Times New Roman', serif;
  --font-body: 'Red Hat Text', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-logo: 'Fraunces', Georgia, 'Times New Roman', serif;
  --font: var(--font-body);
  --shadow-sm: 0 1px 3px rgba(20, 12, 8, 0.06);
  --shadow-md: 0 10px 30px rgba(20, 12, 8, 0.08);
  --shadow-lg: 0 20px 50px rgba(20, 12, 8, 0.14);
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  font-family: var(--font-body);
  background: var(--color-bg);
  color: var(--color-text);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
h1, h2, h3, h4 { margin: 0 0 0.5em; font-weight: 600; letter-spacing: -0.01em; font-family: var(--font-display); }
p { margin: 0 0 1em; }
::selection { background: var(--color-primary); color: #fff; }

.container { max-width: 1180px; margin: 0 auto; padding: 0 20px; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
}

/* ---------- Scroll reveal ---------- */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.7s var(--ease), transform 0.7s var(--ease); }
.reveal.in-view { opacity: 1; transform: translateY(0); }
.reveal-1.in-view { transition-delay: 0.06s; }
.reveal-2.in-view { transition-delay: 0.12s; }
.reveal-3.in-view { transition-delay: 0.18s; }
.reveal-4.in-view { transition-delay: 0.24s; }

/* ---------- Storefront header ---------- */
.site-header {
  border-bottom: 1px solid var(--color-border);
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  position: sticky; top: 0; z-index: 30;
  transition: box-shadow 0.3s var(--ease);
}
.site-header.scrolled { box-shadow: 0 6px 24px rgba(20, 12, 8, 0.07); }
.site-header .bar {
  display: flex; align-items: center; justify-content: space-between;
  /* padding-top includes env(safe-area-inset-top) so the logo/nav/cart row clears
     the notch / Dynamic Island in the iOS app (a full-screen WKWebView) instead of
     rendering underneath it — same approach already used for .admin-shell. */
  padding: calc(16px + env(safe-area-inset-top)) 20px 16px; max-width: 1180px; margin: 0 auto;
  transition: padding 0.3s var(--ease);
}
.site-header.scrolled .bar { padding: calc(10px + env(safe-area-inset-top)) 20px 10px; }

.brand { display: flex; align-items: center; gap: 10px; font-size: 22px; font-weight: 700; font-family: var(--font-logo); color: var(--color-primary-dark); transition: opacity 0.2s ease; }
.brand:hover { opacity: 0.75; }
.brand img { height: 42px; width: auto; transition: height 0.3s var(--ease); }
.site-header.scrolled .brand img { height: 34px; }
.brand .tagline { font-size: 11px; font-weight: 500; letter-spacing: 0.04em; text-transform: uppercase; color: var(--color-muted); display: block; font-family: var(--font-body); }
.tagline-only { display: flex; flex-direction: column; justify-content: center; }

.main-nav { display: flex; gap: 28px; }
.main-nav a { position: relative; font-size: 14px; font-weight: 500; color: var(--color-text); padding: 6px 0; }
.main-nav a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--color-primary);
  transform: scaleX(0); transform-origin: right; transition: transform 0.3s var(--ease);
}
.main-nav a:hover::after, .main-nav a.active::after { transform: scaleX(1); transform-origin: left; }
.main-nav a:hover { color: var(--color-primary-dark); }

/* ---------- Category dropdown (desktop main-nav) ----------
   A category with subcategories wraps its top-level link in .nav-item, with a
   .nav-dropdown panel that opens on hover/focus — text-only, no photos, modeled on
   eviamalta.com's minimalist boutique nav. .nav-dropdown sits flush against the
   top-level link (top: 100%, no gap) so the mouse can travel from one to the other
   without the panel closing. The higher-specificity ".main-nav .nav-dropdown a"
   selectors below intentionally out-rank the plain ".main-nav a" rules above so the
   dropdown gets its own quieter hover style instead of the top-level underline. */
.nav-item { position: relative; display: flex; align-items: center; }
.nav-dropdown {
  position: absolute; top: 100%; left: 50%; transform: translate(-50%, 4px);
  min-width: 190px; background: #fff; border-radius: var(--radius); box-shadow: var(--shadow-lg);
  padding: 16px 20px; display: flex; flex-direction: column;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 0.22s var(--ease), transform 0.22s var(--ease), visibility 0.22s;
  z-index: 30;
}
.nav-item:hover .nav-dropdown, .nav-item:focus-within .nav-dropdown {
  opacity: 1; visibility: visible; pointer-events: auto; transform: translate(-50%, 0);
}
.main-nav .nav-dropdown a {
  position: static; font-size: 13px; font-weight: 400; color: var(--color-muted);
  padding: 7px 0; white-space: nowrap; transition: color 0.2s ease, padding-left 0.2s ease;
}
.main-nav .nav-dropdown a::after { content: none; }
.main-nav .nav-dropdown a:hover { color: var(--color-primary-dark); padding-left: 3px; }
.main-nav .nav-dropdown-viewall {
  font-weight: 600; color: var(--color-primary-dark); padding-bottom: 10px; margin-bottom: 6px;
  border-bottom: 1px solid var(--color-border);
}

.header-actions { display: flex; align-items: center; gap: 18px; font-size: 14px; }
.header-actions a:not(.cart-link):hover { color: var(--color-primary-dark); }
.cart-link { position: relative; font-weight: 600; display: flex; align-items: center; gap: 6px; transition: color 0.2s ease; }
.cart-link:hover { color: var(--color-primary-dark); }
.cart-link svg { transition: transform 0.25s var(--ease); }
.cart-link:hover svg { transform: scale(1.12) rotate(-4deg); }
.cart-count { background: var(--color-primary); color: #fff; border-radius: 999px; padding: 1px 7px; font-size: 11px; position: absolute; top: -8px; right: -12px; box-shadow: 0 0 0 2px #fff; }

.nav-toggle { display: none; flex-direction: column; justify-content: center; gap: 4px; width: 34px; height: 34px; border: none; background: none; cursor: pointer; padding: 0; }
.nav-toggle span { display: block; width: 22px; height: 2px; background: var(--color-text); border-radius: 2px; transition: transform 0.3s var(--ease), opacity 0.3s var(--ease); }
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.mobile-nav-backdrop {
  position: fixed; inset: 0; background: rgba(20, 12, 8, 0.35); z-index: 39;
  opacity: 0; pointer-events: none; transition: opacity 0.3s ease;
}
.mobile-nav-backdrop.open { opacity: 1; pointer-events: auto; }
.mobile-nav {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(78vw, 320px); background: #fff; z-index: 40;
  padding: 90px 28px 28px; box-shadow: -8px 0 30px rgba(20,12,8,0.15);
  transform: translateX(100%); transition: transform 0.35s var(--ease);
  display: flex; flex-direction: column; gap: 4px;
}
.mobile-nav.open { transform: translateX(0); }
.mobile-nav a { font-size: 17px; font-weight: 600; padding: 12px 4px; color: var(--color-text); font-family: var(--font-display); }
.mobile-nav hr { border: none; border-top: 1px solid var(--color-border); margin: 10px 0; }

/* A category with subcategories becomes a zero-JS <details> accordion in the
   off-canvas mobile nav (no hover on touch devices) — same disclosure pattern
   already used for the Reports page's daily-sales breakdown. */
.mobile-nav-category { padding: 0; }
.mobile-nav-category summary {
  font-size: 17px; font-weight: 600; padding: 12px 4px; color: var(--color-text); font-family: var(--font-display);
  cursor: pointer; list-style: none; display: flex; align-items: center; justify-content: space-between;
}
.mobile-nav-category summary::-webkit-details-marker { display: none; }
.mobile-nav-category summary::after {
  content: '+'; font-family: var(--font-body); font-size: 18px; font-weight: 400; color: var(--color-muted);
  transition: transform 0.2s ease;
}
.mobile-nav-category[open] summary::after { content: '−'; }
.mobile-nav-subcategories { display: flex; flex-direction: column; padding: 0 4px 10px 14px; }
.mobile-nav-subcategories a {
  font-size: 15px; font-weight: 500; font-family: var(--font-body); padding: 8px 0; color: var(--color-muted);
}
.mobile-nav-subcategories a:first-child { font-weight: 600; color: var(--color-text); margin-bottom: 2px; }

/* ---------- Buttons / forms ---------- */
.btn {
  display: inline-block; padding: 12px 22px; border-radius: 999px; border: 1px solid var(--color-primary);
  background: var(--color-primary); color: #fff; font-weight: 600; font-size: 14px; cursor: pointer;
  transition: transform 0.2s var(--ease), box-shadow 0.2s var(--ease), background 0.2s ease, border-color 0.2s ease;
  box-shadow: var(--shadow-sm);
}
.btn:hover { background: var(--color-primary-dark); border-color: var(--color-primary-dark); transform: translateY(-2px); box-shadow: var(--shadow-md); }
.btn:active { transform: translateY(0) scale(0.98); box-shadow: var(--shadow-sm); }
.btn.secondary { background: #fff; color: var(--color-primary-dark); }
.btn.small { padding: 7px 14px; font-size: 13px; }
.btn.danger { background: #b23b3b; border-color: #b23b3b; }
.btn.ghost { background: transparent; border-color: var(--color-border); color: var(--color-text); box-shadow: none; }
.btn.ghost:hover { border-color: var(--color-primary); color: var(--color-primary-dark); background: transparent; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none !important; box-shadow: none !important; }

/* Admin keeps rectangular buttons for a more utilitarian feel */
.admin-main .btn, .admin-main .btn.small { border-radius: var(--radius); }

label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 4px; color: var(--color-muted); }
input, select, textarea {
  width: 100%; padding: 10px 12px; border: 1px solid var(--color-border); border-radius: 8px;
  font-size: 14px; font-family: var(--font-body); margin-bottom: 14px; background: #fff; color: var(--color-text);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 20%, transparent); }
.field-row { display: flex; gap: 12px; }
.field-row > * { flex: 1; }

/* ---------- Free delivery banner (homepage only, shown while
   free_delivery_enabled is true — see routes/storefront.js) ---------- */
.free-delivery-banner {
  background: var(--color-primary-dark);
  color: #fff;
  text-align: center;
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  padding: 10px 16px;
}

/* ---------- Cookie consent banner (2026-10-01, GA4 analytics — only ever
   rendered at all when Settings > Analytics has a GA4 Measurement ID saved; see
   lib/layout.js/public/consent.js) ---------- */
.cookie-consent-banner {
  display: none; /* flipped to flex by public/consent.js when a decision is needed */
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  background: #fff; border-top: 1px solid var(--color-border);
  padding: 14px 20px; gap: 16px; align-items: center; flex-wrap: wrap;
  box-shadow: 0 -2px 12px rgba(20, 12, 8, 0.1);
}
.cookie-consent-banner p { margin: 0; flex: 1 1 320px; font-size: 13.5px; color: var(--color-text); }
.cookie-consent-banner a { text-decoration: underline; }
.cookie-consent-actions { display: flex; gap: 10px; flex: 0 0 auto; }
@media (max-width: 520px) {
  .cookie-consent-banner { padding: 14px 16px; }
  .cookie-consent-actions { width: 100%; }
  .cookie-consent-actions .btn { flex: 1; }
}

/* ---------- Hero ---------- */
.hero {
  position: relative; overflow: hidden; padding: 110px 20px 100px; text-align: center;
  background: linear-gradient(135deg, var(--color-accent), var(--color-bg) 60%);
  background-size: 200% 200%;
}
/* Applied (see routes/storefront.js) only when the free-delivery banner is
   actually rendered above the hero — trims the hero's top padding by roughly
   the banner's own rendered height (~41px desktop, ~37px mobile below), so the
   video/content still ends at about the same spot on screen as it did with no
   banner, rather than the whole hero (and however much of the video shows
   above the fold) simply shifting down by the banner's height (2026-09-30,
   reported by Ian on his phone). Left off entirely when the banner is hidden
   (free_delivery_enabled off), so the hero keeps its normal, untrimmed padding. */
.hero.with-banner { padding-top: 69px; }
@media (prefers-reduced-motion: no-preference) {
  .hero { animation: heroGradient 14s ease-in-out infinite alternate; }
}
@keyframes heroGradient { from { background-position: 0% 0%; } to { background-position: 100% 100%; } }

.hero::before, .hero::after {
  content: ''; position: absolute; border-radius: 50%; filter: blur(50px); opacity: 0.5; z-index: 0;
  background: var(--color-primary);
}
.hero::before { width: 260px; height: 260px; top: -80px; left: -60px; }
.hero::after { width: 320px; height: 320px; bottom: -120px; right: -80px; background: var(--color-primary-dark); }
@media (prefers-reduced-motion: no-preference) {
  .hero::before { animation: float1 9s ease-in-out infinite; }
  .hero::after { animation: float2 11s ease-in-out infinite; }
}
@keyframes float1 { 0%,100% { transform: translate(0,0); } 50% { transform: translate(20px, 25px); } }
@keyframes float2 { 0%,100% { transform: translate(0,0); } 50% { transform: translate(-25px, -15px); } }

.hero > * { position: relative; z-index: 1; }
.hero .eyebrow { display: inline-block; font-size: 12px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--color-primary-dark); margin-bottom: 14px; opacity: 0; }
.hero h1 { font-size: clamp(38px, 6vw, 60px); color: var(--color-primary-dark); margin-bottom: 14px; opacity: 0; }
.hero p { color: var(--color-muted); max-width: 520px; margin: 0 auto 28px; font-size: 16px; opacity: 0; }
.hero .hero-cta { opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
  .hero .eyebrow { animation: fadeSlideUp 0.8s var(--ease) 0.05s forwards; }
  .hero h1 { animation: fadeSlideUp 0.8s var(--ease) 0.18s forwards; }
  .hero p { animation: fadeSlideUp 0.8s var(--ease) 0.32s forwards; }
  .hero .hero-cta { animation: fadeSlideUp 0.8s var(--ease) 0.46s forwards; }
}
@media (prefers-reduced-motion: reduce) {
  .hero .eyebrow, .hero h1, .hero p, .hero .hero-cta { opacity: 1; }
}
@keyframes fadeSlideUp { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: translateY(0); } }

/* ---------- Hero background video (optional, set in Settings) ---------- */
.hero.has-video { background: #0c1414; }
.hero.has-video::before, .hero.has-video::after { display: none; }
.hero-video, .hero-video-overlay { position: absolute; inset: 0; z-index: 0; }
.hero-video { width: 100%; height: 100%; object-fit: cover; }
.hero-video-overlay { background: linear-gradient(180deg, rgba(1,143,145,0.4), rgba(0,0,0,0.5)); }
.hero.has-video .eyebrow, .hero.has-video h1 { color: #fff; }
.hero.has-video p { color: rgba(255,255,255,0.9); }
.hero.has-video .hero-cta { background: #fff; border-color: #fff; color: var(--color-primary-dark); }
.hero.has-video .hero-cta:hover { background: var(--color-accent); border-color: var(--color-accent); }
@media (prefers-reduced-motion: reduce) {
  .hero-video { display: none; }
}

/* ---------- Section headings ---------- */
.section-heading { text-align: center; max-width: 560px; margin: 0 auto 40px; }
.section-heading h2 { font-size: 30px; margin-bottom: 8px; }
.section-heading p { color: var(--color-muted); margin: 0; }
.section { padding: 70px 0; }
.section.tight { padding: 40px 0; }

/* ---------- Category tiles ---------- */
.category-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 22px; }
.category-tile {
  position: relative; display: flex; align-items: flex-end; aspect-ratio: 4/3; border-radius: var(--radius-lg);
  padding: 22px; overflow: hidden; color: #fff; font-family: var(--font-display); font-size: 22px; font-weight: 600;
  transition: transform 0.4s var(--ease), box-shadow 0.4s var(--ease); box-shadow: var(--shadow-sm);
}
.category-tile:hover { transform: translateY(-6px) scale(1.015); box-shadow: var(--shadow-lg); }
.category-tile .tile-icon { position: absolute; top: 20px; right: 20px; opacity: 0.55; transition: transform 0.4s var(--ease); }
.category-tile:hover .tile-icon { transform: rotate(8deg) scale(1.08); }
.category-tile img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform 0.4s var(--ease); }
.category-tile:hover img { transform: scale(1.04); }
.category-tile span { position: relative; z-index: 1; }
.category-tile::after { content: ''; position: absolute; inset: 0; background: linear-gradient(180deg, transparent 40%, rgba(0,0,0,0.28)); }
.category-tile.c0 { background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark)); }
.category-tile.c1 { background: linear-gradient(135deg, #d9a441, #b5762a); }
.category-tile.c2 { background: linear-gradient(135deg, #6b8f71, #3f6247); }
.category-tile.c3 { background: linear-gradient(135deg, #7c7ab0, #4c4a7d); }

/* ---------- Product grid ---------- */
.filters-bar { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-end; padding: 14px 0; border-bottom: 1px solid var(--color-border); margin-bottom: 24px; }
.filters-bar form { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-end; width: 100%; }
.filters-bar label { margin-bottom: 4px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; }
.filters-bar input, .filters-bar select { margin-bottom: 0; min-width: 130px; }
.filters-bar .checkbox-field { display: flex; align-items: center; height: 41px; }
.result-count { color: var(--color-muted); font-size: 13px; margin-bottom: 16px; }

.product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 30px 26px; }
.product-card { display: block; }
.product-card .thumb {
  aspect-ratio: 3/4; background: linear-gradient(150deg, var(--color-accent), #fff 70%); border-radius: var(--radius);
  overflow: hidden; margin-bottom: 12px; position: relative;
  box-shadow: var(--shadow-sm); transition: box-shadow 0.35s var(--ease), transform 0.35s var(--ease);
}
.product-card:hover .thumb { box-shadow: var(--shadow-md); transform: translateY(-4px); }
.product-card .thumb img { width: 100%; height: 100%; object-fit: cover; position: absolute; inset: 0; transition: transform 0.5s var(--ease); }
.product-card:hover .thumb img { transform: scale(1.07); }
/* Hover-preview the second photo (desktop) / swipe-to-next-photo (touch),
   2026-09-30, Ian's request, styled after eviamalta.com — lives on every
   product card, so it applies equally to the home page's Featured grid and any
   /shop listing (both built from routes/storefront.js's shared productCard()).
   .thumb-img-2 only exists in the markup when a product actually has a second
   photo (see lib/catalog.js's image2 subquery) — a single-photo card has
   nothing here to trigger. Desktop is pure CSS (no JS needed); touch swipe is
   wired up in site.js's initProductCardSwap(), which toggles the "show-alt"
   class below since touch devices have no real hover to preview with. */
.thumb-img-2 { opacity: 0; z-index: 1; transition: opacity 0.4s ease, transform 0.5s var(--ease); }
@media (hover: hover) {
  .product-card:hover .thumb-img-2 { opacity: 1; }
}
.thumb.show-alt .thumb-img-2 { opacity: 1; }
@media (hover: none) {
  /* Claim horizontal drags for the swipe gesture in site.js; vertical page
     scroll under a shopper's thumb is left completely untouched. */
  .product-card .thumb { touch-action: pan-y; }
}
.product-card .thumb .placeholder-icon {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--color-primary);
  opacity: 0.4;
}
.badge { position: absolute; top: 10px; left: 10px; background: var(--color-primary); color: #fff; font-size: 11px; font-weight: 700; padding: 4px 10px; border-radius: 999px; text-transform: uppercase; letter-spacing: 0.03em; z-index: 2; }
.badge.out { background: #8a8a8a; }
.quick-add {
  position: absolute; left: 8px; right: 8px; bottom: 8px; text-align: center; padding: 9px 10px; border-radius: 999px;
  background: rgba(255,255,255,0.94); color: var(--color-primary-dark); font-size: 12px; font-weight: 700;
  transform: translateY(130%); transition: transform 0.3s var(--ease); backdrop-filter: blur(4px); z-index: 2;
}
.product-card:hover .quick-add { transform: translateY(0); }
.product-card .name { font-size: 14px; font-weight: 600; margin-bottom: 2px; transition: color 0.2s ease; }
.product-card:hover .name { color: var(--color-primary-dark); }
.product-card .price { font-size: 14px; color: var(--color-primary-dark); font-weight: 700; }
.product-card .price .was { color: var(--color-muted); text-decoration: line-through; font-weight: 400; margin-right: 6px; }

/* ---------- Product detail ---------- */
/* Mobile-only "Back" link above the product photo (2026-09-29, Ian's request) —
   without it, a phone shopper's only way off a product page is the header's
   Home link, which drops the shop/category filters, sort, and scroll position
   they had. This instead calls history.back() (see public/product.js) so it
   returns to that exact list as they left it; hidden on desktop, where the
   header's own nav is already visible and a back link would be redundant. */
.pdp-back-row { display: none; }
.pdp-back-link { display: inline-flex; align-items: center; gap: 4px; font-size: 14px; font-weight: 600; color: var(--color-primary-dark); }
.product-detail { display: grid; grid-template-columns: 1.1fr 1fr; gap: 50px; padding: 40px 0 60px; }
.gallery-main {
  aspect-ratio: 3/4; border-radius: var(--radius-lg); overflow: hidden; margin-bottom: 12px;
  background: linear-gradient(150deg, var(--color-accent), #fff 70%); position: relative; box-shadow: var(--shadow-sm);
}
.gallery-main img { width: 100%; height: 100%; object-fit: cover; transition: opacity 0.3s ease; }
.gallery-main .placeholder-icon { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--color-primary); opacity: 0.35; }
.gallery-thumbs { display: flex; gap: 10px; flex-wrap: wrap; }
.gallery-thumbs img {
  width: 68px; height: 88px; object-fit: cover; border-radius: 8px; cursor: pointer; opacity: 0.6;
  border: 2px solid transparent; transition: opacity 0.2s ease, border-color 0.2s ease;
}
.gallery-thumbs img:hover { opacity: 1; }
.gallery-thumbs img.active { opacity: 1; border-color: var(--color-primary); }

/* Prev/Next arrows on the main product photo (2026-09-28, Ian's request) — lets
   shoppers step through a product's photos without clicking each thumbnail one by
   one. Only rendered server-side when a product has more than one photo. */
.gallery-arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 40px; height: 40px; border-radius: 50%; border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, 0.85); color: var(--color-primary-dark);
  box-shadow: var(--shadow-sm); opacity: 0; transition: opacity 0.2s ease, background 0.2s ease;
}
.gallery-main:hover .gallery-arrow, .gallery-arrow:focus-visible { opacity: 1; }
.gallery-arrow:hover { background: #fff; }
.gallery-arrow.prev { left: 12px; }
.gallery-arrow.next { right: 12px; }
/* Touch devices have no hover to reveal the arrows on — show them outright there,
   same breakpoint the rest of this stylesheet already uses for touch/mobile. */
@media (hover: none) {
  .gallery-arrow { opacity: 1; }
}

.product-detail .price-row { font-size: 26px; font-family: var(--font-display); font-weight: 600; color: var(--color-primary-dark); margin-bottom: 18px; }
.swatches { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; }
.swatches .opt {
  border: 1px solid var(--color-border); border-radius: 999px; padding: 9px 16px; font-size: 13px; cursor: pointer; background: #fff;
  transition: transform 0.15s var(--ease), border-color 0.2s ease, background 0.2s ease, color 0.2s ease;
}
.swatches .opt:hover:not(.disabled) { border-color: var(--color-primary); transform: translateY(-1px); }
.swatches .opt.selected { border-color: var(--color-primary); background: var(--color-primary); color: #fff; font-weight: 700; }
.swatches .opt.disabled { opacity: 0.35; text-decoration: line-through; cursor: not-allowed; }
.stock-note { font-size: 13px; margin-bottom: 16px; color: var(--color-muted); }
.stock-note.in { color: #3b7a3b; }
.stock-note.low { color: #a06a1c; }
.stock-note.out { color: #b23b3b; }

.mobile-add-bar {
  display: none; position: fixed; left: 0; right: 0; bottom: 0; z-index: 25; background: #fff;
  border-top: 1px solid var(--color-border); padding: 12px 16px; align-items: center; justify-content: space-between; gap: 14px;
  box-shadow: 0 -8px 24px rgba(20,12,8,0.08);
}
.mobile-add-bar .price { font-weight: 700; color: var(--color-primary-dark); font-size: 15px; }
.mobile-add-bar .btn { flex: 1; text-align: center; }

@media (max-width: 760px) {
  .product-detail { grid-template-columns: 1fr; gap: 28px; }
  .main-nav { display: none; }
  .nav-toggle { display: flex; }
  .mobile-add-bar { display: flex; }
  body { padding-bottom: 74px; }
  .hero { padding: 70px 20px 60px; }
  .hero.with-banner { padding-top: 31px; }
  /* Two products per row on mobile instead of one long single-column list
     (2026-09-29, Ian's request) — a fixed 2-column grid rather than the
     auto-fill/minmax used above, since minmax(220px,1fr) can't fit two columns
     on a phone-width screen at all. Card text/badges/quick-add already sit at
     comfortable sizes down to this width, so nothing else needs to shrink. */
  .product-grid { grid-template-columns: repeat(2, 1fr); gap: 22px 14px; }
  .pdp-back-row { display: block; padding-top: 14px; padding-bottom: 4px; }
  /* Category/sort now auto-submit on change with no separate Apply button
     (2026-09-30, Ian's request) — on a phone the two selects can sit on one
     compact row side by side instead of each claiming the full width, so the
     filters bar stops eating vertical space above the product grid. */
  .filters-bar { padding: 10px 0; margin-bottom: 16px; }
  .filters-bar form { gap: 10px; }
  .filters-bar form > div { flex: 1 1 0; min-width: 0; }
  .filters-bar input, .filters-bar select { min-width: 0; width: 100%; }
}

/* iPad (744-1366px CSS width depending on model/orientation) sits above the
   760px phone breakpoint above, so the "‹ Back" link was falling through the
   gap entirely and staying hidden there (2026-09-30, Ian: it shows fine on
   iPhone but was missing on iPad). A plain max-width cutoff can't safely
   cover both iPad orientations though — iPad landscape runs up to ~1366px,
   which is also a completely ordinary laptop/desktop *window* width, and
   this link is deliberately hidden on desktop (the header's own nav is
   already visible there — see the comment above .pdp-back-row's base rule).
   (hover: none) and (pointer: coarse) is what actually distinguishes them:
   true for a touch-only iPad at any rotation, false for a mouse/trackpad
   laptop even sized to that exact same pixel width — already the same
   technique used for the product-card swipe handling above. Deliberately
   scoped to .pdp-back-row alone, not reused as a second general mobile
   breakpoint — iPad keeps its normal desktop-style nav and grid otherwise. */
@media (max-width: 1366px) and (hover: none) and (pointer: coarse) {
  .pdp-back-row { display: block; padding-top: 14px; padding-bottom: 4px; }
}

/* ---------- Cart / checkout ---------- */
.cart-table { width: 100%; border-collapse: collapse; margin-bottom: 24px; }
.cart-table th, .cart-table td { text-align: left; padding: 12px 10px; border-bottom: 1px solid var(--color-border); font-size: 14px; }
.cart-table img { width: 56px; height: 70px; object-fit: cover; border-radius: 6px; }
.cart-summary { max-width: 340px; margin-left: auto; }
.cart-summary .row { display: flex; justify-content: space-between; margin-bottom: 8px; font-size: 14px; }
.cart-summary .total { font-size: 18px; font-weight: 700; border-top: 1px solid var(--color-border); padding-top: 10px; }

.notice { background: var(--color-accent); border-radius: var(--radius); padding: 16px 20px; margin-bottom: 20px; font-size: 14px; }
.notice.success { background: #dff0d8; }
.notice.error { background: #f5d6d6; }

/* ---------- Admin ---------- */
/* padding-top: env(safe-area-inset-top) keeps the sidebar/topbar clear of the
   notch/Dynamic Island when this page is loaded inside the Capacitor mobile app
   (which renders edge-to-edge under the status bar) — it's a harmless 0px no-op
   in a regular desktop or Android browser, so it's safe to apply unconditionally. */
.admin-shell { display: flex; min-height: 100vh; padding-top: env(safe-area-inset-top); }
.admin-sidebar { width: 220px; background: #221a18; color: #f4ece9; padding: 22px 0; flex-shrink: 0; }
.admin-sidebar .brand { color: #fff; padding: 0 20px 20px; font-size: 18px; }
.admin-sidebar a { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 10px 20px; font-size: 14px; color: #d9c9c4; transition: background 0.15s ease, color 0.15s ease; }
.admin-sidebar a:hover, .admin-sidebar a.active { background: rgba(255,255,255,0.08); color: #fff; border-left: 3px solid var(--color-primary); padding-left: 17px; }
/* Live count badge on the Orders nav link (see lib/layout.js's adminLayout) — same
   pill shape as the storefront's cart-count bubble, just inline instead of absolutely
   positioned since it sits within the flex nav link rather than over an icon. */
.nav-badge { background: var(--color-primary); color: #fff; border-radius: 999px; padding: 1px 8px; font-size: 11px; font-weight: 700; line-height: 1.6; flex-shrink: 0; }
.admin-main { flex: 1; padding: 28px 34px; max-width: 1200px; }
.admin-topbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 22px; }
.admin-topbar h1 { font-size: 22px; margin: 0; }

.stat-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin-bottom: 28px; }
.stat-card { background: #fff; border: 1px solid var(--color-border); border-radius: var(--radius); padding: 18px; transition: box-shadow 0.2s ease; }
.stat-card:hover { box-shadow: var(--shadow-sm); }
.stat-card .num { font-size: 28px; font-weight: 700; color: var(--color-primary-dark); font-family: var(--font-display); }
.stat-card .label { font-size: 13px; color: var(--color-muted); }

table.data { width: 100%; border-collapse: collapse; background: #fff; border: 1px solid var(--color-border); border-radius: var(--radius); overflow: hidden; }
table.data th { text-align: left; background: #f7f0ee; font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-muted); padding: 10px 12px; }
table.data td { padding: 10px 12px; border-top: 1px solid var(--color-border); font-size: 14px; vertical-align: middle; }
table.data tr:hover td { background: #fbf6f5; }

.pill { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: 600; }
.pill.PENDING_REVIEW { background: #fde7c7; color: #8a5a10; }
.pill.CONFIRMED_AWAITING_PAYMENT { background: #d8e6fb; color: #274d8a; }
.pill.PAID { background: #dbe9d8; color: #2e6b2e; }
.pill.FULFILLED { background: #dcdcdc; color: #444; }
.pill.CANCELLED { background: #f5d6d6; color: #8a2e2e; }
.pill.low-stock { background: #f5d6d6; color: #8a2e2e; }
.pill.ACTIVE { background: #dbe9d8; color: #2e6b2e; }
.pill.SCHEDULED { background: #d8e6fb; color: #274d8a; }
.pill.EXPIRED { background: #dcdcdc; color: #444; }
.pill.PAUSED { background: #f5d6d6; color: #8a2e2e; }
.pill.USED_UP { background: #dcdcdc; color: #444; }

.card { background: #fff; border: 1px solid var(--color-border); border-radius: var(--radius); padding: 20px; margin-bottom: 20px; }
.card h2 { font-size: 16px; margin-bottom: 14px; }

.inline-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.inline-form input, .inline-form select { margin-bottom: 0; }

.site-footer { border-top: 1px solid var(--color-border); margin-top: 60px; padding: 48px 0 0; font-size: 13px; color: var(--color-muted); }
.site-footer .footer-brand { font-family: var(--font-logo); font-size: 22px; color: var(--color-primary-dark); margin-bottom: 6px; }
.footer-grid { display: grid; grid-template-columns: 1.2fr 0.65fr 0.75fr 0.9fr 1fr; gap: 28px; padding-bottom: 36px; }
.footer-col { display: flex; flex-direction: column; gap: 9px; }
.footer-col a { color: var(--color-muted); text-decoration: none; transition: color 0.15s var(--ease); }
.footer-col a:hover { color: var(--color-primary-dark); text-decoration: underline; }
.footer-col-title { font-weight: 600; color: var(--color-text); margin-bottom: 4px; letter-spacing: 0.02em; text-transform: uppercase; font-size: 11px; }
.footer-brand-col p { margin: 0 0 4px; }
.footer-social { display: inline-flex; align-items: center; gap: 7px; margin-top: 10px; color: var(--color-primary-dark); text-decoration: none; font-weight: 500; }
.footer-social:hover { text-decoration: underline; }
.app-badge { display: flex; align-items: center; gap: 8px; color: var(--color-muted); }
.app-badge svg { flex: none; opacity: 0.75; }
.app-badge small { color: var(--color-muted); opacity: 0.85; }
.footer-bottom { border-top: 1px solid var(--color-border); padding: 16px 0; text-align: center; font-size: 12px; }
.footer-credit { margin-top: 4px; font-size: 11px; color: var(--color-muted); opacity: 0.75; }

@media (max-width: 700px) {
  .footer-grid { grid-template-columns: 1fr; gap: 26px; text-align: left; }
}

.prose-page h2 { margin-top: 30px; font-size: 19px; }
.prose-page p { line-height: 1.7; color: var(--color-text); }

/* ---------- Sell (in-person sale entry) ---------- */
#sell-app { position: relative; }
.sell-results { position: relative; margin-top: 8px; display: none; flex-direction: column; border: 1px solid var(--color-border); border-radius: var(--radius); overflow: hidden; max-height: 340px; overflow-y: auto; }
.sell-results.open { display: flex; }
.sell-result-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; width: 100%; text-align: left; background: #fff; border: none; border-bottom: 1px solid var(--color-border); padding: 12px 14px; font-size: 14px; cursor: pointer; font-family: inherit; }
.sell-result-row:last-child { border-bottom: none; }
.sell-result-row:hover:not(:disabled) { background: #fbf6f5; }
.sell-result-row:disabled { opacity: 0.45; cursor: not-allowed; }
.sell-result-row.muted { cursor: default; color: var(--color-muted); }
.sell-result-main { flex: 1; }
.sell-result-meta { font-size: 13px; color: var(--color-muted); white-space: nowrap; }
#cart-table input { margin-bottom: 0; }

/* ---------- Admin: mobile/tablet responsiveness ---------- */
.admin-nav-toggle { display: none; }

@media (max-width: 880px) {
  .admin-topbar { gap: 10px; }
  .admin-nav-toggle {
    display: flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; border: 1px solid var(--color-border); border-radius: var(--radius);
    background: #fff; cursor: pointer; flex-direction: column; gap: 4px; padding: 0;
  }
  .admin-nav-toggle span { display: block; width: 20px; height: 2px; background: var(--color-text); border-radius: 2px; }

  .admin-sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; z-index: 50; width: min(78vw, 280px);
    transform: translateX(-100%); transition: transform 0.3s ease; overflow-y: auto;
    /* On mobile the sidebar becomes fixed (taken out of .admin-shell's normal flow), so
       it needs its own safe-area padding — .admin-shell's padding-top no longer reaches it. */
    padding-top: env(safe-area-inset-top);
  }
  .admin-sidebar.open { transform: translateX(0); }
  .admin-sidebar-backdrop {
    position: fixed; inset: 0; background: rgba(20, 12, 8, 0.35); z-index: 49;
    opacity: 0; pointer-events: none; transition: opacity 0.3s ease;
  }
  .admin-sidebar-backdrop.open { opacity: 1; pointer-events: auto; }

  /* The horizontal scroll lives on .admin-main itself (the one common ancestor for
     every admin page's content) rather than on individual tables, so normal table
     column layout is completely undisturbed — .admin-main just becomes a scrollable
     viewport, and only pages with a table wide enough to need it actually scroll. */
  .admin-main { padding: 18px 16px; max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .admin-main table.data { min-width: 640px; }

  .field-row { flex-direction: column; gap: 0; }
  .stat-cards { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }

  /* Every quick-entry row form (.inline-form — Transfer stock, Record a return, Add
     staff, Add location, Reserve a hold, Start a stock take, etc.) packs several
     inputs/selects/buttons onto one line. Above this breakpoint that's fine, but in
     portrait on a phone the row has no room to breathe: each control's inline
     max-width/width (set per-field in the page that renders it, e.g. "Variant SKU"
     capped at 160px) is what a wide desktop row wants, but on a narrow screen the row
     just shrinks everything down toward its minimum content size instead of ever
     wrapping (flex-wrap alone isn't enough, since these are fixed-width children, not
     ones that naturally wrap to a new line) — every field and button becomes too small
     to comfortably tap, which is exactly why turning the phone sideways (more available
     width) used to be the only way to use these forms comfortably. Stacking each
     control to its own full-width row instead removes that squeeze entirely, so nothing
     needs the extra width landscape orientation provided. The !important on width/
     max-width is required specifically to win over each field's own inline style
     attribute (inline styles otherwise always beat a stylesheet rule of any
     specificity) — scoped tightly to just this breakpoint and just .inline-form's
     direct children, so it can't affect anything above this width. */
  .inline-form { flex-direction: column; align-items: stretch; }
  .inline-form > * { width: 100% !important; max-width: none !important; }
  .inline-form > span { text-align: center; font-size: 18px; }

  .btn, .admin-sidebar a { min-height: 40px; display: flex; align-items: center; justify-content: center; }
  .admin-sidebar a { justify-content: space-between; }
  input, select, textarea, .btn { font-size: 16px; } /* prevents iOS auto-zoom on focus */
}
.prose-page p.text-muted { line-height: 1.6; }
.prose-page a { color: var(--color-primary-dark); }

.empty-state { text-align: center; padding: 60px 20px; color: var(--color-muted); }

.text-muted { color: var(--color-muted); font-size: 13px; }
.mb-0 { margin-bottom: 0; }
.flex-between { display: flex; justify-content: space-between; align-items: center; }

/* Homepage promo pop-up (see lib/promoCodes.js's getActivePopupPromo() and
   public/site.js's initPromoPopup()) — only ever present on the homepage, and
   only when a promo code is currently active with "show on homepage" turned on. */
.promo-popup-overlay {
  position: fixed; inset: 0; background: rgba(20, 12, 8, 0.45); z-index: 1000;
  display: flex; align-items: center; justify-content: center; padding: 20px;
  opacity: 0; pointer-events: none; transition: opacity 0.25s var(--ease);
}
.promo-popup-overlay.open { opacity: 1; pointer-events: auto; }
.promo-popup {
  position: relative; background: #fff; border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  max-width: 380px; width: 100%; padding: 32px 28px 28px; text-align: center;
  transform: translateY(14px) scale(0.98); transition: transform 0.25s var(--ease);
}
.promo-popup-overlay.open .promo-popup { transform: translateY(0) scale(1); }
@media (prefers-reduced-motion: reduce) {
  .promo-popup-overlay, .promo-popup { transition: none; }
}
.promo-popup-close {
  position: absolute; top: 10px; right: 10px; width: 32px; height: 32px; border-radius: 50%;
  border: none; background: transparent; color: var(--color-muted); font-size: 20px; line-height: 1;
  cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.promo-popup-close:hover { background: var(--color-bg); color: var(--color-text); }
.promo-popup-kicker { font-family: var(--font-body); font-size: 12px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-primary-dark); margin: 0 0 10px; }
.promo-popup-title { font-family: var(--font-display); font-size: 26px; line-height: 1.15; margin: 0 0 12px; color: var(--color-text); }
.promo-popup-body { font-size: 14px; line-height: 1.55; color: var(--color-text); opacity: 0.85; margin: 0 0 18px; }
.promo-popup-code { display: inline-block; background: var(--color-bg); border: 1px dashed var(--color-primary); border-radius: 8px; padding: 8px 16px; font-weight: 700; letter-spacing: 0.06em; color: var(--color-primary-dark); margin: 0 0 20px; }
.promo-popup .btn { width: 100%; }
