/* Shared structure for both service themes.
   Theme files (theme-design.css / theme-performance.css) set every token used here. */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 88px; /* the 76px header row + 12px air (76px below 1024: the 64px row + air) */
  --wipe-y: 38px;           /* toggle centre = the middle of the header row (32px below 1024) */
  --gutter: 16px;
}
@media (min-width: 768px) { html { --gutter: 32px; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

img, svg, video { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
p { margin: 0; text-wrap: pretty; }
ul, ol { margin: 0; padding: 0; list-style: none; }

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  line-height: var(--display-leading);
  text-transform: var(--display-case);
  text-wrap: balance;
}

:focus-visible { outline: 2px solid var(--focus, currentColor); outline-offset: 3px; }

.skip-link {
  position: absolute; left: 16px; top: -64px; z-index: 100;
  padding: 10px 16px; border-radius: var(--radius-full);
  background: var(--ink); color: var(--bg);
}
.skip-link:focus { top: 12px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.container { width: min(100% - 32px, var(--max-width)); margin-inline: auto; }
@media (min-width: 768px) { .container { width: min(100% - 64px, var(--max-width)); } }

.section { padding-block: var(--section-y); }

/* ---------- Buttons (structure only; theme sets the look) ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 44px; padding: 0 20px;
  border: 0; border-radius: var(--radius-full);
  font: var(--btn-font); letter-spacing: var(--btn-tracking); text-transform: var(--btn-case);
  white-space: nowrap; cursor: pointer;
  transition: transform .15s ease, background-color .2s ease, color .2s ease, box-shadow .2s ease, border-color .2s ease;
}
.btn:active { transform: translateY(1px) scale(.98); }
.btn--lg { min-height: 52px; padding: 0 26px; }

/* ---------- Header ----------
   One sticky row at every width: the brand, then the 3-way service toggle.
   Nothing else lives in it (Client login is in each page's footer), and
   nothing tucks: the row just blurs once the page scrolls (.is-scrolled,
   script.js).
   ≥1024  76px row. The 560×40 toggle is centred in the container: the
          empty third grid column mirrors the brand column.
   ≤1023  64px row. The toggle sits on the right, filling the rest of the
          row up to 560px. Breakpoints come from measured widths: the widest
          full label (the design theme's mono caps "Performance Marketing",
          161px) needs the full 560px toggle; the lockup is 140px wide, the
          symbol 24px.
            ≥800     lockup + full labels   (32 + 140 + 16 + 560 + 32 = 780px)
            640–799  symbol + full labels   (16 + 24 + 16 + 560 + 16 = 632px)
            ≤639     symbol + "Web · Ads · AI", the toggle fills the rest
   Brand and toggle share geometry on every theme so the wipe reads as one
   page flipping. */
.site-header {
  --hdr-row: 76px;
  position: sticky; top: 0; z-index: 50;
}
/* Row background (blur once scrolled). */
.site-header::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  pointer-events: none;
  transition: background-color .25s ease, box-shadow .25s ease;
}
.site-header.is-scrolled::before {
  background: var(--header-bg);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  box-shadow: 0 1px 0 var(--line);
}

.site-header__inner {
  position: relative;
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  grid-template-areas: "brand bar lang";
  align-items: center; column-gap: 16px;
  min-height: var(--hdr-row);
}
/* font-optical-sizing: none — the Performance page loads Inter with an opsz axis,
   which made its brand 0.6px narrower than the static Inter on the other two. */
.brand { grid-area: brand; position: relative; z-index: 2; display: inline-flex; align-items: center; gap: 10px; justify-self: start; font-family: var(--font-body); font-weight: 700; font-size: 16px; letter-spacing: -0.01em; font-optical-sizing: none; }
/* Three Signals lockup (inline SVG, outlined Geist Mono wordmark): 24px tall in the
   76px row, so the symbol sits on its exact 3px-per-cell grid. Bars + wordmark use
   currentColor (the theme ink); the lifted bar is always neon. */
.site-header .brand { color: var(--ink); }
.brand__logo { display: block; flex: none; width: auto; height: 24px; overflow: visible; }

/* ---------- Service bar and toggle ---------- */
.service-bar { grid-area: bar; position: relative; display: flex; justify-content: center; }
.service-toggle {
  position: relative; flex: none;
  display: inline-grid; grid-template-columns: repeat(3, 1fr); gap: 2px;
  padding: 3px; border-radius: var(--radius-full);
  background: var(--toggle-bg); border: var(--toggle-border);
}
.service-toggle a {
  display: grid; place-items: center;
  padding: 0 8px; border-radius: var(--radius-full);
  font: var(--toggle-font); letter-spacing: var(--toggle-tracking, 0); text-transform: var(--toggle-case, none);
  text-align: center; white-space: nowrap; color: var(--toggle-ink);
  transition: color .2s ease, background-color .2s ease, box-shadow .2s ease;
}
.service-toggle a:hover { color: var(--toggle-ink-hover); }
.service-toggle a[aria-current="page"] {
  background: var(--toggle-active-bg); color: var(--toggle-active-ink); box-shadow: var(--toggle-active-shadow);
}
/* ---------- Thai pages (/th/*, built by scripts/build-th.mjs) ----------
   Thai is never tracked: letter-spacing pulls the stacked vowel and tone marks
   off their consonants, and the themes track caps labels and tighten display
   headings. Display lines also need room above for those marks. */
html[lang="th"] body *:not([lang="en"]) { letter-spacing: normal !important; }
html[lang="th"] :is(h1, h2, h3) { line-height: 1.25; }

/* ---------- Language toggle (EN | TH) ----------
   Sits in the header's third column, styled from the same theme tokens as the
   service toggle so it reads as its smaller sibling. */
.lang-toggle {
  grid-area: lang; justify-self: end; position: relative; z-index: 2;
  display: inline-grid; grid-template-columns: 1fr 1fr; gap: 2px;
  padding: 3px; border-radius: var(--radius-full);
  background: var(--toggle-bg); border: var(--toggle-border);
}
.lang-toggle a {
  display: grid; place-items: center; min-width: 36px; height: 30px; padding: 0 6px;
  border-radius: var(--radius-full);
  font: var(--toggle-font); letter-spacing: var(--toggle-tracking, 0);
  color: var(--toggle-ink); white-space: nowrap;
  transition: color .2s ease, background-color .2s ease;
}
.lang-toggle a:hover { color: var(--toggle-ink-hover); }
.lang-toggle a[aria-current="true"] {
  background: var(--toggle-active-bg); color: var(--toggle-active-ink); box-shadow: var(--toggle-active-shadow);
}

@media (min-width: 1024px) {
  /* 560×40 on every theme (design = 12px mono caps, performance/ai = 13px
     Inter): 3 × 182px segments + 2 × 2px gaps + 3px padding + 2px border. */
  .service-toggle a { min-width: 182px; height: 30px; }
}
@media (max-width: 1023px) {
  html { --wipe-y: 32px; scroll-padding-top: 76px; }
  .site-header { --hdr-row: 64px; }
  .site-header__inner { grid-template-columns: auto minmax(0, 1fr) auto; grid-template-areas: "brand bar lang"; column-gap: 8px; }
  .service-bar { justify-content: flex-end; }
  .lang-toggle a { min-width: 32px; height: 40px; padding: 0 4px; }
  /* 50px tall: 40px segments (touch-sized) + 3px padding + 2px border. */
  .service-toggle { width: 100%; max-width: 560px; }
  .service-toggle a { min-width: 0; height: 40px; }
}
/* Symbol only (the three bars): the link clips the full-size lockup to its
   first 24px (max-width: none, or the global svg rule would shrink the whole
   lockup into the box). Its accessible name ("Arctis Labs, … home") is unchanged. */
@media (max-width: 799px) {
  .site-header .brand { width: 24px; overflow: hidden; }
  .site-header .brand__logo { max-width: none; }
}
/* Short labels ("Web" · "Ads" · "AI") where the full ones no longer fit a
   third of the toggle. The full label stays in .roll__a, visually hidden, so
   it is still the link's accessible name; the short copy is aria-hidden. */
.service-toggle .st-short { display: none; }
@media (max-width: 639px) {
  .service-toggle .st-short { display: inline; }
  .service-toggle .roll__b .st-full { display: none; }
  .service-toggle .roll__a .st-full {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}

/* ---------- Label roll (buttons, toggle, nav pills, footer links) ----------
   <span class="roll"><span class="roll__a">Label</span><span class="roll__b" aria-hidden="true">Label</span></span> */
.roll { position: relative; display: inline-grid; overflow: hidden; line-height: 1.2; vertical-align: top; }
.roll__a, .roll__b { grid-area: 1 / 1; transition: transform .42s var(--ease-out); }
.roll__b { transform: translateY(110%); }
@media (hover: hover) {
  :is(.btn, a):hover .roll__a { transform: translateY(-110%); }
  :is(.btn, a):hover .roll__b { transform: none; }
  .btn:hover .btn__icon svg { transform: rotate(360deg); }
}
.btn__icon svg { transition: transform .55s var(--ease-pop); }

/* ---------- Marquee ----------
   <div class="marquee" data-marquee data-speed="55" [data-reverse]><ul class="marquee__track">…</ul></div>
   motion.js repeats + clones the items and sets --dur. Without motion the
   track wraps and clones are hidden. */
.marquee {
  --mq-gap: 16px;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.marquee__track { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--mq-gap); }
.js-motion .marquee__track { flex-wrap: nowrap; justify-content: flex-start; width: max-content; }
.js-motion .marquee.is-ready .marquee__track { padding-right: var(--mq-gap); animation: mq var(--dur, 40s) linear infinite; }
.js-motion .marquee.is-ready[data-reverse] .marquee__track { animation-direction: reverse; }
.marquee.is-off .marquee__track { animation-play-state: paused; }
@keyframes mq { to { transform: translateX(-50%); } }
:where(html:not(.js-motion)) .marquee { -webkit-mask-image: none; mask-image: none; }
:where(html:not(.js-motion)) .marquee [data-clone] { display: none; }

/* ---------- Reveal on scroll (motion.js adds .visible, then .is-revealed) ---------- */
.js-motion .reveal:not(.visible) { opacity: 0; transform: translateY(var(--reveal-y, 48px)); }
.js-motion .reveal:not(.is-revealed) {
  transition: opacity .6s var(--ease-expo), transform .8s var(--ease-expo);
  transition-delay: var(--d, 0ms);
}
.work-card.reveal, .price-card.reveal, .d-final.reveal { --reveal-y: 140px; }
/* No JS / reduced motion: static and visible (also beats legacy styles.css). */
:where(html:not(.js-motion)) .reveal { opacity: 1; transform: none; }

/* ---------- Line-split headings ([data-split]) ---------- */
:is([data-split], [data-intro]) .w { display: inline-block; }
.js-motion [data-split]:not(.visible) .w { opacity: 0; transform: translateY(.3em); }
[data-split].visible .w {
  transition: opacity .5s var(--ease-out), transform .6s var(--ease-out);
  transition-delay: calc(var(--line, 0) * 80ms);
}

/* ---------- Intro (motion.js: .intro-go after fonts, .intro-done when finished) ----------
   Arriving through the service toggle (.via-switch) keeps the header in place
   so the wipe has something to reveal. */
.js-motion:not(.intro-done) [data-intro]:not(.is-split) { opacity: 0; }
.js-motion:not(.intro-done) [data-intro] .w { opacity: 0; transform: translateY(.14em); }
.intro-go:not(.intro-done) [data-intro] .w { animation: rise .5s var(--ease-out) both; animation-delay: calc(120ms + var(--i, 0) * 70ms); }
.js-motion:not(.intro-done):not(.via-switch) .site-header__inner { opacity: 0; transform: translateY(-24px); }
.intro-go:not(.intro-done):not(.via-switch) .site-header__inner { animation: rise .45s var(--ease-out) both; }
@keyframes rise { to { opacity: 1; transform: none; } }
@keyframes pop { to { opacity: 1; transform: rotate(-12deg); } }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

@media print {
  .reveal, :is([data-split], [data-intro]) .w, .site-header__inner { opacity: 1 !important; transform: none !important; }
}

/* ---------- FAQ (native <details>) ---------- */
.faq-list { display: grid; gap: 12px; }
.faq-list details > summary { list-style: none; cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 24px; }
.faq-list details > summary::-webkit-details-marker { display: none; }
.faq-list .faq-icon { flex: none; transition: transform .25s ease; }
.faq-list details[open] .faq-icon { transform: rotate(45deg); }

/* ---------- Toggle page transition ---------- */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: none; }
::view-transition-new(root) { animation: service-wipe .7s cubic-bezier(.7,0,.2,1) both; }
@keyframes service-wipe {
  from { clip-path: circle(0 at var(--wipe-x, 50%) var(--wipe-y)); }
  to   { clip-path: circle(160vmax at var(--wipe-x, 50%) var(--wipe-y)); }
}

@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
  .roll__b { display: none; }
  .roll__a { transform: none !important; }
  /* Infinite loops stop outright rather than running one .01ms cycle. */
  .marquee__track, .d-burst, .p-orb { animation: none !important; }
}
