/* Shared across every page — fonts, colors, the global header/footer/nav,
   and any other chrome that isn't specific to one collection's product
   grid. Collection pages load this first, then their own style.css for
   grid/product-card/controls rules. Paths here are root-relative
   (/assets/...) since this file (and the header/footer that use these
   classes) is included from pages at different folder depths. */
@font-face {
  font-family: 'Director';
  src: url('/assets/fonts/Director-Bold.otf') format('opentype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'GenWanMin JP';
  src: url('/assets/fonts/GenWanMin JP SemiBold.ttf') format('truetype');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Neue Montreal';
  src: url('/assets/fonts/NeueMontreal-Light.ttf') format('truetype');
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Neue Montreal';
  src: url('/assets/fonts/NeueMontreal-LightItalic.ttf') format('truetype');
  font-weight: 300;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: 'Neue Montreal';
  src: url('/assets/fonts/NeueMontreal-Regular.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Neue Montreal';
  src: url('/assets/fonts/NeueMontreal-RegularItalic.ttf') format('truetype');
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: 'Neue Montreal';
  src: url('/assets/fonts/NeueMontreal-Medium.ttf') format('truetype');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Neue Montreal';
  src: url('/assets/fonts/NeueMontreal-MediumItalic.ttf') format('truetype');
  font-weight: 500;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: 'Neue Montreal';
  src: url('/assets/fonts/NeueMontreal-Bold.ttf') format('truetype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Neue Montreal';
  src: url('/assets/fonts/NeueMontreal-BoldItalic.ttf') format('truetype');
  font-weight: 700;
  font-style: italic;
  font-display: swap;
}

:root {
  --font-headline: 'Director', Georgia, 'Times New Roman', serif;
  --font-body: 'Neue Montreal', Helvetica, Arial, sans-serif;
  --color-bg: #f9f8f2;
  --color-text: #ce1d0b;
  --color-muted: #ce1d0b;
  --color-border: #e2e2e2;
  --grid-gap: 32px;
  /* .site-header's own left/right margin below reads this too (not a
     hardcoded matching value), so page content and the header always
     line up at the same distance from the window edge, on mobile
     included (see the @media override right after this block). */
  --page-padding: 50px;
  /* The ticket-notch concave corner, as a clip-path shape: fixed-size
     (plain px) at the 8-point bezier-sampled curve in each corner, so the
     radius never changes no matter how big or small the box it's applied
     to is; calc(100% - Npx) along the straight runs in between, so it
     still stretches to fit any box size/aspect ratio. One definition,
     reused wherever this site draws that corner (the header's own frame
     in this file, the homepage tiles in home.css) — same source, so
     "does every ticket frame use the same radius" is true by
     construction, not by keeping copies in sync by hand. Points sampled
     from WB-FRAME.svg's own corner curve (assets/graphics/WB-FRAME.svg),
     scaled 28/26× to match that file's border-image-width/-slice ratio
     from when the header still used border-image. */
  --ticket-clip: polygon(
    19.35px 0px,
    calc(100% - 19.35px) 0px,
    calc(100% - 19.103px) 3.198px,
    calc(100% - 17.864px) 7.635px,
    calc(100% - 15.687px) 11.533px,
    calc(100% - 12.692px) 14.773px,
    calc(100% - 9px) 17.236px,
    calc(100% - 4.729px) 18.802px,
    100% 19.35px,
    100% calc(100% - 19.35px),
    calc(100% - 4.729px) calc(100% - 18.802px),
    calc(100% - 9px) calc(100% - 17.236px),
    calc(100% - 12.692px) calc(100% - 14.773px),
    calc(100% - 15.687px) calc(100% - 11.533px),
    calc(100% - 17.864px) calc(100% - 7.635px),
    calc(100% - 19.103px) calc(100% - 3.198px),
    calc(100% - 19.35px) 100%,
    19.35px 100%,
    19.103px calc(100% - 3.198px),
    17.864px calc(100% - 7.635px),
    15.687px calc(100% - 11.533px),
    12.692px calc(100% - 14.773px),
    9px calc(100% - 17.236px),
    4.729px calc(100% - 18.802px),
    0px calc(100% - 19.35px),
    0px 19.35px,
    4.729px 18.802px,
    9px 17.236px,
    12.692px 14.773px,
    15.687px 11.533px,
    17.864px 7.635px,
    19.103px 3.198px
  );
}

/* Same 900px breakpoint every other mobile rule in this file and home.css
   use. Redefining the custom property (not overriding each of its
   consumers individually) reaches every page's edge margin at once — the
   header's own margin below, .home's padding, each collection's
   .products-section/.controls/.linesheet padding, all read this same
   variable. */
@media (max-width: 900px) {
  :root {
    --page-padding: 20px;
  }
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
}

.brand {
  min-height: 60vh;
}

h1, h2, h3, .site-title {
  font-family: var(--font-headline);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
  margin: 0;
}

/* Header: framed with the same ticket-notch corner curve as the product
   cards (not a hand-drawn approximation — see .site-header__frame below
   for the actual line). Sticky, so it stays reachable while scrolling
   the grid. */
.site-header {
  position: sticky;
  top: 20px;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 40px;
  margin: 20px var(--page-padding) 0;
  padding: 0;
  /* Transparent 28px reservation only — same content inset as before, but
     no border-image drawing the frame itself anymore (see .site-header__
     corner/__edge below for why). */
  border: 28px solid transparent;
  overflow: visible;
  /* No clip-path here (moved to .site-header__bg below) — clip-path on an
     element clips its *entire* descendant subtree for painting purposes,
     not just content that would otherwise overflow the box. .site-nav's
     mobile full-screen panel is position:fixed and a DOM descendant of
     .site-header; with the clip-path here, it got silently clipped down
     to this ~96px-tall box the instant it opened — still logically
     "open" (right classes, right computed styles, fully clickable-looking
     in devtools) but invisible and unclickable below the header itself.
     Moving the clip to its own layer, instead of the ancestor .site-nav
     lives inside, keeps .site-nav's ancestor chain clip-path-free. */
}

/* Just the header's background fill, cut to the ticket shape — the part
   of the old .site-header clip-path that still needs clipping. Same
   -28px reach-out as .site-header__frame below (this element's own box
   needs to match .site-header's border-box for --ticket-clip to line up
   with the visible outline), z-index 0 so it stays under the frame ring
   (1/2) and real content (3+) without needing to be first in the DOM. */
.site-header__bg {
  position: absolute;
  top: -28px;
  right: -28px;
  bottom: -28px;
  left: -28px;
  z-index: 0;
  pointer-events: none;
  background: var(--color-bg);
  clip-path: var(--ticket-clip);
}

/* The header's frame line — unlike the homepage tiles (3 fixed aspect
   ratios, each with its own pre-drawn SVG), the header's width is
   continuously variable, so no single pre-sized frame image fits it.
   Three earlier approaches each left a visible defect:
   - border-image (stretching WB-FRAME.svg's straight edges, keeping its
     corners fixed-size) is a known-flaky combination with an SVG source
     — stale/partial repaints during a live window resize.
   - 4 real <img> corners + 4 separate SVG edge lines fixed the resize
     glitch, but a raster corner curve and an independent straight line
     are two unrelated shapes that only *look* joined when their pixels
     happen to land exactly right — at some sizes they didn't, showing as
     a step where the curve meets the straight run.
   - A single self-intersecting clip-path polygon (outer boundary, a
     thin slit, inner boundary traced back) drew a true one-piece ring
     with no seam in principle, but Chromium's clip-path rasterizer
     visibly mishandles that razor-thin slit — the line faded out toward
     one corner in testing, reproducible in headless Chromium, absent in
     WebKit. Not worth chasing a browser bug.

   This is two ordinary (non-self-intersecting) polygons, both the same
   shared --ticket-clip shape .site-header itself uses to clip its
   background (see :root in this file): an outer layer, full-size, filled
   with the line color; an inner layer, inset by 1px on every side,
   filled with the page background, painted on top. What shows between
   them is a uniformly 1px-thick ring, corners included — nothing to
   misalign, and nothing exotic enough to trip a rasterizer.

   That inner layer is still a near-full-size opaque rectangle (only 2px
   smaller each way, not an actual hole), so on its own it would blank
   out the header's real content sitting underneath. Fixed the other way
   round instead of chasing a true hole: the real content (logo, nav,
   Order Now — see the z-index rule below) is given a higher z-index than
   both ring layers, so it always paints on top of them regardless of how
   much of the header they visually cover. */
.site-header__frame,
.site-header__frame-inner {
  position: absolute;
  pointer-events: none;
  /* Reaches out from the padding box (this absolutely-positioned
     element's containing block) to the true border edge, exactly like
     .site-header's own -28px border reservation — so .site-header__frame's
     box matches .site-header's border-box 1:1, and --ticket-clip (written
     for that border-box) lines up with the visible ticket outline instead
     of some smaller inset rectangle. */
  top: -28px;
  right: -28px;
  bottom: -28px;
  left: -28px;
  clip-path: var(--ticket-clip);
}

.site-header__frame {
  z-index: 1;
  background: var(--color-text);
}

/* Inset 1 further px on every side than .site-header__frame — its own
   box is 2px smaller each way, so the *same* polygon above (px corner
   offsets stay the same absolute size; the calc(100% - Npx) terms
   resolve against this smaller box) produces the same curve 1px further
   in, not a re-derived one. That's what keeps the ring's width identical
   at the corners and along the straight runs: both layers are the one
   shape, just measured against two box sizes 2px apart. z-index 2 (not a
   tie with .site-header__frame's 1) so it reliably paints second/on top
   — it's what erases the outer layer's middle down to a ring, regardless
   of the two elements' DOM order. */
.site-header__frame-inner {
  top: -27px;
  right: -27px;
  bottom: -27px;
  left: -27px;
  z-index: 2;
  background: var(--color-bg);
}

/* A circle only approximates WB-FRAME.svg's actual corner curve (it's a
   bezier hook, not a true arc), which left a sliver of square fill
   peeking past the curve on close inspection. Each tile below is instead
   clipped with that exact curve, scaled 28/26× (matching border-image's
   own slice/width scaling) into a fixed 28×28 box: path traces the true
   header corner out along both straight edges to where the border-image
   curve starts, connected by the same cubic bezier the SVG uses, so the
   erased area's boundary sits exactly on the drawn stroke, not near it.
   Tiles are fixed-size regardless of header width, so — unlike an
   objectBoundingBox clip-path, which would normalize x/y to this
   short/wide box's own ratio and squash the curve flat — the shape
   never distorts. */
.ticket-notch {
  position: absolute;
  width: 28px;
  height: 28px;
  background: var(--color-bg);
  pointer-events: none;
}

.ticket-notch--tl {
  top: -28px;
  left: -28px;
  clip-path: path('M 0,19.35 C 10.93,19.35 19.35,10.93 19.35,0 L 0,0 Z');
}

.ticket-notch--tr {
  top: -28px;
  right: -28px;
  clip-path: path('M 28,19.35 C 17.07,19.35 8.65,10.93 8.65,0 L 28,0 Z');
}

.ticket-notch--bl {
  bottom: -28px;
  left: -28px;
  clip-path: path('M 0,8.65 C 10.93,8.65 19.35,17.07 19.35,28 L 0,28 Z');
}

.ticket-notch--br {
  bottom: -28px;
  right: -28px;
  clip-path: path('M 28,8.65 C 17.07,8.65 8.65,17.07 8.65,28 L 28,28 Z');
}

/* Reusable version of the header's own frame, for any other box that
   should read as the same ticket-stub shape (currently the Sales Tools
   homepage's tiles) — same border-image + notch-erase technique, just
   generalized. Pair with the 4 .ticket-notch spans (echo_ticket_notch()
   in includes/functions.php writes them out) as children. */
.ticket-frame {
  position: relative;
  border: 28px solid transparent;
  border-image-source: url('/assets/graphics/WB-FRAME.svg');
  border-image-slice: 26;
  border-image-width: 28px;
  border-image-repeat: stretch;
  background: var(--color-bg);
  background-clip: border-box;
  overflow: hidden;
}

/* Stacked above .site-header__frame/__frame-inner's z-index 1/2 (works
   without position:relative — these are flex items of .site-header, and
   z-index applies to flex items regardless of position) so the header's
   real content always paints over the frame ring's layers, even though
   the inner ring layer is itself a near-full-size opaque rectangle. */
.site-logo-link,
.nav-burger,
.site-nav,
.site-header__order {
  z-index: 3;
}

.site-logo-link {
  display: flex;
  align-items: center;
  text-decoration: none;
}

/* 36px, not 40px — the nav links' actual rendered footprint (the
   uppercase Director line + the smaller CJK line below it, gap
   included) is 36px tall despite each .site-nav__link container being
   fixed at 40px for alignment purposes, so 40px here visibly overshot
   the links' real height. Measured directly (getBoundingClientRect),
   not eyeballed. */
.site-logo {
  height: 36px;
  width: auto;
}

/* Nav placeholders (left cluster) and Order Now (right cluster) share this
   two-line layout: uppercase Director label over a smaller CJK line. */
.site-nav {
  display: flex;
  align-items: flex-start;
  gap: 32px;
}

.site-header__order {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-left: auto;
}

.site-nav__link {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  height: 40px;
  color: inherit;
  text-decoration: none;
}

.site-header .site-nav__link:hover .site-nav__link-en,
.site-header .site-nav__link:hover .site-nav__link-zh {
  text-decoration: underline;
}

/* Nav menu items (unlike the standalone Order Now CTA) read left-to-right,
   so their two-line EN/ZH stack is left-aligned rather than centered. */
.site-nav .site-nav__link {
  align-items: flex-start;
}

/* Order Now sits at the header's right edge, next to the favorite star —
   right-aligned so its two-line stack reads flush against that edge. */
.site-header__order .site-nav__link {
  align-items: flex-end;
}

/* Burger toggle: hidden on desktop (the inline .site-nav row already
   shows the menu directly) — only appears below the mobile breakpoint,
   where it opens .site-nav as a full-screen panel instead. Right-aligned
   and flex-shrink:0 since at narrow widths it's sharing the header row
   with the logo and would otherwise get squeezed toward 0 width. */
.nav-burger {
  display: none;
  flex-shrink: 0;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 28px;
  height: 28px;
  padding: 0;
  margin-left: auto;
  border: none;
  background: none;
  cursor: pointer;
}

.nav-burger__bar {
  display: block;
  width: 100%;
  height: 2px;
  background: var(--color-text);
}

.site-nav__close {
  display: none;
}

/* Order Now's mobile duplicate inside the burger panel (see header.php) —
   hidden on desktop, where the real one already shows inline at the
   header's right edge. */
.site-nav__link--mobile-only {
  display: none;
}

@media (max-width: 900px) {
  /* .site-header's 40px gap is sized for the desktop nav's multi-word
     links — with only the burger/logo/star left in this row on mobile,
     it was eating a third of the available width by itself (2 gaps ×
     40px) on top of a full-size 205px-wide logo, together overflowing a
     320px phone's ~224px available header width badly enough to force
     the whole page into horizontal scroll. Both shrink here: a much
     tighter gap, and the logo itself scaled down (.site-logo below) —
     confirmed by measurement, not eyeballed, to actually fit with real
     margin at 320px, not just "smaller". */
  .site-header {
    gap: 8px;
  }

  /* height:auto width instead of the desktop 40px — at that height the
     header row's other content (burger + star, 32px each, fixed) leaves
     nowhere near enough space for it once .site-header's gap above is
     also accounted for. */
  .site-logo {
    height: 24px;
  }

  /* Burger moves to the far left (order: -1 puts it before the logo
     visually without changing tab order). Centering the logo with plain
     auto margins only works out exact when both flanking items are the
     same width — they aren't (burger ~28px vs the favorite star's
     ~25px) — so both get pinned to an identical 32px slot instead, each
     centering their own (still differently-sized) icon within it, which
     makes the logo's auto-margin centering exact regardless. */
  .nav-burger {
    display: flex;
    order: -1;
    width: 32px;
    margin-left: 0;
  }

  .site-header__order {
    width: 32px;
    /* Unset — competed with the logo's own auto margins below for the
       row's free space, which was skewing the "centering" off-center. */
    margin-left: 0;
    justify-content: center;
  }

  .site-logo-link {
    margin: 0 auto;
  }

  /* The desktop-only inline Order Now text is replaced by the burger
     panel's own copy on mobile — the favorite star stays put. */
  .site-header__order .site-nav__link {
    display: none;
  }

  .site-nav__link--mobile-only {
    display: flex;
  }

  /* Same <nav> element as desktop, just repositioned into a full-screen
     panel and hidden until main.js toggles .is-open on it. */
  .site-nav {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 100;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 36px;
    padding: 32px;
    background: var(--color-bg);
  }

  .site-nav.is-open {
    display: flex;
  }

  .site-nav__close {
    display: block;
    position: absolute;
    top: 24px;
    right: 24px;
    width: 32px;
    height: 32px;
    padding: 0;
    border: none;
    background: none;
    color: var(--color-text);
    font-size: 1.75rem;
    line-height: 1;
    cursor: pointer;
  }

  .site-nav .site-nav__link {
    height: auto;
  }

  .site-nav .site-nav__link-en {
    font-size: 1.6rem;
  }

  .site-nav .site-nav__link-zh {
    font-size: 1.3rem;
  }
}

.site-nav__link-en {
  font-family: var(--font-headline);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 0.8rem;
  font-weight: 700;
}

.site-nav__link-zh {
  font-family: 'GenWanMin JP', var(--font-body);
  font-size: 0.9rem;
}

.site-logo-placeholder {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 40px;
  padding: 0 12px;
  border: 1px dashed var(--color-border);
  font-family: var(--font-headline);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--color-muted);
}

/* Favorite star: outline by default, fills solid red when active. The
   header one filters the grid to favorited products (on a linesheet
   page); the per-card one (styled in that page's own stylesheet) marks a
   product as a favorite. Both share this base look so the "on" state
   reads consistently everywhere. */
.favorite-toggle {
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 0;
}

.favorite-toggle svg {
  width: 25px;
  height: 25px;
  display: block;
}

/* stroke-width is in the icon's own 29.51-unit viewBox, not CSS px — it
   scales with the icon size above. 1.5 was picked to match the ticket
   frame's line weight (WB-FRAME.svg's default 1-unit stroke, ~1.25px on
   screen at a typical card width) at this icon's 25px render size. */
.favorite-toggle svg path {
  fill: none;
  stroke: var(--color-text);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.favorite-toggle[aria-pressed="true"] svg path {
  fill: var(--color-text);
}

/* Footer */
.site-footer {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 24px;
  padding: 24px var(--page-padding) 40px;
}

.site-footer .site-nav__link {
  height: auto;
  align-items: center;
  text-align: center;
}

.site-footer a.site-nav__link:hover .site-nav__link-en,
.site-footer a.site-nav__link:hover .site-nav__link-zh {
  text-decoration: underline;
}

@media (max-width: 900px) {
  /* Stacked and centered instead of spread left/center/right. */
  .site-footer {
    flex-direction: column;
    align-items: center;
    gap: 20px;
  }

  /* "Enjoy your meal" (the middle item, a <span> rather than an <a> like
     the email/phone links either side of it) sits last in the stacked
     mobile column instead of its desktop between-the-two-links spot. */
  .site-footer span.site-nav__link {
    order: 1;
  }

}

/* Back to top: hidden until main.js adds .is-visible (past one viewport
   height of scroll), faded/nudged in and out rather than just toggled.
   Desktop: top-left, under the header — left is page-padding + 15px extra
   margin, top:140px clears the header's own 96px height (sticky top:20px
   + its 28px transparent border reservation) plus a 24px gap. Mobile
   (below) overrides this to bottom-center instead — its header is a
   different height, and top-left there sits awkwardly close to the
   burger/logo row, so it goes to the bottom-center position instead. */
.back-to-top {
  position: fixed;
  left: calc(var(--page-padding) + 15px);
  top: 140px;
  z-index: 20;
  height: 44px;
  padding: 0 20px;
  border: 1px solid var(--color-text);
  border-radius: 999px;
  background: var(--color-bg);
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transform: translateY(8px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.back-to-top__text {
  font-family: var(--font-headline);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--color-text);
  white-space: nowrap;
}

.back-to-top.is-visible {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

/* Mobile: bottom-center instead of top-left — left:50% + translateX(-50%)
   centers it regardless of its own (content-driven) width; the fade/nudge
   translateY is combined into the same transform rather than a second
   property, since only one transform can apply at a time. Placed after
   the base rule above (same specificity — source order decides) rather
   than inside an earlier media block. */
@media (max-width: 900px) {
  .back-to-top {
    left: 50%;
    top: auto;
    bottom: 25px;
    transform: translate(-50%, 8px);
  }

  .back-to-top.is-visible {
    transform: translate(-50%, 0);
  }
}

.back-to-top img {
  width: 12px;
  height: auto;
  transform: rotate(-90deg);
  display: block;
}
