/* ============================================================
   Typeface — self-hosted, deliberately
   Cormorant Garamond used to be pulled from fonts.googleapis.com, which put
   a render-blocking request to a third-party origin in front of the first
   paint of every single page. Page-to-page that reads as a white gap: the
   browser has the HTML and cannot draw until a stylesheet arrives over the
   network. Self-hosted it is one 37KB file served from the same folder as
   the page, so it costs nothing on navigation and the site works with no
   internet connection at all — which matters while the site is still being
   reviewed from local files rather than a live domain.
   One file covers every weight: Google serves Cormorant Garamond as a
   variable font, and the 400/500/600/700 downloads were byte-identical.
   ============================================================ */
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 300 700;          /* variable axis, not four separate faces */
  font-display: swap;
  src: url('fonts/cormorant-garamond-var.woff2') format('woff2');
  /* Latin only. Every glyph the site uses sits in this range, accented brand
     names included (Herm\00E8s, Lanc\00F4me, Av\00E8ne, K\00E9rastase, Decort\00E9). */
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}

/* ============================================================
   CravePact Pte Ltd — Design Tokens
   Adapted from the Grammarly design system (styles.refero.design)
   Near-white/near-achromatic canvas + one confident accent, flat
   surfaces, hairline borders, 8px shape system throughout.
   ============================================================ */

:root {
  /* Brand accent — antique-gold tone taken from the owner's business card
     (2026-09-02). The card's own ink reads lighter/more golden than this at
     roughly #a3805a, but --color-accent is also used as body-size text on
     white (nav links, "View ... brands" arrows) and as a full-bleed CTA-band
     background under white paragraph text — both need >=4.5:1 contrast, and
     #a3805a only clears ~3.6:1. This is the closest gold to the card that
     still passes AA everywhere the token is already used. Previous value,
     sampled from the wordmark instead of the card: #8f5c30 / #744a26. */
  --color-accent: #8e6b2c;
  --color-accent-hover: #735623;
  --color-accent-tint: #f3ece3;

  /* Richer, more golden accent for details sitting directly on
     --color-carbon (currently just the .glyph-tile bullets and eyebrow).
     --color-accent is held back by needing >=4.5:1 as body text on WHITE;
     against carbon (near-black) that same 4.5:1 floor is reached by almost
     any warm color, so there is no reason to use the muted one here too.
     2026-09-02: added because the accent read muddy against carbon compared
     to everywhere else it's used on paper. */
  --color-gold-bright: #c69a44;

  /* Neutrals */
  --color-ink: #1a140e;       /* headings, highest emphasis */
  --color-body: #362d23;      /* body copy */
  --color-muted: #6d6055;     /* secondary/helper text */
  --color-steel: #8f8478;     /* tertiary/meta text */
  --color-silver: #bdb2a4;    /* disabled, placeholder */
  --color-border: #e4dcd0;    /* dividers, hairlines */
  --color-border-light: #eee8de;
  --color-surface: #f8f5f0;   /* subtle alternating section bg */
  --color-carbon: #211a12;    /* dark footer / accent-adjacent band */
  --color-paper: #ffffff;

  /* Typography
     2026-09-02: single serif across the whole site, on the owner's
     instruction and matching his business card. Manrope and Inter are gone.
     All three tokens point at the same family so any rule still asking for
     "display" or "body" resolves correctly. */
  --font-serif: 'Cormorant Garamond', 'Times New Roman', Georgia, serif;
  --font-display: var(--font-serif);
  --font-body: var(--font-serif);

  /* Cormorant has a markedly smaller x-height than the sans faces it
     replaced, so every step is set larger than its sans equivalent would be.
     The small end moves most: 12px caption -> 14px, 14px UI -> 15px. Below
     about 14px this face goes thin and loses its serifs on Windows. */
  --text-caption: 14px;
  --text-body-sm: 15px;
  --text-body: 18px;
  --text-subheading: 21px;
  --text-heading-sm: 24px;
  --text-heading: clamp(2rem, 1.7rem + 1.4vw, 2.6rem);
  --text-heading-lg: clamp(2.4rem, 1.9rem + 2.2vw, 3.25rem);
  --text-display: clamp(2.75rem, 2rem + 3.4vw, 3.9rem);

  /* Spacing (8px base unit) */
  --sp-8: 8px;
  --sp-16: 16px;
  --sp-24: 24px;
  --sp-32: 32px;
  --sp-40: 40px;
  --sp-48: 48px;
  --sp-64: 64px;
  --sp-80: 80px;
  --sp-96: 96px;

  /* Layout */
  --page-max: 1200px;
  --section-gap: var(--sp-80);
  --card-padding: var(--sp-32);

  /* Shape */
  --radius: 8px;

  /* Motion */
  --ease-out: cubic-bezier(.2, .8, .2, 1);
}

/* ============================================================
   Motion — page transitions
   Cross-document view transitions. Chromium + recent Safari get a
   cross-fade with a persistent header; every other browser simply
   navigates as normal with nothing broken.
   ============================================================ */
@view-transition { navigation: auto; }

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 260ms;
  animation-timing-function: var(--ease-out);
}

/* The header is the one part that must not cross-fade with the body: it is
   geometrically identical on all four pages (77px, stuck to the top), so
   naming it holds it perfectly still while the "current page" underline
   cross-fades inside it. The group animation is given the same duration as
   the root so the two halves of the transition stay in step — left at the
   UA default they run 250ms against the root's 260ms.

   2026-09-02: the footer used to be named here too, and that was a mistake.
   It sits 944-4598px down depending on the page, so its group animated a
   365px-tall full-width snapshot across up to 3.6k px on every navigation,
   while never being on screen to benefit from it. It is byte-identical on
   all four pages, so cross-fading it as part of the root is seamless and
   free. */
.site-header { view-transition-name: site-header; }
::view-transition-group(site-header) {
  animation-duration: 260ms;
  animation-timing-function: var(--ease-out);
}

/* ------------------------------------------------------------
   Fallback page transition — html.pt
   The native transition above needs a real http(s) origin. Opened straight
   from disk over file://, every page counts as its own opaque origin and the
   browser skips the transition entirely, so pages snap with nothing joining
   them; browsers without support do the same. The inline script in each
   <head> detects both cases and sets html.pt, and this takes over.

   Only <main> moves, so the header stays put exactly as it does under the
   native transition. The fade-in is an animation rather than a transition
   on purpose: an animation always runs to its end state on its own, so the
   page cannot get stuck invisible if the script that fades it out never
   runs. `both` holds the opening frame, so there is no flash of full-
   opacity content before it starts.
   ------------------------------------------------------------ */
@keyframes pt-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
html.pt main { animation: pt-in 280ms var(--ease-out) both; }
html.pt.pt-leaving main {
  animation: none;
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 150ms var(--ease-out), transform 150ms var(--ease-out);
}

/* ============================================================
   Motion — scroll reveal
   Every reveal runs exactly once and then stays put (bar.md #4:
   motion resolves, it does not idle-loop). Styles are scoped to
   html.js so content is never hidden if JS fails to run.
   ============================================================ */
.js .reveal {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity .5s var(--ease-out), transform .5s var(--ease-out);
}
.js .reveal.is-visible { opacity: 1; transform: none; }

/* Already on screen when the page opened — render settled, do not animate.
   Anything inside the first viewport used to fade up from opacity 0 on every
   single arrival: measured at 0.00 opacity / 12px low at first paint, still
   moving ~500ms later, i.e. ~200ms after the page transition had already
   finished. Two motions of different lengths overlapping is what read as a
   jerk when changing tabs. Scroll reveals are for content you scroll to. */
.js .reveal.is-instant,
.js .reveal-stagger.is-instant > * {
  opacity: 1;
  transform: none;
  transition: none;
  transition-delay: 0s;
}

/* Staggered children — each child offset by its index */
.js .reveal-stagger > * {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity .45s var(--ease-out), transform .45s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 45ms);
}
.js .reveal-stagger.is-visible > * { opacity: 1; transform: none; }

/* Fade-only stagger — for grids whose 1px hairline gaps would tear
   if their cells translated (the logo wall) */
.js .reveal-stagger.fade-only > * { transform: none; }

@media (prefers-reduced-motion: reduce) {
  /* Nothing to undo for scrolling here: scroll-behavior is never set to
     smooth in CSS, and main.js checks this same query before it smooth-
     scrolls an anchor click. */
  .js .reveal,
  .js .reveal-stagger > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
  /* Same for the file:// fallback. main.js also checks this query before it
     delays a navigation to fade out, so nothing is held up either. */
  html.pt main { animation: none !important; }
  html.pt.pt-leaving main {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .js .glyph-tile.in-view .pg-drift,
  .js .glyph-tile.in-view .lb-route-done,
  .js .glyph-tile.in-view .lb-runner,
  .js .glyph-tile.in-view .lb-landed,
  .js .glyph-tile.in-view [class*="lb-market-"] { animation: none !important; }
}

/* ============================================================
   Reset & base
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }
/* Deliberately NOT `scroll-behavior: smooth`. As a global rule it also
   applies to the scroll the browser performs when a navigation lands on a
   fragment, so arriving at catalog.html#fragrance animated ~1040px over
   ~700ms on top of the 260ms page transition — the page appeared, then crept
   downwards. Landing should be instant; smoothness belongs only to anchor
   clicks made while already on the page, which main.js handles (and which
   honours prefers-reduced-motion there). */
body {
  margin: 0;
  background: var(--color-paper);
  color: var(--color-body);
  font-family: var(--font-body);
  font-size: var(--text-body);
  /* Looser than the 1.5 the sans used. A small-x-height serif needs more
     leading or the lines knit together and long paragraphs get hard to
     track. */
  line-height: 1.65;
  /* Oldstyle figures sit below the baseline and read as a defect in UI
     contexts (prices, dates, unit numbers, years). Lining figures site-wide. */
  font-variant-numeric: lining-nums;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, h4 {
  font-family: var(--font-display);
  /* 600, not the 800 Manrope used. Cormorant's heaviest weight is 700 and
     it is far lighter than a geometric sans at the same number; 600 gives
     presence without the strokes clogging at display sizes. */
  font-weight: 600;
  color: var(--color-ink);
  margin: 0 0 var(--sp-16);
  /* Serif headings need more room than the sans's 1.12, and the negative
     tracking that suited Manrope closes up Cormorant's fine joins. */
  line-height: 1.18;
  letter-spacing: 0;
}
p { margin: 0 0 var(--sp-16); color: var(--color-body); }
ul { margin: 0; padding: 0; list-style: none; }
button { font-family: inherit; cursor: pointer; }

.wrap {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 var(--sp-24);
}

section { padding: var(--section-gap) 0; }
section.tight { padding: var(--sp-48) 0; }

/* Small caps labels are where this face is weakest, so they carry the
   heaviest weight available plus generous tracking. */
.eyebrow {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--text-caption);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin: 0 0 var(--sp-16);
  display: block;
}

.section-head {
  max-width: 720px;
  margin: 0 0 var(--sp-48);
}
.section-head.center { margin-left: auto; margin-right: auto; text-align: center; }
.section-head h2 { font-size: var(--text-heading); }
.section-head p { color: var(--color-muted); font-size: var(--text-subheading); line-height: 1.45; margin: 0; }

/* ============================================================
   Buttons
   ============================================================ */
/* Buttons carry weight 700 and light tracking so the label still reads as a
   solid, tappable object rather than as fine text sitting on a colour. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 13px 26px;
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--text-body);
  letter-spacing: 0.04em;
  line-height: 1.2;
  border: 1px solid transparent;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
  white-space: nowrap;
}
.btn-primary { background: var(--color-accent); color: #fff; }
.btn-primary:hover { background: var(--color-accent-hover); }
.btn-outline { background: transparent; color: var(--color-ink); border-color: var(--color-ink); }
.btn-outline:hover { background: var(--color-ink); color: #fff; }
.btn-ghost { background: transparent; color: #fff; border-color: rgba(255,255,255,.6); }
.btn-ghost:hover { background: rgba(255,255,255,.12); border-color: #fff; }

/* ============================================================
   Header / Nav
   ============================================================ */
/* Opaque, not translucent-blurred. A backdrop-filter on a sticky header makes
   the browser re-blur the strip of page behind it on every single scroll
   frame, which is the most expensive thing a static site can ask for and the
   main reason scrolling felt choppy. The page behind it is white anyway, so
   the blur was buying nothing visible. */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: var(--color-paper);
  border-bottom: 1px solid var(--color-border);
}
/* Grid, not flex + space-between. The logo and the "Partner With Us" button
   are different widths, so space-between (which just puts equal gaps around
   the middle item) pulled the nav links visibly off-centre toward the
   narrower side. A 1fr/auto/1fr grid centers the middle column on the header
   itself, independent of how wide either side is. */
.site-header .wrap {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  height: 76px;
}
.brand { display: flex; align-items: center; gap: 10px; }
.brand img { height: 26px; width: auto; }
.nav-links {
  display: flex; align-items: center; gap: var(--sp-32);
}
/* Nav sits at the small end of this face, so it gets a size bump, weight 600
   and open tracking to stay crisp and clearly clickable. */
.nav-links a {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--color-body);
  padding: 6px 2px;
  border-bottom: 2px solid transparent;
  transition: border-color .15s ease, color .15s ease;
}
.nav-links a:hover, .nav-links a[aria-current="page"] {
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
}
.nav-right { display: flex; align-items: center; gap: var(--sp-24); justify-self: end; }
.nav-toggle {
  display: none;
  background: none; border: 1px solid var(--color-border); border-radius: var(--radius);
  width: 42px; height: 42px; align-items: center; justify-content: center;
}
.nav-toggle span, .nav-toggle span::before, .nav-toggle span::after {
  content: ""; display: block; width: 18px; height: 2px; background: var(--color-ink);
  position: relative; transition: transform .2s ease, opacity .2s ease;
}
.nav-toggle span::before { position: absolute; top: -6px; }
.nav-toggle span::after { position: absolute; top: 6px; }

@media (max-width: 860px) {
  .nav-links { position: fixed; inset: 76px 0 auto 0; background: #fff; border-bottom: 1px solid var(--color-border);
    flex-direction: column; align-items: flex-start; gap: 0; padding: var(--sp-8) var(--sp-24) var(--sp-24);
    transform: translateY(-8px); opacity: 0; pointer-events: none; transition: opacity .18s ease, transform .18s ease; }
  .nav-links a { width: 100%; padding: 14px 0; border-bottom: 1px solid var(--color-border-light); }
  body.nav-open .nav-links { transform: translateY(0); opacity: 1; pointer-events: auto; }
  .nav-toggle { display: inline-flex; }
  .nav-right .btn-primary { display: none; }
  body.nav-open .nav-links a[aria-current="page"] { border-bottom-color: var(--color-border-light); }
}

/* ============================================================
   Hero
   ============================================================ */
.hero {
  padding: var(--sp-96) 0 var(--sp-80);
  text-align: center;
}
.hero .wrap { max-width: 820px; }
.hero h1 {
  font-size: var(--text-display);
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1.14;
}
.hero p.lead {
  font-size: var(--text-subheading);
  color: var(--color-muted);
  line-height: 1.5;
  max-width: 640px;
  margin: 0 auto var(--sp-40);
}
.hero-ctas { display: flex; gap: var(--sp-16); justify-content: center; flex-wrap: wrap; }

/* Page header for interior pages (About / Brands / Contact) */
.page-hero {
  padding: var(--sp-64) 0 var(--sp-48);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-surface);
}
.page-hero .wrap { max-width: 780px; text-align: center; }
.page-hero h1 { font-size: var(--text-heading-lg); }
.page-hero p { color: var(--color-muted); font-size: var(--text-subheading); margin: 0; }

/* ============================================================
   Stat strip
   ============================================================ */
.stat-strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--sp-24);
}
.stat-card {
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--sp-32) var(--sp-24);
  text-align: center;
}
.stat-card .num {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-heading-lg);
  color: var(--color-accent);
  letter-spacing: 0;
  line-height: 1.1;
  margin-bottom: 6px;
}
.stat-card .label {
  font-size: var(--text-body-sm);
  color: var(--color-muted);
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.45;
}
@media (max-width: 860px) { .stat-strip { grid-template-columns: repeat(2, 1fr); } }

/* ============================================================
   Category / feature cards
   ============================================================ */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-24);
}
.category-card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--card-padding);
  transition: border-color .15s ease, transform .15s ease;
}
.category-card:hover { border-color: var(--color-accent); transform: translateY(-2px); }
.category-card .eyebrow { margin-bottom: var(--sp-8); }
.category-card h3 { font-size: var(--text-heading-sm); margin-bottom: var(--sp-8); }
.category-card p { color: var(--color-muted); font-size: var(--text-body-sm); margin-bottom: var(--sp-24); }
.category-card .link-arrow {
  margin-top: auto;
  font-weight: 700;
  font-size: var(--text-body-sm);
  color: var(--color-accent);
  white-space: nowrap;
}
/* Four-up variant, added when Confectionery became a stated category in the
   V4 company profile and the What We Trade row went from three cards to
   four. Drops to 2x2 before it drops to a single column. */
.card-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1080px) { .card-grid.cols-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 860px) { .card-grid { grid-template-columns: 1fr; } }
@media (max-width: 620px) { .card-grid.cols-4 { grid-template-columns: 1fr; } }

/* ============================================================
   Split content section (image + text)
   ============================================================ */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-64);
  align-items: center;
}
.split.reverse .split-media { order: 2; }
.split-media img {
  border-radius: var(--radius);
  width: 100%;
  height: 100%;
  object-fit: cover;
  aspect-ratio: 4/3;
}
.split-media.graphic {
  background: var(--color-accent-tint);
  border-radius: var(--radius);
  aspect-ratio: 4/3;
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-40);
}
.split-media.graphic img {
  width: 100%; height: 100%;
  object-fit: contain;
  aspect-ratio: auto;
  border-radius: 0;
}
.split-body h2 { font-size: var(--text-heading); margin-bottom: var(--sp-24); }
.split-body p { color: var(--color-body); }
.split-body p + p { margin-top: var(--sp-16); }
@media (max-width: 860px) {
  .split { grid-template-columns: 1fr; gap: var(--sp-32); }
  .split.reverse .split-media { order: 0; }
}

/* ============================================================
   Brand grid — the catalog listing
   The client asked for brands to be listed by name, not by logo. That is the
   safer call as well as the requested one: a wholesaler naming the brands it
   trades in is ordinary practice, whereas reproducing two dozen trademarked
   logos implies an authorised relationship the company does not claim.
   Same hairline grid, 8px radius and flat surfaces as every other component;
   only the cell contents changed.
   ============================================================ */
/* Editorial treatment, built to the design reference the client supplied:
   centred serif category name, a short accent rule under it, centred body
   copy, one full hairline, then the brand names in letterspaced caps with
   vertical rules between columns only — no boxes, no cell fills, no outer
   border. The whole block sits on the warm surface tone.

   This deliberately reads quieter than the rest of the site. A catalogue of
   fragrance and beauty houses is the one place where an editorial, almost
   printed-page treatment earns its keep, and it is what the client asked
   for. */
/* All three category blocks share one background, so consecutive sections
   merge into a single continuous page — which is what the reference does.
   That makes the default 80px section padding read as dead air rather than
   as separation, since there is no colour change to justify it. */
section.cat-block { background: var(--color-surface); padding: var(--sp-48) 0; }
.cat-block .wrap { max-width: 1080px; }

.cat-head { text-align: center; }
.cat-head h2 {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: clamp(2rem, 1.6rem + 1.7vw, 2.85rem);
  letter-spacing: .005em;
  line-height: 1.15;
  color: var(--color-ink);
  margin: 0;
}
.cat-rule {
  width: 128px; height: 1px;
  background: var(--color-accent);
  opacity: .5;
  margin: var(--sp-24) auto var(--sp-32);
}
.cat-head p {
  font-family: var(--font-serif);
  /* Cormorant has a small x-height, so it needs a step up from the sans body
     size to stay comfortable across three paragraphs. */
  font-size: 18px;
  line-height: 1.75;
  color: var(--color-body);
  max-width: 760px;
  margin: 0 auto var(--sp-16);
}
.cat-head p:last-child { margin-bottom: 0; }

/* Sub-grouping inside one category (Confectionery has chocolates & snacks
   and coffee & capsules). Sits a clear step below the category name. */
.brand-subgroup-head {
  font-family: var(--font-serif);
  font-size: var(--text-subheading);
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--color-muted);
  text-align: center;
  margin: var(--sp-48) 0 0;
}

.brand-list {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  border-top: 1px solid var(--color-border);
  margin-top: var(--sp-40);
  padding-top: var(--sp-16);
}
.brand-list .brand-name {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  text-align: center;
  padding: var(--sp-24) var(--sp-8);
  min-height: 64px;
  font-family: var(--font-serif);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .1em;
  line-height: 1.5;
  text-transform: uppercase;
  color: var(--color-ink);
  /* Cormorant defaults to oldstyle figures, which drop below the baseline
     and look like a mistake inside letterspaced caps — "CREED 1760" and
     "BOND NO.9" both carry numerals. */
  font-variant-numeric: lining-nums;
  text-wrap: balance;
}
/* The divider is a short vertical rule on the cell's leading edge, never
   before the first cell in a row. Which cells start a row changes with the
   column count, so each breakpoint restores the previous rule and suppresses
   its own — order matters here. */
.brand-list .brand-name::before {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  transform: translateY(-50%);
  width: 1px; height: 40px;
  background: var(--color-border);
}
.brand-list .brand-name:nth-child(5n + 1)::before { display: none; }

@media (max-width: 900px) {
  .brand-list { grid-template-columns: repeat(3, 1fr); }
  .brand-list .brand-name:nth-child(5n + 1)::before { display: block; }
  .brand-list .brand-name:nth-child(3n + 1)::before { display: none; }
}
@media (max-width: 560px) {
  .brand-list { grid-template-columns: repeat(2, 1fr); }
  .brand-list .brand-name:nth-child(3n + 1)::before { display: block; }
  .brand-list .brand-name:nth-child(2n + 1)::before { display: none; }
}

/* ============================================================
   Category jump nav — the Catalog page now carries what would
   otherwise be three separate pages, so it needs a way in.
   ============================================================ */
/* 2026-09-03: this strip used to be a row of small neutral-bordered links
   sitting flush left, and the owner reported it read as incidental text
   rather than as the way into the four categories. Three things fix that,
   none of which introduce a new pattern: a label saying what the row is
   for, real button weight on the targets (accent border + tint fill, the
   accent's sanctioned "primary action" use), and centring so it reads as a
   deliberate feature rather than a leftover line under the section above. */
section.cat-nav-section {
  padding: var(--sp-64) 0;
  border-top: 1px solid var(--color-border);
}
.cat-nav-head { text-align: center; margin-bottom: var(--sp-32); }
.cat-nav-head p {
  font-size: var(--text-body);
  color: var(--color-muted);
  margin: 0;
}
.cat-nav {
  display: flex; flex-wrap: wrap; gap: var(--sp-16);
  justify-content: center;
}
/* Jump targets must clear the sticky header, or the heading lands underneath
   it and the visitor arrives looking at the brand list with no title. */
#fragrance, #skincare, #hair-personal-care, #confectionery { scroll-margin-top: 92px; }
.cat-nav a {
  display: inline-flex; align-items: center; gap: 10px;
  font-size: var(--text-body);
  font-weight: 600;
  color: var(--color-ink);
  background: var(--color-accent-tint);
  border: 1px solid var(--color-accent);
  border-radius: var(--radius);
  padding: 15px var(--sp-24);
  transition: background-color .18s ease, color .18s ease, transform .18s ease;
}
/* Down arrow, not the right arrow used by .link-arrow: these move the reader
   down this page, they do not leave it. Falls back to a system glyph like the
   existing arrows do, since the self-hosted Cormorant subset is latin only. */
.cat-nav a::after {
  content: "\2193";
  font-size: 15px;
  color: var(--color-accent);
  transition: color .18s ease, transform .18s ease;
}
.cat-nav a:hover {
  background: var(--color-accent);
  color: var(--color-paper);
  transform: translateY(-2px);
}
.cat-nav a:hover::after { color: var(--color-paper); transform: translateY(2px); }
@media (prefers-reduced-motion: reduce) {
  .cat-nav a, .cat-nav a::after { transition: none; }
  .cat-nav a:hover { transform: none; }
  .cat-nav a:hover::after { transform: none; }
}
@media (max-width: 560px) {
  /* Four full-width targets stacked is the honest mobile form here — the
     longest label alone fills the row, so side-by-side would only shrink
     the tap targets back to the size that caused the problem. */
  .cat-nav { flex-direction: column; align-items: stretch; }
  .cat-nav a { justify-content: space-between; font-size: var(--text-body-sm); }
}
/* Retained but unused on the Catalog: the supplied reference shows no count
   under the lists. Left in place because it is cheap and the client may want
   the counts back. */
.brand-count {
  font-size: var(--text-caption);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--color-steel);
  margin-top: var(--sp-16);
}

/* ============================================================
   Signature glyph — "The Pact"
   Built from the wordmark's own vocabulary: thick crescents,
   knife-edge blades, narrow negative seams. Two halves close on a
   central blade and lock, then hold — the motion resolves to a
   settled state rather than idling (bar.md #4). Contained in an
   8px tile, single accent colour, no shadow (bar.md #1, #3, #6).
   ============================================================ */
.glyph-tile {
  background: var(--color-carbon);
  border-radius: var(--radius);
  aspect-ratio: 4/3;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-32);
  overflow: hidden;
}
.pact-glyph { width: 100%; height: 100%; display: block; }

/* --- Atmosphere: slow drift behind the cards (bar.md #2) --- */
.pact-glyph .pg-wash   { fill: var(--color-accent-tint); opacity: .07; }
.pact-glyph .pg-drift  { transform-origin: 200px 150px; }
.js .glyph-tile.in-view .pg-drift { animation: pg-drift 40s linear infinite; }
@keyframes pg-drift { to { transform: rotate(360deg); } }

/* --- Cards: the foreground. Real edges, real content, and they never
   leave the frame — only their state changes (bar.md #2, #4). --- */
.pact-glyph .lb-card {
  fill: var(--color-paper);
  stroke: var(--color-accent);
  stroke-opacity: .22;
  stroke-width: 1.5;
}
/* Two background planes with real tonal separation — one clearly darker
   than the ground, one clearly lighter. Both bleed off the tile edge so
   the frame reads as a crop of something larger. */
.pact-glyph .lb-plane-back  { fill: var(--color-accent); opacity: .45; }
.pact-glyph .lb-plane-front { fill: var(--color-accent-tint); opacity: .12; }
.pact-glyph .lb-bullet      { fill: var(--color-gold-bright); }

/* SVG units, scaled up by the tile, so these read larger than the numbers
   suggest. Still nudged up and weighted for the serif. */
.pact-glyph text { font-family: var(--font-body); }
.pact-glyph .lb-eyebrow {
  font-size: 10px; font-weight: 700; letter-spacing: .16em;
  fill: var(--color-gold-bright);
}
.pact-glyph .lb-market { font-size: 26px; font-weight: 600; fill: var(--color-ink); }
.pact-glyph .lb-cat    { font-size: 12px; font-weight: 500; fill: var(--color-muted); }

/* --- Hub mark: a fan of knife-edge blades taken from the wedge forms
   inside the wordmark's A/V/E letters. Deliberately NOT a letterform,
   so it cannot read as a third typeface. --- */
.pact-glyph .lb-blade { fill: var(--color-accent-tint); }

/* --- The route: draws from the hub to the market card, a marker runs
   it, then it holds. A real before/after that resolves (bar.md #4). --- */
/* The planned route is always drawn, so the composition is never empty;
   the travelled route fills in over it, then holds. */
.pact-glyph .lb-route-plan {
  fill: none; stroke: var(--color-accent-tint); stroke-opacity: .3;
  stroke-width: 2; stroke-dasharray: 5 6; stroke-linecap: butt;
}
.pact-glyph .lb-route-done {
  fill: none; stroke: var(--color-accent-tint); stroke-opacity: .95;
  stroke-width: 2.5; stroke-dasharray: 260; stroke-linecap: butt;
}
.pact-glyph .lb-runner {
  fill: var(--color-accent-tint);
  offset-path: path("M 305 92 C 305 140, 268 172, 188 180");
  offset-rotate: 0deg;
}
.pact-glyph .lb-landed { fill: var(--color-accent-tint); transform-origin: 188px 180px; }

/* Resting (no-JS / reduced-motion) state: route complete, first market. */
.pact-glyph .lb-market-2, .pact-glyph .lb-market-3, .pact-glyph .lb-market-4 { opacity: 0; }
.pact-glyph .lb-runner { opacity: 0; }

/* Travel is linear so it reads as steady, deliberate movement — an
   ease-out here front-loads the motion and makes it imperceptible.

   One route leg runs in 3.6s and the card advances through four markets in
   14.4s. The earlier 6s / 24s pacing was slow enough that a visitor could
   watch the whole tile and not be sure anything was moving, which invites
   exactly the wrong reading of a graphic about shipping routes. The leg now
   also spends a larger share of its cycle actually travelling and less of it
   holding a finished frame. */
.js .glyph-tile.in-view .lb-route-done { animation: lb-draw 3.6s linear infinite; }
.js .glyph-tile.in-view .lb-runner   { animation: lb-run    3.6s linear infinite; }
.js .glyph-tile.in-view .lb-landed   { animation: lb-land   3.6s var(--ease-out) infinite; }
.js .glyph-tile.in-view .lb-market-1 { animation: lb-cyc-1 14.4s steps(1, end) infinite; }
.js .glyph-tile.in-view .lb-market-2 { animation: lb-cyc-2 14.4s steps(1, end) infinite; }
.js .glyph-tile.in-view .lb-market-3 { animation: lb-cyc-3 14.4s steps(1, end) infinite; }
.js .glyph-tile.in-view .lb-market-4 { animation: lb-cyc-4 14.4s steps(1, end) infinite; }

/* The route line draws itself in, holds, then clears for the next market */
@keyframes lb-draw {
  0%        { stroke-dashoffset: 260; opacity: 1; }
  42%       { stroke-dashoffset: 0;   opacity: 1; }
  84%       { stroke-dashoffset: 0;   opacity: 1; }
  95%, 100% { stroke-dashoffset: 0;   opacity: 0; }
}
/* The marker travels the route, then is absorbed on arrival */
@keyframes lb-run {
  0%       { offset-distance: 0%;   opacity: 0; }
  5%       { opacity: 1; }
  42%      { offset-distance: 100%; opacity: 1; }
  48%,100% { offset-distance: 100%; opacity: 0; }
}
/* The arrival marker at the card: lands, then holds for the rest of the cycle */
@keyframes lb-land {
  0%, 40%   { opacity: 0; transform: scale(.4); }
  46%       { opacity: 1; transform: scale(1.35); }
  52%, 86%  { opacity: 1; transform: scale(1); }
  95%, 100% { opacity: 0; transform: scale(1); }
}
@keyframes lb-cyc-1 { 0% { opacity: 1 } 25% { opacity: 0 } 100% { opacity: 0 } }
@keyframes lb-cyc-2 { 0% { opacity: 0 } 25% { opacity: 1 } 50% { opacity: 0 } }
@keyframes lb-cyc-3 { 0% { opacity: 0 } 50% { opacity: 1 } 75% { opacity: 0 } }
@keyframes lb-cyc-4 { 0% { opacity: 0 } 75% { opacity: 1 } 100% { opacity: 0 } }

/* ============================================================
   Region / customers badges
   ============================================================ */
.region-list {
  display: flex; flex-wrap: wrap; gap: var(--sp-16);
}
.region-badge {
  display: flex; align-items: center; gap: 10px;
  background: #fff; border: 1px solid var(--color-border); border-radius: var(--radius);
  padding: 12px 20px; font-weight: 600; font-size: var(--text-body-sm); color: var(--color-ink);
}
.region-badge .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--color-accent); }

/* ============================================================
   Full-bleed accent band (CTA)
   ============================================================ */
.cta-band {
  background: var(--color-accent);
  color: #fff;
  padding: var(--sp-80) 0;
  text-align: center;
}
.cta-band .wrap { max-width: 720px; }
.cta-band .eyebrow { color: rgba(255,255,255,.75); }
.cta-band h2 { color: #fff; font-size: var(--text-heading-lg); }
.cta-band p { color: rgba(255,255,255,.88); font-size: var(--text-subheading); margin-bottom: var(--sp-40); }
.cta-band .cta-actions { display: flex; gap: var(--sp-16); justify-content: center; flex-wrap: wrap; }
.cta-band .btn-primary { background: #fff; color: var(--color-accent); }
.cta-band .btn-primary:hover { background: #f3ece3; }

/* ============================================================
   Contact page
   ============================================================ */
.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-64);
  align-items: start;
}
.contact-cards { display: grid; gap: var(--sp-16); }
.contact-card {
  display: flex; gap: var(--sp-16); align-items: flex-start;
  background: #fff; border: 1px solid var(--color-border); border-radius: var(--radius);
  padding: var(--sp-24);
}
.contact-card .icon {
  width: 40px; height: 40px; border-radius: var(--radius);
  background: var(--color-accent-tint); color: var(--color-accent);
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.contact-card h3 { font-size: var(--text-body); margin-bottom: 4px; }
.contact-card p, .contact-card a { color: var(--color-muted); font-size: var(--text-body-sm); }
.contact-card a:hover { color: var(--color-accent); }
.map-embed {
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--color-border);
  aspect-ratio: 4/3;
}
.map-embed iframe { width: 100%; height: 100%; border: 0; }
@media (max-width: 860px) { .contact-grid { grid-template-columns: 1fr; } }

/* ============================================================
   Footer
   ============================================================ */
.site-footer { background: var(--color-carbon); color: #cfc4b6; padding: var(--sp-64) 0 var(--sp-32); }
.footer-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: var(--sp-48);
  padding-bottom: var(--sp-48);
  border-bottom: 1px solid rgba(255,255,255,.12);
}
.footer-brand img { height: 24px; margin-bottom: var(--sp-16); filter: brightness(0) invert(1); }
/* Light serif text on a dark ground is the hardest case in the whole design:
   thin strokes lose ground against the carbon. Footer copy is lifted a step
   in size and given a slightly brighter grey than the sans version used. */
.footer-brand p { color: #a89c8c; font-size: 16px; max-width: 320px; line-height: 1.6; }
.footer-col h4 {
  color: #fff; font-family: var(--font-body); font-weight: 700; font-size: var(--text-body-sm);
  text-transform: uppercase; letter-spacing: .12em; margin-bottom: var(--sp-16);
}
.footer-col a, .footer-col p {
  display: block; color: #a89c8c; font-size: 16px; margin-bottom: 10px; line-height: 1.55;
}
.footer-col a:hover { color: #fff; }
.footer-bottom {
  display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: var(--sp-16);
  padding-top: var(--sp-24); font-size: var(--text-caption); color: #7a6f61;
}
@media (max-width: 760px) { .footer-grid { grid-template-columns: 1fr; gap: var(--sp-32); } }

/* ============================================================
   Utility
   ============================================================ */
.bg-surface { background: var(--color-surface); }
.text-center { text-align: center; }
