/* ==========================================================================
   Studio30 — studio30th.com
   Design tokens below are copied verbatim from the Claude Design project
   (92982995-9658-4775-97c1-2048c2fa117f): tokens/colors.css, typography.css,
   spacing.css, effects.css — merged into one file so the browser makes one
   request instead of a chain of @imports. Keep the variable names identical
   so the two can still be diffed against each other later.
   ========================================================================== */

/* --- Anuphan ------------------------------------------------------------- */
/* One variable file covers weights 100–700; the design system uses 100, 200,
   300, 400, 500 and 700, so this replaces ~800KB of static files with 215KB. */
@font-face {
  font-family: 'Anuphan';
  src: url('fonts/Anuphan-VariableFont_wght.ttf') format('truetype-variations');
  font-weight: 100 700;
  font-style: normal;
  font-display: swap;
}

/* --- Brand CI ------------------------------------------------------------ */
/* 60 / 30 / 10 split:
     60%  Main Base       #F9F9F9  off-white  — page and section backgrounds
     30%  Main Structure  #1F1E1A  charcoal   — logo, headings, body, borders, icons
     10%  Accent          #C85A32  terracotta — buttons, key highlights
   Everything below derives from these three. */
:root {
  --base: #f9f9f9;
  --structure: #1f1e1a;
  --accent: #c85a32;

  /* supporting steps, all mixed from the three above */
  --base-deep: #f1efe9;      /* alternating section fields */
  --accent-dark: #a3441f;    /* accent pressed / hover */
  --accent-tint: #f0d9cd;    /* accent on dark surfaces */

  --gray-100: #efeee9;
  --gray-300: #d9d7d0;
  --gray-500: #8b877f;
  --gray-600: #6b675f;
  --gray-800: #33312c;

  /* Legacy aliases — the component rules below still read these names, and
     they keep this file diffable against the design project's tokens. */
  --black: var(--structure);
  --white: var(--base);
  --off-white: var(--base-deep);
  --clay: var(--accent);
  --clay-dark: var(--accent-dark);
  --sand: var(--accent-tint);

  /* Semantic */
  --surface: var(--base);
  --surface-alt: var(--base-deep);
  --surface-inverse: var(--structure);
  --text-primary: var(--structure);
  --text-secondary: var(--gray-600);
  --text-inverse: var(--base);
  --text-inverse-secondary: rgba(249, 249, 249, .68);
  --border-default: var(--structure);
  --border-subtle-color: var(--gray-300);
  --accent-strong: var(--accent-dark);
}

/* --- tokens/typography.css ---------------------------------------------- */
:root {
  --font-display: 'Anuphan', sans-serif;
  --font-body: 'Anuphan', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  --text-2xs: 11px;
  --text-xs: 12px;
  --text-sm: 14px;
  --text-base: 16px;
  --text-md: 18px;
  --text-lg: 24px;
  --text-xl: 32px;
  --text-2xl: 48px;
  --text-3xl: 64px;
  --text-4xl: clamp(48px, 8vw, 88px);

  --weight-thin: 200;
  --weight-light: 300;
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;

  --tracking-tight: -3px;
  --tracking-snug: -1px;
  --tracking-normal: 0;
  --tracking-wide: 1px;
  --tracking-wider: 2px;
  --tracking-widest: 3px;

  --leading-tight: 0.9;
  --leading-snug: 1.2;
  --leading-normal: 1.6;
  --leading-loose: 1.8;
}

/* --- tokens/spacing.css -------------------------------------------------- */
:root {
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
  --space-20: 80px;
  --space-24: 96px;
  --space-30: 120px;
  --space-40: 160px;
  --container-max: 1200px;
  --container-pad: 20px;
}

/* --- tokens/effects.css -------------------------------------------------- */
/* Studio30 is a hard-edged, hairline-bordered system: radius is 0 everywhere
   except true circles. Depth comes from borders + offset corner brackets +
   translate on hover, never from soft shadows. */
:root {
  --radius-none: 0px;
  --radius-full: 999px;

  --border-hair: 1px solid var(--border-default);
  --border-thick: 2px solid var(--border-default);
  --border-heavy: 3px solid var(--border-default);
  --border-subtle: 1px solid var(--border-subtle-color);

  --shadow-none: none;
  --shadow-soft: 0 2px 20px rgba(31, 30, 26, .10);

  --ease-standard: cubic-bezier(0.25, 0.46, 0.45, 0.94);
  --duration-fast: 0.3s;
  --duration-slow: 0.6s;

  --nav-height: 77px;
}

/* ==========================================================================
   Base
   ========================================================================== */
*,
*::before,
*::after { box-sizing: border-box; }

html { scroll-behavior: smooth; scroll-padding-top: var(--nav-height); }

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--text-primary);
  background: var(--surface);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }

h1, h2, h3, h4 { margin: 0; }
p { margin: 0; }

a { color: inherit; }

::selection { background: var(--black); color: var(--white); }

.container {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-pad);
}

.section { padding: var(--space-30) var(--container-pad); position: relative; }
.section--alt { background: var(--surface-alt); }

.section-head { text-align: center; margin-bottom: var(--space-16); }

.section-title {
  font-family: var(--font-display);
  font-size: 56px;
  font-weight: 100;
  letter-spacing: var(--tracking-snug);
  margin: 0 0 var(--space-4);
}

.section-lede { color: var(--text-secondary); font-size: var(--text-base); }

.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;
}

/* Scroll reveal — .visible is added by the IntersectionObserver in script.js.
   Scoped to .js so that if scripting is off or script.js fails to load, the
   content is simply visible instead of stuck at opacity 0. */
.js .fade-in {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity var(--duration-slow) var(--ease-standard),
              transform var(--duration-slow) var(--ease-standard);
}
.js .fade-in.visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .fade-in { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after { animation-duration: .001s !important; transition-duration: .001s !important; }
}

/* ==========================================================================
   Button — components/buttons/Button.jsx
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-family: var(--font-body);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  border-radius: var(--radius-none);
  transition: all var(--duration-fast) ease;
  position: relative;
  padding: 18px 50px;
  font-size: var(--text-xs);
  background: none;
}

.btn--sm { padding: var(--space-2) var(--space-4); font-size: var(--text-2xs); }

/* Accent carries the primary CTA — this is the main slice of the 10% budget.
   The offset block underneath is what lifts it off the page. It is a hard
   shadow (zero blur, solid --structure), not a soft one: a blurred shadow
   would be the one thing in the design language that has a soft edge. Read
   as a second solid plane behind the button, it keeps the flat, hard-edged
   look while still giving depth. The hover already slides the button up and
   left, so growing the offset to 11px makes it genuinely lift off its own
   shadow rather than just sliding. */
.btn--primary {
  background: var(--accent);
  color: var(--base);
  border: 2px solid var(--accent);
  font-weight: var(--weight-medium);
  box-shadow: 6px 6px 0 var(--structure);
}
.btn--primary:hover {
  background: transparent;
  color: var(--accent-dark);
  border-color: var(--accent-dark);
  transform: translate(-5px, -5px);
  box-shadow: 11px 11px 0 var(--structure);
}
/* Pressed: drop all the way onto the page, shadow collapsed. */
.btn--primary:active {
  transform: translate(6px, 6px);
  box-shadow: 0 0 0 var(--structure);
}

.btn--ghost { background: transparent; color: var(--structure); border: var(--border-thick); }
.btn--ghost:hover { background: var(--accent); color: var(--base); border-color: var(--accent); }

.btn--tag { background: transparent; color: var(--structure); border: var(--border-hair); }
.btn--tag:hover { background: var(--accent); color: var(--base); border-color: var(--accent); transform: translateY(-2px); }

/* offset corner bracket accent on the primary CTA */
.btn--bracket::after {
  content: '';
  position: absolute;
  top: -10px;
  right: -10px;
  width: 20px;
  height: 20px;
  border-top: 2px solid var(--structure);
  border-right: 2px solid var(--structure);
  pointer-events: none;
  transition: border-color var(--duration-fast) ease;
}
.btn--bracket:hover::after { border-color: var(--accent); }

/* ==========================================================================
   NavBar — components/navigation/NavBar.jsx (made sticky, plus mobile menu)
   ========================================================================== */
.navbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  background: rgba(249, 249, 249, .96);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: var(--border-thick);
  padding: var(--space-5) 0;
  transition: box-shadow var(--duration-fast) ease;
}

.navbar.scrolled { box-shadow: var(--shadow-soft); }

/* flex-start + margin-left:auto on .nav-links rather than space-between: with
   the language toggle added as a third child, space-between would have pushed
   the links into the middle of the bar. This keeps logo left, links and toggle
   together on the right. */
.nav-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-pad);
  display: flex;
  justify-content: flex-start;
  align-items: center;
  gap: var(--space-8);
}

.nav-logo {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: 22px;
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-snug);
  color: var(--black);
  text-decoration: none;
}

.nav-logo img { height: 36px; width: auto; }

.nav-links {
  display: flex;
  gap: var(--space-10);
  list-style: none;
  margin: 0;
  margin-left: auto;
  padding: 0;
}

/* Language toggle. Deliberately quiet — a hairline box, secondary grey, one
   step smaller than the nav links, so it reads as a control without competing
   with them. It darkens to full structure colour on hover rather than filling
   with accent, which would make it the loudest thing in the bar. */
.lang-toggle {
  font-family: var(--font-body);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  color: var(--text-secondary);
  background: none;
  border: 1px solid var(--border-subtle-color);
  border-radius: var(--radius-none);
  padding: 6px 10px;
  line-height: 1;
  cursor: pointer;
  flex: none;
  transition: color var(--duration-fast) ease, border-color var(--duration-fast) ease;
}

.lang-toggle:hover { color: var(--structure); border-color: var(--structure); }
.lang-toggle:focus-visible { outline: 2px solid var(--accent-dark); outline-offset: 2px; }

.nav-link {
  color: var(--black);
  text-decoration: none;
  font-size: 13px;
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  position: relative;
  padding-bottom: 4px;
  transition: color var(--duration-fast) ease;
}

.nav-link::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 1px;
  background: var(--black);
  transition: width var(--duration-fast) ease;
}

.nav-link:hover::after,
.nav-link.active::after { width: 100%; }
.nav-link.active { font-weight: var(--weight-bold); }

.menu-toggle {
  display: none;
  flex-direction: column;
  justify-content: space-between;
  width: 26px;
  height: 18px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

.menu-toggle span {
  display: block;
  height: 2px;
  width: 100%;
  background: var(--black);
  transition: transform var(--duration-fast) ease, opacity var(--duration-fast) ease;
}

.menu-toggle.active span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
.menu-toggle.active span:nth-child(2) { opacity: 0; }
.menu-toggle.active span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

/* ==========================================================================
   Hero
   ========================================================================== */
.hero {
  min-height: 92vh;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
  text-align: center;
  padding: calc(var(--nav-height) + var(--space-20)) var(--container-pad) var(--space-20);
  /* 2400x1350 so it stays sharp up to a 2K monitor; 72KB as WebP because the
     white veil hides most compression detail anyway. Note `cover` + centre
     means phones only ever see the middle ~28% of the width. */
  background-image: linear-gradient(rgba(249, 249, 249, .62), rgba(249, 249, 249, .62)),
                    url('img/BG-HERO01.webp');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

.hero-content { position: relative; z-index: 2; max-width: 1100px; }

/* Sized to hold "Style | Space | Solution" on a single line at every width —
   the vw term shrinks it faster than the container does, and nowrap makes a
   bad fit obvious rather than silently wrapping. */
.hero-title {
  font-family: var(--font-display);
  font-size: clamp(26px, 5.4vw, 74px);
  font-weight: var(--weight-medium);
  letter-spacing: -1.5px;
  line-height: 1.05;
  margin: 0 0 var(--space-8);
  white-space: nowrap;
}

/* Also held to one line at every width, by the same method as .hero-title.
   Both the size and the tracking are fluid so there is no jump at any
   breakpoint; they reach their full desktop values (16px / 2px) at ~643px and
   stay there. The floor is 10px — reached at 320px, where the line measures
   ~268px against 280px of available width. Because this line shrinks faster
   than .hero-title does, the title stays at least twice its size and a weight
   step heavier all the way down. */
.hero-subtitle {
  font-size: clamp(10px, calc(3.6vw - 1.2px), 16px);
  letter-spacing: clamp(0.5px, calc(0.42vw - 0.7px), 2px);
  text-transform: uppercase;
  margin: 0 0 var(--space-12);
  white-space: nowrap;
}

/* Ambient drifting marks — the source hero's slow floating shapes */
.hero-objects { position: absolute; inset: 0; z-index: 1; pointer-events: none; }

.hero-objects span {
  position: absolute;
  border: 1px solid var(--black);
  opacity: .18;
}

.hero-objects .o1 { width: 120px; height: 120px; border-radius: var(--radius-full); top: 18%; left: 8%; animation: drift 22s var(--ease-standard) infinite; }
.hero-objects .o2 { width: 60px; height: 60px; border-radius: var(--radius-full); bottom: 22%; right: 12%; animation: drift 18s var(--ease-standard) infinite reverse; }
.hero-objects .o3 { width: 90px; height: 90px; top: 62%; left: 16%; animation: drift 25s var(--ease-standard) infinite; }
.hero-objects .o4 { width: 140px; height: 0; border-width: 1px 0 0 0; top: 30%; right: 8%; animation: drift 20s var(--ease-standard) infinite reverse; }

@keyframes drift {
  0%   { transform: translate(0, 0) rotate(0deg); }
  50%  { transform: translate(30px, -30px) rotate(45deg); }
  100% { transform: translate(0, 0) rotate(0deg); }
}

/* ==========================================================================
   About — founder quote, stats, core values, philosophy
   ========================================================================== */
.quote-block { max-width: 900px; margin: 0 auto var(--space-20); text-align: center; position: relative; }

.quote-mark {
  display: block;
  font-family: var(--font-display);
  font-size: 120px;
  line-height: .6;
  font-weight: var(--weight-thin);
  color: var(--gray-300);
  margin-bottom: var(--space-4);
}

.quote-text {
  font-size: var(--text-xl);
  font-weight: var(--weight-light);
  line-height: 1.4;
  letter-spacing: var(--tracking-snug);
  margin-bottom: var(--space-8);
  text-align: left;
}

.quote-text b { font-weight: var(--weight-semibold); }

.quote-author {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-5);
}

.author-line { width: 50px; height: 1px; background: var(--black); }

.quote-author p {
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  font-weight: var(--weight-medium);
}

.rule { width: 100%; height: 1px; background: var(--black); margin: var(--space-16) 0; }

/* How-we-work strip — replaces the old numeric stat cards (no invented figures) */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 2px;
  background: var(--structure);
  margin-bottom: var(--space-24);
}

.stat-card {
  background: var(--base);
  padding: var(--space-12) var(--space-8);
  position: relative;
  transition: background var(--duration-fast) ease;
}

.stat-card:hover { background: var(--base-deep); }

.stat-mark {
  display: block;
  width: 18px;
  height: 18px;
  border: 1px solid var(--accent);
  transform: rotate(45deg);
  margin-bottom: var(--space-8);
  transition: background var(--duration-fast) ease;
}

.stat-card:hover .stat-mark { background: var(--accent); }

.stat-label {
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  font-weight: var(--weight-semibold);
  margin-bottom: var(--space-3);
}

.stat-desc { font-size: var(--text-sm); color: var(--text-secondary); line-height: 1.55; }

/* Work groups — "Design Work" / "Built Work". The whole point of the portfolio
   is that a visitor sees both, so each group keeps its own heading and grid
   rather than hiding behind a filter that defaults to showing everything. */
.work-group + .work-group { margin-top: var(--space-24); }

.work-group-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-4);
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-8);
  border-bottom: var(--border-thick);
}

.work-group-head h3 {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: var(--weight-light);
  letter-spacing: var(--tracking-snug);
}

.work-group-head p { color: var(--text-secondary); font-size: var(--text-sm); }

.inline-badge {
  display: inline-block;
  background: var(--accent);
  color: var(--base);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  font-weight: var(--weight-semibold);
  padding: 2px 7px;
  white-space: nowrap;
}

/* ==========================================================================
   PortfolioCard — components/cards/PortfolioCard.jsx
   ========================================================================== */
/* Two densities per group. At 50-60 projects a single 4:5 grid means ~27 screens
   of scrolling on a phone, so only the first few run large; the rest drop to a
   square mosaic that fits six across and reveals in pages. Fixed column counts
   (not auto-fit) keep tile size identical whether a group holds 3 or 40. */
.portfolio-grid {
  display: grid;
  gap: var(--space-4);
}

.portfolio-grid--featured { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.portfolio-grid--compact {
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-4);
}

/* Doubled class: the base .portfolio-card rule is declared further down this
   file, and at equal specificity it would otherwise win on source order. */
.portfolio-card.portfolio-card--compact { aspect-ratio: 1 / 1; }

/* the diamond corner mark is clutter at thumbnail size */
.portfolio-card--compact::after { display: none; }

.portfolio-card--compact .portfolio-scrim { padding: var(--space-3); }
.portfolio-card--compact .portfolio-category { display: none; }

.portfolio-card--compact .portfolio-title {
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  line-height: 1.3;
}

.portfolio-card--compact .portfolio-badge {
  top: var(--space-2);
  left: var(--space-2);
  font-size: 9px;
  padding: 3px 5px;
}

.portfolio-card[hidden] { display: none; }

.load-more-wrap { display: flex; justify-content: center; margin-top: var(--space-8); }

.load-more-count {
  display: block;
  text-align: center;
  margin-top: var(--space-3);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-secondary);
}

.portfolio-card {
  position: relative;
  cursor: pointer;
  overflow: hidden;
  aspect-ratio: 4 / 5;
  border: 0;
  padding: 0;
  background: var(--black);
  width: 100%;
  text-align: left;
  font-family: var(--font-body);
}

.portfolio-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1);
  transition: transform var(--duration-slow) ease;
}

.portfolio-card:hover img,
.portfolio-card:focus-visible img { transform: scale(1.05); }

.portfolio-scrim {
  position: absolute;
  inset: 0;
  background: rgba(31, 30, 26, 0);
  transition: background var(--duration-fast) ease;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: var(--space-6);
}

.portfolio-card:hover .portfolio-scrim,
.portfolio-card:focus-visible .portfolio-scrim { background: rgba(31, 30, 26, .58); }

.portfolio-caption {
  color: var(--white);
  opacity: 0;
  transform: translateY(10px);
  transition: all var(--duration-fast) ease;
}

.portfolio-card:hover .portfolio-caption,
.portfolio-card:focus-visible .portfolio-caption { opacity: 1; transform: none; }

.portfolio-category {
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--accent-tint);
  margin-bottom: 6px;
  display: block;
}

.portfolio-title,
.portfolio-count { display: block; }

.portfolio-title { font-size: var(--text-lg); font-weight: var(--weight-light); }

.portfolio-count {
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wide);
  color: var(--text-inverse-secondary);
  margin-top: var(--space-1);
}

.portfolio-card::after {
  content: '';
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  width: 12px;
  height: 12px;
  border: 1px solid var(--white);
  transform: rotate(45deg);
}

/* Marks a project the studio both designed and built — the strongest single
   claim on the page, so it stays visible without needing a hover. */
.portfolio-badge {
  position: absolute;
  top: var(--space-4);
  left: var(--space-4);
  background: var(--accent);
  color: var(--base);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  font-weight: var(--weight-semibold);
  padding: 5px var(--space-3);
}

/* ==========================================================================
   TimelineStep — components/timeline/TimelineStep.jsx
   ========================================================================== */
.timeline {
  position: relative;
  display: flex;
  justify-content: space-between;
  max-width: 900px;
  margin: 0 auto;
  gap: var(--space-4);
}

.timeline-line {
  position: absolute;
  top: 20px;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--black);
  z-index: 0;
}

.timeline-step { position: relative; text-align: center; flex: 1; }

.timeline-dot {
  width: 40px;
  height: 40px;
  background: var(--white);
  border: var(--border-thick);
  margin: 0 auto var(--space-8);
  position: relative;
  z-index: 1;
  transition: all var(--duration-fast) ease;
}

.timeline-step:hover .timeline-dot { background: var(--black); transform: rotate(45deg); }

.timeline-step h4 { font-size: var(--text-md); font-weight: var(--weight-medium); margin-bottom: var(--space-2); letter-spacing: .5px; }
.timeline-step p { font-size: var(--text-sm); color: var(--text-secondary); }

/* ==========================================================================
   Services — a plain "what we do" list. No photos, no links: the job of this
   section is to let a visitor see the four offerings at a glance.
   ========================================================================== */
.services-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  background: var(--gray-300);
  border: var(--border-subtle);
}

.service-item {
  background: var(--surface-alt);
  padding: var(--space-12) var(--space-10);
  position: relative;
  transition: background var(--duration-fast) ease;
}

.service-item:hover { background: var(--base); }

.service-head { display: flex; align-items: baseline; gap: var(--space-4); margin-bottom: var(--space-4); }

/* accent-dark, not accent: at this size the lighter tone measures 4.02:1 on
   the off-white base, below the 4.5:1 needed for small text. */
.service-number {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  color: var(--accent-dark);
  flex: none;
}

.service-item h3 {
  font-size: var(--text-lg);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-snug);
}

.service-item p {
  color: var(--text-secondary);
  line-height: var(--leading-normal);
  font-size: var(--text-base);
  margin-bottom: var(--space-5);
}

.service-caption {
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  font-weight: var(--weight-medium);
  color: var(--text-secondary);
}

/* ==========================================================================
   Contact — components/contact/ContactInfoItem.jsx
   ========================================================================== */
.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1px 1fr;
  gap: var(--space-16);
  max-width: 1100px;
  margin: 0 auto;
}

.contact-divider { background: var(--black); }

.contact-col h2 {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 100;
  margin-bottom: var(--space-10);
}

.info-item { margin-bottom: var(--space-8); padding-left: var(--space-8); position: relative; }

.info-item::before {
  content: '';
  position: absolute;
  left: 0;
  top: 8px;
  width: 10px;
  height: 10px;
  background: var(--black);
  transform: rotate(45deg);
}

.info-item h4 {
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  margin-bottom: var(--space-2);
  font-weight: var(--weight-medium);
}

.info-item p { font-size: var(--text-md); font-weight: var(--weight-light); }

.info-item a:not(.social-btn) {
  font-size: var(--text-md);
  font-weight: var(--weight-light);
  text-decoration: none;
  border-bottom: 1px solid var(--gray-300);
  transition: color var(--duration-fast) ease, border-color var(--duration-fast) ease;
}

.info-item a:not(.social-btn):hover { color: var(--accent-dark); border-color: var(--accent); }

.social-list { display: flex; flex-direction: column; gap: var(--space-3); align-items: flex-start; }

.social-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 10px var(--space-5);
  border: var(--border-hair);
  color: var(--black);
  text-decoration: none;
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  transition: all var(--duration-fast) ease;
}

.social-btn:hover { background: var(--black); color: var(--white); }

/* ==========================================================================
   Footer
   ========================================================================== */
.site-footer { padding: var(--space-16) var(--container-pad); text-align: center; border-top: var(--border-heavy); }

.site-footer p {
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  font-weight: var(--weight-medium);
}

/* ==========================================================================
   Lightbox
   ========================================================================== */
/* The overlay stays in the layout and fades, rather than snapping between
   display:none and display:flex — that jump was what read as harsh. */
/* height:100dvh, not the 100vh that inset:0 gives you: on a phone 100vh is the
   height with the URL bar hidden, so a centred column is laid out taller than
   what you can actually see and the bottom of it — the arrows — ends up behind
   the browser chrome. dvh tracks the visible height instead. Browsers without
   dvh keep the inset:0 sizing, which is the old behaviour. */
.lightbox {
  position: fixed;
  inset: 0;
  height: 100dvh;
  padding: var(--space-4);
  background: rgba(31, 30, 26, .94);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: var(--space-4);
  overflow: hidden;
  /* Horizontal drags anywhere on the overlay are the swipe handler's, not the
     browser's — the image alone is too small a target on a phone. */
  touch-action: pan-y;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.4s var(--ease-standard),
              visibility 0s linear 0.4s;
}

.lightbox.active {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 0.4s var(--ease-standard), visibility 0s;
}

/* min-height:0 + shrink is what guarantees the arrows stay on screen: if the
   photo and the nav row together do not fit, the photo gives up the height,
   never the row. */
.lightbox img {
  max-width: 80vw;
  max-height: 72vh;
  min-height: 0;
  flex-shrink: 1;
  object-fit: contain;
  opacity: 0;
  transform: scale(.965);
  transition: opacity 0.35s var(--ease-standard),
              transform 0.5s var(--ease-standard);
}

.lightbox.active img { opacity: 1; transform: none; }

/* Held while the next image decodes, so the swap is a crossfade instead of
   a blank frame followed by a pop. */
.lightbox.active img.is-swapping { opacity: 0; transform: scale(.99); }

/* Arrow — caption — arrow, as one row. min-width on the caption keeps the two
   arrows a steady distance apart instead of letting a short project name pull
   them together and a long one shove them apart. */
.lightbox-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-6);
  max-width: 90vw;
  flex: none;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.4s var(--ease-standard) 0.1s,
              transform 0.4s var(--ease-standard) 0.1s;
}

.lightbox.active .lightbox-nav { opacity: 1; transform: none; }

.lightbox-caption {
  color: var(--white);
  text-align: center;
  min-width: 220px;
  flex: 1;
}

/* Square, hairline, zero radius — the same ghost-button language as the rest
   of the site, so these read as the one thing to press without a fill that
   would out-shout the photo. */
.lightbox-arrow {
  flex: none;
  width: 52px;
  height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 1px solid rgba(249, 249, 249, .35);
  border-radius: var(--radius-none);
  color: var(--white);
  cursor: pointer;
  padding: 0;
  transition: background var(--duration-fast) ease,
              border-color var(--duration-fast) ease,
              color var(--duration-fast) ease;
}

.lightbox-arrow svg { width: 26px; height: 26px; display: block; }

.lightbox-arrow:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--base);
}

.lightbox-arrow:active { background: var(--accent-dark); border-color: var(--accent-dark); }
.lightbox-arrow:focus-visible { outline: 2px solid var(--base); outline-offset: 3px; }

/* An explicit display above beats the UA's [hidden] rule, so single-image
   albums need this to actually drop the arrows. */
.lightbox-arrow[hidden] { display: none; }

.lightbox-category {
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--accent-tint);
}

.lightbox-title { font-size: var(--text-lg); font-weight: var(--weight-light); }

.lightbox-counter { font-size: var(--text-xs); letter-spacing: var(--tracking-wide); color: var(--text-inverse-secondary); margin-top: var(--space-1); }

/* Close is the only control still floating over the image. */
.lightbox-close {
  position: absolute;
  top: var(--space-6);
  right: var(--space-8);
  font-size: 40px;
  background: none;
  border: 0;
  color: var(--white);
  cursor: pointer;
  font-family: var(--font-body);
  line-height: 1;
  padding: var(--space-3);
  opacity: .7;
  transition: opacity var(--duration-fast) ease;
}

.lightbox-close:hover { opacity: 1; }
.lightbox-close:focus-visible { outline: 2px solid var(--base); outline-offset: 2px; opacity: 1; }

body.no-scroll { overflow: hidden; }

/* ==========================================================================
   Responsive — the design kit shipped no breakpoints, these are additions
   ========================================================================== */
@media (max-width: 1100px) {
  .portfolio-grid--compact { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

@media (max-width: 1024px) {
  .contact-grid { grid-template-columns: 1fr; gap: var(--space-12); }
  .contact-divider { height: 1px; width: 100%; }
}

@media (max-width: 820px) {
  .portfolio-grid--featured { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .portfolio-grid--compact { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .services-list { grid-template-columns: 1fr; }
  .service-item { padding: var(--space-10) var(--space-6); }
}

@media (max-width: 768px) {
  :root { --nav-height: 69px; }

  .section { padding: var(--space-16) var(--container-pad); }

  .menu-toggle { display: flex; }

  /* .nav-links goes position:fixed below, so the only children left in the bar
     are the logo, the toggle and the burger. Source order puts the burger
     first; order swaps them so the language toggle sits before it, and
     margin-left:auto carries the pair to the right edge. */
  .nav-inner { gap: var(--space-5); }
  .lang-toggle { order: 2; margin-left: auto; padding: 8px 12px; }
  .menu-toggle { order: 3; }

  .nav-links {
    position: fixed;
    top: var(--nav-height);
    left: 0;
    right: 0;
    background: var(--white);
    border-bottom: var(--border-thick);
    flex-direction: column;
    gap: 0;
    padding: var(--space-2) var(--container-pad) var(--space-6);
    transform: translateY(-140%);
    transition: transform var(--duration-fast) var(--ease-standard);
  }

  .nav-links.active { transform: none; }
  .nav-links li { padding: var(--space-3) 0; border-bottom: var(--border-subtle); }
  .nav-link { font-size: var(--text-sm); }

  .section-title { font-size: var(--text-xl); }
  .quote-text { font-size: var(--text-lg); }
  .quote-mark { font-size: 80px; }
  .contact-col h2,
  .work-group-head h3 { font-size: var(--text-xl); }

  .stats-grid { grid-template-columns: 1fr; margin-bottom: var(--space-16); }
  .stat-card { padding: var(--space-8) var(--space-6); }

  /* timeline flips to vertical */
  .timeline { flex-direction: column; gap: var(--space-8); max-width: 420px; }
  .timeline-line { top: 0; bottom: 0; left: 20px; right: auto; width: 2px; height: auto; }
  .timeline-step { display: grid; grid-template-columns: 40px 1fr; gap: var(--space-6); text-align: left; align-items: start; }
  .timeline-dot { margin: 0; }

  /* Featured goes full width so the lead projects still land; the mosaic keeps
     three across, which is what turns 60 projects from 27 screens into ~4. */
  .portfolio-grid--featured { grid-template-columns: 1fr; }
  .portfolio-grid--compact { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
  .portfolio-card--compact .portfolio-title { font-size: 10px; }
  .lightbox img { max-width: 92vw; max-height: 58vh; }
  .lightbox-close { right: var(--space-4); font-size: 32px; }

  /* Arrows stay put but tighten up, and the caption gives up its min-width so
     the row still fits across a 320px screen. */
  .lightbox-nav { gap: var(--space-4); width: 92vw; }
  .lightbox-caption { min-width: 0; }
  .lightbox-arrow { width: 46px; height: 46px; }
  .lightbox-arrow svg { width: 22px; height: 22px; }
}

/* ==========================================================================
   Thai
   ==========================================================================
   The tracking tokens are tuned for Latin and do not transfer. Thai stacks
   vowels and tone marks above and below the base consonant: negative tracking
   pinches the stack into the neighbouring character, and wide tracking breaks
   a word into loose syllables, because Thai has no spaces between words to
   re-establish where one ends. Both are neutralised here, at the token level
   so every component picks the change up at once.

   Set by i18n.js on <html>, which is also what makes this revert cleanly when
   the visitor switches back. */
html[lang="th"] {
  --tracking-tight: 0;
  --tracking-snug: 0;
  --tracking-wide: 0;
  --tracking-wider: .5px;
  --tracking-widest: 1px;
}

/* Untranslated by decision, so they keep the Latin tracking they were set
   with. The hero title and subtitle need no entry — both use literal values
   rather than the tokens. */
html[lang="th"] .nav-logo { letter-spacing: -1px; }
html[lang="th"] .site-footer p { letter-spacing: 2px; }
html[lang="th"] .timeline-step h4 { letter-spacing: 0; }

/* Room for the diacritics: 1.4 clips the tone marks off a 32px Thai line. */
html[lang="th"] .quote-text { line-height: 1.55; }
