/* 3B Printing — combined design tokens (generated from tokens/*.css) */
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Noto+Sans+Thai:wght@400;500;600;700&family=Sarabun:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&display=swap');

:root {
  /* Base palette */
  --bg-base: #0E0E10;        /* dark sections, hero backdrop */
  --bg-light: #FAF9F7;       /* gallery / content sections */
  --ink: #1A1A1A;            /* body text on light backgrounds */
  --paper: #F5F1EA;          /* card / paper surface tone */
  --accent-gold: #B99356;    /* foil accent, CTA hover, dividers */
  --accent-copper: #8C5A3C;  /* secondary accent */
  --line: #E4DFD6;           /* hairlines / borders */
  --gold-gradient: linear-gradient(135deg, #C9A66B 0%, #8C5A3C 100%);  /* luxury gold surface — replaces flat dark fills on active/highlighted UI */

  /* Derived tints (oklch-adjacent mixes of the base palette) */
  --ink-soft: #4A4741;       /* secondary text on light */
  --ink-faint: #8B857B;      /* captions, meta on light */
  --paper-dim: #EDE8DF;      /* pressed / alt paper surface */
  --gold-soft: rgba(185, 147, 86, 0.16);  /* gold wash / hover fill */
  --on-dark: #F5F1EA;        /* primary text on dark */
  --on-dark-soft: rgba(245, 241, 234, 0.64); /* secondary text on dark */
  --line-dark: rgba(245, 241, 234, 0.14);    /* hairlines on dark */

  /* Semantic aliases */
  --surface-page: var(--bg-light);
  --surface-dark: var(--bg-base);
  --surface-card: var(--paper);
  --text-body: var(--ink);
  --text-muted: var(--ink-faint);
  --text-accent: var(--accent-gold);
  --border-hairline: var(--line);

  /* Fonts */
  --font-heading: 'Cormorant Garamond', 'Noto Sans Thai', Georgia, serif;
  --font-heading-thai: 'Noto Sans Thai', sans-serif;  /* no serif fallback — Latin/numerals inside Thai headings must stay Noto Sans */
  --font-body: 'Noto Sans Thai', 'Sarabun', 'Helvetica Neue', sans-serif; /* covers Thai + Latin */

  /* Type scale — "-base" tokens are what the mobile media query overrides below; the
     builder's typography-panel "heading scale" tweak only ever sets --heading-scale
     (a plain multiplier) inline, so calc() here keeps BOTH the mobile breakpoint and
     the user's scale preference in effect at once instead of one silently winning. */
  --heading-scale: 1;
  /* Separate multiplier for Thai heading SIZE only (font-family/weight/leading were already
     split by language — size wasn't). Defaults to 1 (no change) so nothing shifts until the
     builder's Thai-heading-scale control is actually touched; stacks with --heading-scale. */
  --heading-scale-thai: 1;
  --text-h1-base: 40px;   --leading-h1-base: 48px;   --leading-h1-thai-base: 54px;
  --text-h2-base: 32px;   --leading-h2-base: 40px;   --leading-h2-thai-base: 45px;
  --text-h3-base: 24px;   --leading-h3-base: 32px;   --leading-h3-thai-base: 36px;
  --text-h1: calc(var(--text-h1-base) * var(--heading-scale));
  --text-h1-thai: calc(var(--text-h1-base) * var(--heading-scale) * var(--heading-scale-thai));
  --leading-h1: calc(var(--leading-h1-base) * var(--heading-scale));
  --leading-h1-thai: calc(var(--leading-h1-thai-base) * var(--heading-scale));
  --text-h2: calc(var(--text-h2-base) * var(--heading-scale));
  --text-h2-thai: calc(var(--text-h2-base) * var(--heading-scale) * var(--heading-scale-thai));
  --leading-h2: calc(var(--leading-h2-base) * var(--heading-scale));
  --leading-h2-thai: calc(var(--leading-h2-thai-base) * var(--heading-scale));
  --text-h3: calc(var(--text-h3-base) * var(--heading-scale));
  --text-h3-thai: calc(var(--text-h3-base) * var(--heading-scale) * var(--heading-scale-thai));
  --leading-h3: calc(var(--leading-h3-base) * var(--heading-scale));
  --leading-h3-thai: calc(var(--leading-h3-thai-base) * var(--heading-scale));
  --text-body-size: 16px; --leading-body: 26px; --leading-body-thai: 29px;
  --text-caption: 13px;   --leading-caption: 18px; --leading-caption-thai: 20px;

  /* Details */
  --tracking-label: 0.14em;  /* uppercase eyebrow labels */
  --weight-heading: 600;
  --weight-heading-thai: 400; /* Thai headings render regular, not bold */
  --weight-body: 400;

  /* Spacing scale: 4 · 8 · 12 · 16 · 24 · 32 · 48 · 64 · 96 */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  --space-9: 96px;

  /* Radius: print-precise, not app-rounded */
  --radius-card: 2px;   /* cards, photos */
  --radius-ui: 12px;    /* interactive chips / buttons ONLY */

  /* Shadow: single soft style, used sparingly — hover-lift only */
  --shadow-lift: 0 8px 24px rgba(0, 0, 0, 0.08);

  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --duration-standard: 220ms;

  /* Minimum comfortable touch target (mobile webapp usability) */
  --touch-min: 44px;

  /* Recommended Designs marquee card width — narrower on mobile (below) so the "3 focused
     cards + faded slivers" framing still roughly holds on a narrow viewport. */
  --tier-card-w: 300px;

  /* Card Galleries paged-set width (one 3×2 grid). min() keeps it centered with side peeks on
     desktop and never wider than the viewport (minus gutters) on mobile — self-responsive, so
     no separate breakpoint override is needed. */
  --gallery-set-w: min(980px, calc(100vw - 96px));
}

/* Mobile: every component already reads the tokens above via var() — overriding the
   custom properties here cascades the resize everywhere for free, no component edits. */
@media (max-width: 768px) {
  :root {
    --text-h1-base: 30px;   --leading-h1-base: 38px;   --leading-h1-thai-base: 42px;
    --text-h2-base: 26px;   --leading-h2-base: 33px;   --leading-h2-thai-base: 37px;
    --text-h3-base: 20px;   --leading-h3-base: 27px;   --leading-h3-thai-base: 30px;

    --space-6: 20px;
    --space-7: 28px;
    --space-8: 40px;
    --space-9: 56px;

    --tier-card-w: 190px;

    /* Reclaim the desktop side-peek gutters (96px) — on a phone the peek costs more than it
       communicates, and every pixel it takes comes straight out of the thumbnails. */
    --gallery-set-w: calc(100vw - 32px);
  }

  /* Two columns, not three: a 3-up grid on a ~360px viewport leaves each card barely 100px
     wide. HomePage.jsx's useIsMobile hook already switches the SET SIZE to 4 (2×2) at this same
     768px breakpoint, so this only needs to fix the column count — overrides the inline
     gridTemplateColumns HomePage.jsx sets for desktop, hence !important. */
  .gallery-set { grid-template-columns: repeat(2, 1fr) !important; }

  /* The set now spans nearly the full viewport, so the arrows would sit on top of the tiles.
     GalleryStrip already handles touch swipe, which is the native gesture here anyway. */
  .gallery-arrow { display: none; }

  /* Full Finish Gallery page (GalleryGrid.jsx) — same problem as .gallery-set above: a 3-up
     grid leaves each card too small to read on a phone. Two columns instead. */
  .gallery-grid { grid-template-columns: repeat(2, 1fr) !important; }
}

* { box-sizing: border-box; }
body {
  margin: 0;
  background: var(--bg-light);
  color: var(--ink);
  font-family: var(--font-body);
}

/* Mobile webapp shell: BottomTabBar/lang-pill replace the desktop Header, pure CSS
   toggle (no JS resize listener) so there's no hydration/timing flash either way. */
.site-header { display: flex; }
.bottom-tab-bar, .mobile-lang-pill { display: none; }
@media (max-width: 768px) {
  .site-header { display: none; }
  .bottom-tab-bar, .mobile-lang-pill { display: flex; }
  /* Pages must clear the fixed tab bar's height at their very bottom. */
  .page-mobile-pad { padding-bottom: 64px; }
}

/* Recommended Designs roulette marquee (HomePage.jsx TierCarousel) — the track renders the
   tier list duplicated once, so translating exactly -50% (one list-copy's width, whatever
   that is) lands perfectly on the identical second copy — an invisible, seamless loop point. */
@keyframes tier-marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* Trust strip client logos (TrustStrip.jsx) — desaturated so the client marks sit quietly
   under our own palette, full colour on hover. */
.trust-logo { filter: grayscale(1); opacity: 0.55; transition: filter 0.3s, opacity 0.3s; }
.trust-logo:hover { filter: none; opacity: 1; }

/* Header nav links (Header/SubmenuLink, shared.jsx) — color eases to gold on hover, with a
   thin underline that grows in from center rather than just snapping into view. */
.nav-link {
  position: relative; transition: color var(--duration-standard) var(--ease-standard);
}
.nav-link::after {
  content: ''; position: absolute; left: 50%; bottom: -4px; width: 100%; height: 1px;
  background: var(--accent-gold); transform: translateX(-50%) scaleX(0);
  transition: transform var(--duration-standard) var(--ease-standard);
}
.nav-link:hover { color: var(--accent-gold) !important; }
.nav-link:hover::after { transform: translateX(-50%) scaleX(1); }

/* "See more / ดูทั้งหมด" links (HomePage.jsx carousels + gallery teaser) — color eases to gold
   and the trailing arrow nudges forward, echoing the same gold hover language as nav links. */
.see-more-link { transition: color var(--duration-standard) var(--ease-standard); }
.see-more-link:hover { color: var(--accent-gold) !important; }
.see-more-link .see-more-arrow { display: inline-block; transition: transform var(--duration-standard) var(--ease-standard); }
.see-more-link:hover .see-more-arrow { transform: translateX(4px); }

/* Card Galleries strip (HomePage.jsx GalleryStrip) — native scroll track, arrow-paged rather
   than auto-animated; scrollbarWidth:'none' (inline, Firefox) hides it there, this covers
   Chrome/Safari/Edge which only support the WebKit pseudo-element, not the standard property. */
.gallery-strip-track::-webkit-scrollbar { display: none; }

/* Lookbook page/language transition (shared.jsx LookbookTransition) — the outgoing page is
   frozen on a fixed overlay and recedes (scale 0.95 + fade to 0.5) while the incoming page
   glides up from the bottom of the viewport and fades in over the top of it. Both animations
   touch only transform + opacity, which the browser GPU-composites, so the long 1.2s ease-out
   stays perfectly smooth. Shared graceful ease-out curve: cubic-bezier(0.16, 1, 0.3, 1). */

/* Incoming page — sits above the receding overlay and glides up into place. min-height + an
   opaque page background keep it covering the viewport as it rises. */
.lookbook-in {
  position: relative;
  z-index: 9999;
  min-height: 100vh;
  background: var(--bg-light);
  will-change: transform, opacity;
  animation: lookbook-rise 1.2s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes lookbook-rise {
  from { transform: translateY(100vh); opacity: 0; }
  to   { transform: translateY(0);     opacity: 1; }
}

/* Outgoing page — frozen, recedes into the background beneath the incoming page. */
.lookbook-out {
  position: fixed; inset: 0; z-index: 9998; overflow: hidden; pointer-events: none;
  transform-origin: 50% 50%;
  will-change: transform, opacity;
  animation: lookbook-recede 1.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes lookbook-recede {
  from { transform: scale(1);    opacity: 1;   }
  to   { transform: scale(0.95); opacity: 0.5; }
}

/* Reverse pair — RETURNING TO THE HOMEPAGE only. Roles swap: the outgoing page is now on top
   and is pulled straight down out of the viewport, revealing the incoming homepage beneath it,
   which swells back from the receded state (the exact end-state of lookbook-recede) to rest. */
.lookbook-in-rev {
  position: relative;
  z-index: 9997;              /* BELOW the outgoing overlay (9998), which now covers it */
  min-height: 100vh;
  background: var(--bg-light);
  will-change: transform, opacity;
  animation: lookbook-emerge 1.2s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes lookbook-emerge {
  from { transform: scale(0.95); opacity: 0.5; }
  to   { transform: scale(1);    opacity: 1;   }
}

.lookbook-out-rev {
  position: fixed; inset: 0; z-index: 9998; overflow: hidden; pointer-events: none;
  background: var(--bg-light);   /* opaque, so the emerging page never shows through it */
  will-change: transform, opacity;
  animation: lookbook-drop 1.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes lookbook-drop {
  from { transform: translateY(0);     opacity: 1; }
  to   { transform: translateY(100vh); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .lookbook-in  { animation: lookbook-rise 0.3s ease-out both; }
  .lookbook-out { animation: lookbook-recede 0.3s linear forwards; }
  .lookbook-in-rev  { animation: lookbook-emerge 0.3s ease-out both; }
  .lookbook-out-rev { animation: lookbook-drop 0.3s linear forwards; }
}

/* "Gallery curtain" unveil — LANGUAGE SWITCH ONLY (shared.jsx LangFlipTransition). Nothing slides.
   Each block of the re-rendered page is masked by a clip-path inset that retracts across it, so
   the content is unveiled like a curtain drawing back across a lookbook spread. Switching TO
   English sweeps left→right (.lang-dissolve); switching back to Thai sweeps right→left
   (.lang-dissolve-rev), so the two directions mirror each other.
   Images inside get an independent, very subtle 105%→100% settle — the parallax between a still
   curtain edge and a barely-moving image is what sells it as high-end rather than a wipe.
   Both share the same 0.8s cubic-bezier(0.25, 1, 0.5, 1): a hard initial push into a long, slow
   glide out. Blocks stagger so the page unveils in sequence, not all at once. */
:root {
  --curtain-duration: 0.8s;
  --curtain-ease: cubic-bezier(0.25, 1, 0.5, 1);
}

/* The container is masked; it never moves. Forward hides it from the RIGHT edge inward
   (inset(0 100% 0 0)) and retracts to inset(0), unveiling left→right; reverse starts from the
   LEFT edge (inset(0 0 0 100%)) and unveils right→left. Same end state either way. */
@keyframes curtain-unveil {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0);    }
}
@keyframes curtain-unveil-rev {
  from { clip-path: inset(0 0 0 100%); }
  to   { clip-path: inset(0 0 0 0);    }
}

/* The image inside — scale only, never translate. Shrinking (not growing) into place reads as
   the subject settling, the way a lookbook page comes to rest. Direction-agnostic. */
@keyframes curtain-settle {
  from { transform: scale(1.05); }
  to   { transform: scale(1);    }
}

/* Page div's DIRECT children (header, one wrapper div per CMS section, footer) — not `section`
   itself, which sits nested inside EditSection/Reveal wrappers of varying depth.
   fill-mode `backwards`, NOT `both`: it holds the masked start state through the stagger delay,
   then drops the clip-path entirely once the animation ends. `both` would leave inset(0) applied
   forever, permanently clipping the full-bleed carousels that break out past their wrapper box. */
.lang-dissolve > * > * {
  will-change: clip-path;
  animation: curtain-unveil var(--curtain-duration) var(--curtain-ease) backwards;
}
.lang-dissolve-rev > * > * {
  will-change: clip-path;
  animation: curtain-unveil-rev var(--curtain-duration) var(--curtain-ease) backwards;
}
:is(.lang-dissolve, .lang-dissolve-rev) > * > * img {
  will-change: transform;
  animation: curtain-settle var(--curtain-duration) var(--curtain-ease) backwards;
}

/* Delays are set on block and image alike — animation-delay is NOT an inherited property, so a
   nested img cannot pick its block's delay up via `inherit`; it would compute to 0s and the
   settle would race ahead of its own curtain. */
:is(.lang-dissolve, .lang-dissolve-rev) > * > *:nth-child(1),
:is(.lang-dissolve, .lang-dissolve-rev) > * > *:nth-child(1) img   { animation-delay: 0ms; }    /* header */
:is(.lang-dissolve, .lang-dissolve-rev) > * > *:nth-child(2),
:is(.lang-dissolve, .lang-dissolve-rev) > * > *:nth-child(2) img   { animation-delay: 100ms; }  /* hero / lead imagery */
:is(.lang-dissolve, .lang-dissolve-rev) > * > *:nth-child(3),
:is(.lang-dissolve, .lang-dissolve-rev) > * > *:nth-child(3) img   { animation-delay: 150ms; }  /* titles */
:is(.lang-dissolve, .lang-dissolve-rev) > * > *:nth-child(n+4),
:is(.lang-dissolve, .lang-dissolve-rev) > * > *:nth-child(n+4) img { animation-delay: 200ms; }  /* content, prices, actions */

@media (prefers-reduced-motion: reduce) {
  :is(.lang-dissolve, .lang-dissolve-rev) > * > *,
  :is(.lang-dissolve, .lang-dissolve-rev) > * > * img { animation: none; }
}
