/* Hallmark · pre-emit critique: P5 H4 E4 S5 R5 V5
 * Hallmark · genre: editorial · macrostructure: Portfolio Grid · theme: Atelier-register (catalog)
 * enrichment: E-analog rotating-photo hero (H2 Split Diptych, ratio 5/7) — real portfolio
 * photos now exist, so the hero upgraded from typography-only; images auto-crossfade
 * (--dur-ambient), pause on hover, hold static under prefers-reduced-motion
 * nav: N9 Edge-aligned minimal + inline link row (Layanan/Portfolio/Tentang/FAQ,
 * added post-launch for wayfinding as more homepage sections shipped) · footer: Ft6 Letter close
 * outlier (Geist Mono) roles: taxonomy label (filter chip + tile tag) · process stage numeral ·
 * stat-strip figures
 * contrast: pass (40–41) · nav/footer/hero: pass (42–45) · honest: pass (46) · chrome: pass (47)
 * tokens: pass (48) · responsive: pass (49) · mobile: pass (34, 49, 50–57)
 * studied: yes · DNA-source: url (sweetescape.com/en) — hero rotator pattern only,
 * own photography + Dmzphoto tokens/type throughout, no SweetEscape assets or copy used
 */

@import url("tokens.css");

/* ---------- Reset & base ---------- */

*, *::before, *::after { box-sizing: border-box; }
html, body { overflow-x: clip; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  padding-inline: max(0px, env(safe-area-inset-left));
  padding-bottom: max(0px, env(safe-area-inset-bottom));
}

img { max-width: 100%; display: block; }

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 600;
  font-style: normal;
  line-height: 1.08;
  letter-spacing: -0.01em;
  margin: 0;
  overflow-wrap: anywhere;
  min-width: 0;
}

p { margin: 0; }

a { color: inherit; }

.muted { color: var(--color-muted); }

.small-caps {
  font-family: var(--font-body);
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1.05;
}

/* Outlier face (Geist Mono) is reserved for exactly three roles on this page:
   (1) category/taxonomy labels — filter chips + tile tags, (2) process stage numerals,
   (3) stat-strip figures. */

/* ---------- Layout helpers ---------- */

.wrap {
  max-width: 74rem;
  margin-inline: auto;
  padding-inline: var(--page-gutter);
}

.measure { max-width: 62ch; }

section { padding-block: var(--space-2xl); }

/* Section head — S2 Hanging: floats in negative space, no rule */
.head-hang {
  padding-block: var(--space-3xl) var(--space-lg);
}
.head-hang h2 { font-size: var(--text-2xl); }
.head-hang p { margin-top: var(--space-sm); }

@media (max-width: 60rem) {
  .head-hang { padding-block: var(--space-2xl) var(--space-md); }
}

/* Section head reveal — starting state only. Motion runs the fade+rise from
   here (see js/script.js initSectionReveal) and js/script.js drops this class
   when it lands. Gated behind .js-reveal so headers stay visible if
   JavaScript never runs, and neutralised under reduce-motion in case the
   class is applied before that branch gets a chance to remove it. */
.js-reveal .head-hang.reveal-pending {
  opacity: 0;
  transform: translateY(16px);
}
@media (prefers-reduced-motion: reduce) {
  .js-reveal .head-hang.reveal-pending { opacity: 1; transform: none; }
}

/* ---------- Focus ring ---------- */

:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 3px;
  transition: none;
}

/* ---------- Buttons / CTAs ---------- */

.cta-outline {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  padding: var(--space-sm) var(--space-lg);
  border: var(--rule-hair) solid var(--color-ink);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  letter-spacing: 0.02em;
  text-decoration: none;
  white-space: nowrap;
  min-height: 44px;
  background: transparent;
  color: var(--color-ink);
  transition: background-color var(--dur-short) var(--ease-out),
              color var(--dur-short) var(--ease-out),
              transform 100ms var(--ease-out);
}
.cta-outline:hover { background: var(--color-ink); color: var(--color-paper); }
.cta-outline:active { transform: translateY(1px); }
.cta-outline:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 3px; }
.cta-outline:disabled { opacity: 0.55; cursor: not-allowed; }
.cta-outline--accent { border-color: var(--color-accent); color: var(--color-accent); }
.cta-outline--accent:hover { background: var(--color-accent); color: var(--color-paper); }

/* Typographic link — C3 */
.link {
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
  text-decoration: none;
  white-space: nowrap;
  border-bottom: 1px solid var(--color-rule);
  padding-block-end: 0.1em;
  transition: border-color var(--dur-short) var(--ease-out),
              color var(--dur-short) var(--ease-out);
}
.link:hover { border-color: var(--color-accent); color: var(--color-accent); }

/* ---------- Nav — N9 Edge-aligned minimal ---------- */

.nav-edge {
  padding: var(--space-md) var(--page-gutter) var(--space-sm);
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  background: color-mix(in oklch, var(--color-paper) 92%, transparent);
  backdrop-filter: blur(6px);
}
.nav-edge__top {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-md);
}

/* ---------- Nav rail — tubelight pill ----------
   Port of the React "tubelight-navbar" (framer-motion layoutId + Tailwind)
   to plain CSS/JS: one floating pill, one lamp that slides to the active
   item. The lamp is a single absolutely-positioned element whose x/width JS
   sets — that stands in for the shared-element transition. Colour comes
   from tokens.css, so the tube glows oxblood on ivory, not neon on black. */

/* Below 60rem the pill drops to a line of its own — wordmark and actions
   already fill the row. From 60rem up all three sit on one line, the pill
   optically centred by the 1fr columns either side of it. */
.tubelight-nav {
  display: flex;
  justify-content: center;
  order: 3;
  flex-basis: 100%;
}

@media (min-width: 60rem) {
  .nav-edge { padding-block-start: var(--space-lg); }
  .nav-edge__top {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
  }
  .tubelight-nav { order: 0; flex-basis: auto; }
  .nav-edge__right { justify-self: end; }
}

.tubelight-nav__pill {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3xs);
  max-width: 100%;
  padding: var(--space-3xs);
  border: var(--rule-hair) solid var(--color-rule);
  border-radius: var(--radius-pill);
  background: color-mix(in oklch, var(--color-paper) 74%, transparent);
  backdrop-filter: blur(10px);
  box-shadow: 0 6px 20px color-mix(in oklch, var(--color-ink) 7%, transparent);
}

.tubelight-nav__item {
  position: relative;
  z-index: var(--z-base);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: var(--space-2xs) var(--space-lg);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  letter-spacing: 0.03em;
  color: var(--color-ink-2);
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--dur-short) var(--ease-out);
}
.tubelight-nav__item:hover { color: var(--color-accent); }
.tubelight-nav__item:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
.tubelight-nav__item.is-active { color: var(--color-accent); font-weight: 600; }

.tubelight-nav__icon {
  display: none;
  width: 18px;
  height: 18px;
}

/* The lamp. JS writes --lamp-x / --lamp-w onto the pill; until it does the
   lamp stays transparent, so a JS-less page just shows plain nav links. */
.tubelight-nav__lamp {
  position: absolute;
  left: 0;
  top: var(--space-3xs);
  bottom: var(--space-3xs);
  width: var(--lamp-w, 0px);
  transform: translateX(var(--lamp-x, 0px));
  border-radius: var(--radius-pill);
  background: color-mix(in oklch, var(--color-accent) 7%, transparent);
  opacity: 0;
  pointer-events: none;
  transition: transform var(--dur-long) var(--ease-lamp),
              width var(--dur-long) var(--ease-lamp),
              opacity var(--dur-short) var(--ease-out);
}
.tubelight-nav__pill.is-lit .tubelight-nav__lamp { opacity: 1; }
/* Placement, not travel: the lamp still fades up, it just does not slide
   across the pill. JS adds this for load, relayout and font swaps. */
.tubelight-nav__pill.is-placing .tubelight-nav__lamp {
  transition: opacity var(--dur-short) var(--ease-out);
}

/* The tube riding the top edge of the pill... */
.tubelight-nav__lamp::before {
  content: "";
  position: absolute;
  top: calc(-1 * var(--space-3xs) - 3px);
  left: 50%;
  translate: -50% 0;
  width: 2rem;
  height: 3px;
  border-radius: var(--radius-pill) var(--radius-pill) 0 0;
  background: var(--color-accent);
}
/* ...and its halo, the part that reads as "lit". */
.tubelight-nav__lamp::after {
  content: "";
  position: absolute;
  top: calc(-1 * var(--space-3xs) - 14px);
  left: 50%;
  translate: -50% 0;
  width: 3.5rem;
  height: 1.75rem;
  border-radius: var(--radius-pill);
  background: radial-gradient(closest-side,
    color-mix(in oklch, var(--color-accent) 34%, transparent),
    transparent);
  filter: blur(5px);
}

/* Under 48rem the labels give way to icons — but they stay in the a11y tree
   so the links keep their (translated) accessible names. */
@media (max-width: 48rem) {
  .tubelight-nav__label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .tubelight-nav__icon { display: block; }
  .tubelight-nav__item { padding-inline: var(--space-md); }
}

@media (max-width: 26rem) {
  .nav-edge__top { gap: var(--space-sm); }
  .tubelight-nav__item { padding-inline: var(--space-sm); }
}

@media (prefers-reduced-motion: reduce) {
  .tubelight-nav__lamp { transition: opacity 150ms linear; }
}

/* Across pages there is no shared DOM to ease — the document is replaced, so
   the lamp can only reappear where it belongs (initTubelightNav places it
   without easing). A cross-document view transition gives back the travel:
   the browser matches the old lamp to the new one by name and tweens between
   them, so clicking Tentang from Portfolio slides Portfolio → Tentang.
   Only the lamp is named, so the page swap itself is untouched; browsers
   without view transitions (Firefox today) just navigate as before. */
@view-transition { navigation: auto; }

.tubelight-nav__lamp { view-transition-name: nav-lamp; }

::view-transition-old(root),
::view-transition-new(root) { animation: none; }

::view-transition-group(nav-lamp) {
  animation-duration: var(--dur-long);
  animation-timing-function: var(--ease-lamp);
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(nav-lamp),
  ::view-transition-old(nav-lamp),
  ::view-transition-new(nav-lamp) { animation: none; }
}

.nav-edge .wordmark {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  text-decoration: none;
  color: var(--color-ink);
  white-space: nowrap;
}
.nav-edge__right {
  display: flex;
  align-items: center;
  gap: var(--space-md);
}
.nav-edge .cta-outline { padding: var(--space-xs) var(--space-md); }
.nav-edge .cta-outline .full { display: inline; }
.nav-edge .cta-outline .short { display: none; }

/* ---------- Language switcher — custom dropdown (button + menu) ---------- */

.lang-switch { position: relative; }

.lang-switch__trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--color-ink-2);
  background: transparent;
  border: var(--rule-hair) solid var(--color-rule);
  border-radius: var(--radius-sm);
  padding: var(--space-2xs) var(--space-sm);
  min-height: 36px;
  cursor: pointer;
  transition: border-color var(--dur-short) var(--ease-out),
              color var(--dur-short) var(--ease-out);
}
.lang-switch__trigger:hover { border-color: var(--color-ink-2); color: var(--color-ink); }
.lang-switch__trigger:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }

.lang-switch__caret {
  font-size: 0.65em;
  line-height: 1;
  transition: transform var(--dur-short) var(--ease-out);
}
.lang-switch.is-open .lang-switch__caret { transform: rotate(180deg); }

.lang-switch__menu {
  position: absolute;
  top: calc(100% + var(--space-xs));
  right: 0;
  z-index: var(--z-dropdown);
  display: flex;
  flex-direction: column;
  min-width: 11rem;
  padding: var(--space-2xs);
  margin: 0;
  background: var(--color-paper);
  border: var(--rule-hair) solid var(--color-rule);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px color-mix(in oklch, var(--color-ink) 14%, transparent);
  opacity: 0;
  transform: translateY(-4px);
  pointer-events: none;
  transition: opacity var(--dur-short) var(--ease-out),
              transform var(--dur-short) var(--ease-out);
}
/* The display:flex above beats the UA [hidden] rule — without this the
   closed menu sits invisibly over the phone nav row and eats its taps. */
.lang-switch__menu[hidden] { display: none; }
.lang-switch.is-open .lang-switch__menu {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

.lang-switch__option {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 44px;
  padding-inline: var(--space-sm);
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--color-ink-2);
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--dur-short) var(--ease-out),
              color var(--dur-short) var(--ease-out);
}
.lang-switch__option:hover,
.lang-switch__option:focus-visible {
  background: var(--color-ink);
  color: var(--color-paper);
  outline: none;
}
.lang-switch__option[aria-current="true"] { color: var(--color-accent); font-weight: 600; }
.lang-switch__option[aria-current="true"]:hover,
.lang-switch__option[aria-current="true"]:focus-visible { color: var(--color-paper); }

@media (prefers-reduced-motion: reduce) {
  .lang-switch__menu { transition: opacity 150ms linear; }
  .lang-switch__caret { transition: none; }
}

@media (max-width: 26rem) {
  .nav-edge .cta-outline .full { display: none; }
  .nav-edge .cta-outline .short { display: inline; }
  .lang-switch__trigger { font-size: var(--text-xs); padding: var(--space-3xs) var(--space-2xs); }
}

/* ---------- Hero ---------- */

.hero {
  padding-block: var(--space-2xl) var(--space-3xl);
}
.hero--split {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-2xl);
  align-items: center;
}
@media (min-width: 60rem) {
  .hero--split { grid-template-columns: 5fr 7fr; }
}
.hero__copy { min-width: 0; }
.hero__eyebrow {
  color: var(--color-muted);
  margin: 0 0 var(--space-md);
}
.hero__display {
  /* headline runs 51–90 chars — capped at --text-display-s per typography.md sizing rule */
  font-size: var(--text-display-s);
  max-width: 28ch;
}
.hero__lede {
  margin-top: var(--space-lg);
  font-size: var(--text-md);
  line-height: 1.55;
  color: var(--color-ink-2);
}
.hero__actions {
  margin-top: var(--space-xl);
  display: flex;
  align-items: center;
  gap: var(--space-xl);
  flex-wrap: wrap;
}

/* Hero entrance — starting state only; Motion runs it (see js/script.js
   initHeroReveal), and that function removes `reveal-ready` from the hero
   when the animation lands, which is what switches these rules off.

   Unlike the scroll reveals, this one cannot wait for JS to apply the hidden
   class: the hero is the first thing painted, so a class added at
   DOMContentLoaded would show it, hide it, then animate it — a visible
   blink. So the hidden state ships in the markup (`reveal-ready`) and in the
   stylesheet, behind the .js-reveal gate that each page sets inline in
   <head> before the body is parsed. */
.js-reveal .hero.reveal-ready .hero__eyebrow,
.js-reveal .hero.reveal-ready .hero__display,
.js-reveal .hero.reveal-ready .hero__lede,
.js-reveal .hero.reveal-ready .hero__actions,
.js-reveal .hero.reveal-ready .hero__rotator {
  opacity: 0;
  transform: translateY(10px);
}

@media (prefers-reduced-motion: reduce) {
  .js-reveal .hero.reveal-ready .hero__eyebrow,
  .js-reveal .hero.reveal-ready .hero__display,
  .js-reveal .hero.reveal-ready .hero__lede,
  .js-reveal .hero.reveal-ready .hero__actions,
  .js-reveal .hero.reveal-ready .hero__rotator {
    opacity: 1;
    transform: none;
  }
}

/* ---------- Hero photo rotator — auto-crossfade through real portfolio work ---------- */

.hero__rotator {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-sm);
  overflow: hidden;
  isolation: isolate;
}
.hero__rotator-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--dur-ambient) var(--ease-in-out);
}
.hero__rotator-img.is-active { opacity: 1; }

/* Blend the photo's inner edge into the paper behind the text column — desktop only */
.hero__rotator::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}
@media (min-width: 60rem) {
  .hero__rotator::before {
    background: linear-gradient(to right, var(--color-paper) 0%, transparent 18%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero__rotator-img { transition: none; }
}

/* ---------- Stat strip — T4 Numbered stat strip (real figures, no invented metrics) ---------- */

.stat-strip {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-block: var(--rule-hair) solid var(--color-rule);
  padding-block: var(--space-xl);
}
.stat-strip__item {
  text-align: center;
  padding-inline: var(--space-sm);
}
.stat-strip__item + .stat-strip__item {
  border-inline-start: var(--rule-hair) solid var(--color-rule);
}
.stat-strip__num {
  font-family: var(--font-outlier);
  font-size: var(--text-2xl);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--color-accent);
}
.stat-strip__label {
  margin-top: var(--space-2xs);
  color: var(--color-muted);
}

@media (max-width: 30rem) {
  .stat-strip { grid-template-columns: 1fr; row-gap: var(--space-lg); }
  .stat-strip__item + .stat-strip__item {
    border-inline-start: none;
    border-block-start: var(--rule-hair) solid var(--color-rule);
    padding-block-start: var(--space-lg);
  }
}

/* ---------- Services — F3 Tabular spec sheet ---------- */

.spec-sheet {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}
.spec-sheet th, .spec-sheet td {
  text-align: left;
  padding: var(--space-md) var(--space-md) var(--space-md) 0;
  border-bottom: var(--rule-hair) solid var(--color-rule);
  vertical-align: top;
}
.spec-sheet thead th {
  font-family: var(--font-body);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-muted);
  font-weight: 400;
  border-bottom-color: var(--color-ink);
}
.spec-sheet td.service-name {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  white-space: nowrap;
  padding-right: var(--space-2xl);
}
.spec-sheet th:first-child {
  padding-right: var(--space-2xl);
}
.spec-sheet td.service-note { color: var(--color-muted); font-size: var(--text-sm); }

@media (max-width: 40rem) {
  .spec-sheet, .spec-sheet thead, .spec-sheet tbody, .spec-sheet tr, .spec-sheet th, .spec-sheet td {
    display: block;
  }
  .spec-sheet thead { display: none; }
  .spec-sheet tr { padding-block: var(--space-md); border-bottom: var(--rule-hair) solid var(--color-rule); }
  .spec-sheet td { border-bottom: none; padding: var(--space-3xs) 0; }
  .spec-sheet td.service-name { white-space: normal; padding-right: 0; }
}

/* ---------- Portfolio grid ---------- */

.filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-xl);
}
.filter-chip {
  font-family: var(--font-outlier);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.05;
  padding: var(--space-xs) var(--space-md);
  border: var(--rule-hair) solid var(--color-rule);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-ink-2);
  cursor: pointer;
  min-height: 44px;
  white-space: nowrap;
  transition: background-color var(--dur-short) var(--ease-out),
              color var(--dur-short) var(--ease-out),
              border-color var(--dur-short) var(--ease-out);
}
.filter-chip:hover { border-color: var(--color-ink); }
.filter-chip:active { transform: translateY(1px); }
.filter-chip[aria-pressed="true"] {
  background: var(--color-ink);
  border-color: var(--color-ink);
  color: var(--color-paper);
}

.portfolio-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-auto-flow: dense; /* backfills gaps if wide/narrow tile counts ever stop dividing evenly */
  gap: var(--space-md);
}
@media (max-width: 60rem) {
  .portfolio-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 26rem) {
  .portfolio-grid { grid-template-columns: minmax(0, 1fr); }
}

.karya-more, .tentang-more { margin-top: var(--space-xl); }

.tile {
  position: relative;
  margin: 0;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: opacity var(--dur-short) var(--ease-out),
              transform var(--dur-short) var(--ease-out),
              box-shadow var(--dur-short) var(--ease-out);
}
/* Landscape source photos — span 2 tracks at a ~3:2 ratio instead of being
   force-cropped into the portrait 4:5 frame (matches the ~1400x933 source). */
.tile--wide {
  grid-column: span 2;
  aspect-ratio: 3 / 2;
}
@media (max-width: 26rem) {
  .tile--wide { grid-column: span 1; } /* grid is single-column here — avoid an implicit extra track */
}
.tile.is-hidden {
  opacity: 0;
  transform: translateY(6px) scale(0.98);
  pointer-events: none;
}
.tile.is-collapsed { display: none; }

/* Scroll-in reveal — starting state only; Motion animates out of it (see
   js/script.js initGalleryReveal), then settleReveal() strips the inline
   styles Motion wrote and drops this class in the same pass.

   That cleanup is not optional. The hover lift below is a plain :hover rule,
   so anything still holding `transform` outranks it and the lift silently
   stops working — it used to be a CSS animation still filling forwards, and
   with Motion it would be the inline style attribute instead. */
.js-reveal .tile.tile--pending {
  opacity: 0;
  transform: translateY(20px);
}

/* Hover "lift" — desktop pointers only; purely decorative, no info hides behind it */
@media (hover: hover) and (pointer: fine) {
  .tile:hover {
    transform: translateY(-6px);
    box-shadow: 0 14px 28px color-mix(in oklch, var(--color-ink) 16%, transparent);
  }
}

@media (prefers-reduced-motion: reduce) {
  .js-reveal .tile.tile--pending { opacity: 1; transform: none; }
}

.tile__placeholder {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(150deg,
      color-mix(in oklch, var(--color-paper-2) 100%, var(--color-accent) 6%),
      color-mix(in oklch, var(--color-paper) 100%, var(--color-ink) 6%));
}
.tile__placeholder::after {
  content: "";
  position: absolute; inset: 0;
  opacity: 0.05;
  mix-blend-mode: multiply;
  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='n'><feTurbulence baseFrequency='0.85' numOctaves='2'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}
.tile__plate {
  position: absolute;
  left: var(--space-md);
  bottom: var(--space-md);
  font-family: var(--font-display);
  font-style: normal;
  font-size: var(--text-3xl);
  line-height: 1;
  color: color-mix(in oklch, var(--color-ink) 16%, transparent);
  pointer-events: none;
}
.tile__tag {
  position: absolute;
  right: var(--space-md);
  top: var(--space-md);
  font-family: var(--font-outlier);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-muted);
}
.tile img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.tile img[hidden] { display: none; }

/* ---------- Lightbox — fullscreen viewer for portfolio tiles (see js/script.js initLightbox) ---------- */

body.lightbox-open { overflow: hidden; }

.lightbox {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in oklch, var(--color-ink) 94%, transparent);
  opacity: 0;
  transition: opacity var(--dur-short) var(--ease-out);
}
.lightbox[hidden] { display: none; }
.lightbox.is-open { opacity: 1; }

.lightbox__figure {
  position: relative;
  max-width: min(90vw, 76rem);
  max-height: 88vh;
  margin: 0;
  transform: scale(0.97);
  transition: transform var(--dur-short) var(--ease-out);
}
.lightbox.is-open .lightbox__figure { transform: none; }

.lightbox__img {
  display: block;
  max-width: 100%;
  max-height: 88vh;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: var(--radius-sm);
}

.lightbox__btn {
  position: fixed;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  border: none;
  background: color-mix(in oklch, var(--color-ink) 55%, transparent);
  color: var(--color-paper);
  font-family: var(--font-body);
  font-size: var(--text-lg);
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--dur-short) var(--ease-out);
}
.lightbox__btn:hover { background: var(--color-accent); }

.lightbox__close { top: var(--space-lg); right: var(--space-lg); }
.lightbox__prev { left: var(--space-lg); top: 50%; transform: translateY(-50%); }
.lightbox__next { right: var(--space-lg); top: 50%; transform: translateY(-50%); }

@media (max-width: 40rem) {
  .lightbox__btn { width: 2.25rem; height: 2.25rem; font-size: var(--text-md); }
  .lightbox__close { top: var(--space-md); right: var(--space-md); }
  .lightbox__prev { left: var(--space-xs); }
  .lightbox__next { right: var(--space-xs); }
}

@media (prefers-reduced-motion: reduce) {
  .lightbox, .lightbox__figure { transition: none; }
}

/* ---------- Instagram strip — decorative photo band linking out; not a live embed ---------- */

.insta-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--space-sm);
  margin-block: var(--space-lg) var(--space-xl);
}
.insta-grid__tile {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: var(--radius-sm);
}
.insta-grid__tile img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
@media (max-width: 60rem) {
  .insta-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 26rem) {
  .insta-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---------- Process — F4 Step sequence ---------- */

.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-lg);
}
.head-hang + .steps { margin-top: var(--space-md); }
.steps li {
  display: grid;
  grid-template-columns: 5rem 1fr;
  gap: var(--space-lg);
  align-items: start;
}
.steps .stage {
  font-family: var(--font-outlier);
  font-size: var(--text-md);
  color: var(--color-accent);
}
.steps h3 { font-size: var(--text-xl); margin-bottom: var(--space-xs); }
.steps p { color: var(--color-ink-2); max-width: 52ch; }

@media (max-width: 40rem) {
  .steps li { grid-template-columns: 3rem 1fr; }
}

/* ---------- About ---------- */

.about p { font-size: var(--text-md); line-height: 1.6; color: var(--color-ink-2); }

.prose p { font-size: var(--text-md); line-height: 1.6; color: var(--color-ink-2); }
.prose p + p { margin-top: var(--space-md); }

/* ---------- Values / fact grid — About page values, homepage gear & editing ---------- */

.value-grid {
  display: grid;
  gap: var(--space-2xl);
  margin-top: var(--space-lg);
}
@media (min-width: 48rem) {
  .value-grid { grid-template-columns: repeat(3, 1fr); }
  .value-grid--pair { grid-template-columns: repeat(2, 1fr); }
}
.value-grid__item {
  border-top: var(--rule-hair) solid var(--color-rule);
  padding-top: var(--space-lg);
}
.value-grid__item h3 { font-size: var(--text-xl); margin-bottom: var(--space-xs); }
.value-grid__item p { color: var(--color-ink-2); max-width: 36ch; }

/* ---------- FAQ — native <details> disclosure, no JS required ---------- */

.faq-list {
  border-top: var(--rule-hair) solid var(--color-rule);
}
.faq-item {
  border-bottom: var(--rule-hair) solid var(--color-rule);
  padding-block: var(--space-lg);
}
.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  list-style: none;
  cursor: pointer;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  min-height: 44px;
  transition: color var(--dur-short) var(--ease-out);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "+";
  flex-shrink: 0;
  font-family: var(--font-body);
  font-size: var(--text-xl);
  font-weight: 400;
  line-height: 1;
  color: var(--color-accent);
  transition: transform var(--dur-short) var(--ease-out);
}
.faq-item[open] summary::after { transform: rotate(45deg); }
.faq-item summary:hover { color: var(--color-accent); }
.faq-item__answer {
  margin-top: var(--space-sm);
  max-width: 62ch;
  color: var(--color-ink-2);
}

/* ---------- Final CTA band ---------- */

.cta-band {
  text-align: left;
  border-top: var(--rule-hair) solid var(--color-rule);
  border-bottom: var(--rule-hair) solid var(--color-rule);
  padding-block: var(--space-2xl);
}
.cta-band h2 { font-size: var(--text-2xl); max-width: 16ch; }
.cta-band p { margin-top: var(--space-sm); color: var(--color-ink-2); max-width: 52ch; }
.cta-band .cta-outline { margin-top: var(--space-xl); }

/* ---------- Footer — column layout (brand + nav/contact/follow link groups) ---------- */

.foot-letter {
  padding-block: var(--space-2xl) var(--space-xl);
}

.foot-columns {
  display: grid;
  grid-template-columns: 1.3fr repeat(3, 1fr);
  gap: var(--space-2xl);
  padding-block-end: var(--space-2xl);
  border-bottom: var(--rule-hair) solid var(--color-rule);
}
@media (max-width: 48rem) {
  .foot-columns { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 30rem) {
  .foot-columns { grid-template-columns: 1fr; gap: var(--space-xl); }
}

.foot-columns__brand .foot-brand {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  text-decoration: none;
  color: var(--color-ink);
}
.foot-columns__brand p {
  margin-top: var(--space-sm);
  max-width: 32ch;
  color: var(--color-ink-2);
  font-size: var(--text-sm);
  line-height: 1.5;
}

.foot-columns__group h3 {
  font-family: var(--font-body);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-weight: 400;
  color: var(--color-muted);
  margin: 0 0 var(--space-md);
}
.foot-columns__group ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-sm);
}
.foot-columns__group a {
  font-size: var(--text-sm);
  color: var(--color-ink-2);
  text-decoration: none;
  transition: color var(--dur-short) var(--ease-out);
}
.foot-columns__group a:hover { color: var(--color-accent); }

.foot-bottom {
  padding-block-start: var(--space-lg);
  font-family: var(--font-body);
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
  color: var(--color-muted);
}
.foot-letter__meta {
  margin-top: var(--space-xl);
  font-family: var(--font-body);
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
  color: var(--color-muted);
}

/* ---------- Sticky booking bar — C4, mobile only ---------- */

.cta-sticky {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: var(--z-sticky);
  padding: var(--space-sm) var(--page-gutter);
  padding-bottom: max(var(--space-sm), env(safe-area-inset-bottom));
  background: color-mix(in oklch, var(--color-paper) 96%, transparent);
  backdrop-filter: blur(6px);
  border-top: var(--rule-hair) solid var(--color-rule);
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
}
.cta-sticky span { font-size: var(--text-sm); color: var(--color-ink-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cta-sticky .cta-outline { padding: var(--space-xs) var(--space-md); }

@media (max-width: 60rem) {
  .cta-sticky { display: flex; }
  body { padding-bottom: 4.5rem; }
}

/* ---------- Booking form (booking.html) ---------- */

.booking-intro { padding-block: var(--space-2xl) var(--space-lg); }
.booking-intro .hero__display { font-size: var(--text-display-s); max-width: 22ch; }

.booking-form {
  display: grid;
  gap: var(--space-xl);
  max-width: 40rem;
  padding-block: var(--space-lg) var(--space-3xl);
}

.field {
  display: grid;
  gap: var(--space-2xs);
}
.field > label,
.field--chips legend {
  font-size: var(--text-sm);
  color: var(--color-ink-2);
}
.field input[type="text"],
.field input[type="date"],
.field textarea {
  font-family: var(--font-body);
  font-size: var(--text-base);
  padding: var(--space-sm) var(--space-md);
  border: var(--rule-hair) solid var(--color-rule);
  border-radius: var(--radius-sm);
  background: var(--color-paper);
  color: var(--color-ink);
  min-height: 44px;
  transition: border-color var(--dur-short) var(--ease-out);
}
.field textarea { min-height: 6rem; resize: vertical; }
.field input:hover, .field textarea:hover { border-color: var(--color-ink-2); }
.field input:focus-visible, .field textarea:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 1px;
}
.field input:disabled, .field textarea:disabled { opacity: 0.55; cursor: not-allowed; }

.field__helper {
  min-height: 1.2em; /* reserved so an appearing error doesn't shift the layout */
  font-size: var(--text-xs);
  color: var(--color-error);
  margin: 0;
}
.field.has-error input,
.field.has-error textarea,
.field--chips.has-error .chip-radio-group .chip-radio span {
  border-color: var(--color-error);
}

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-lg);
}
@media (max-width: 40rem) {
  .field-row { grid-template-columns: 1fr; }
}

.field--chips { display: grid; gap: var(--space-2xs); border: none; padding: 0; margin: 0; }
.field--chips legend { padding: 0; }

.chip-radio-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}
.chip-radio { position: relative; display: inline-flex; }
.chip-radio input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.chip-radio span {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-outlier);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.05;
  padding: var(--space-xs) var(--space-md);
  border: var(--rule-hair) solid var(--color-rule);
  border-radius: var(--radius-sm);
  color: var(--color-ink-2);
  cursor: pointer;
  min-height: 44px;
  white-space: nowrap;
  transition: background-color var(--dur-short) var(--ease-out),
              color var(--dur-short) var(--ease-out),
              border-color var(--dur-short) var(--ease-out);
}
.chip-radio:hover span { border-color: var(--color-ink); }
.chip-radio input:checked + span {
  background: var(--color-ink);
  border-color: var(--color-ink);
  color: var(--color-paper);
}
.chip-radio input:focus-visible + span {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.booking-submit { margin-top: var(--space-md); justify-self: start; }

/* ---------- Client gallery lookup (galeri.html) ---------- */

.gallery-lookup {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: var(--space-lg);
  max-width: 32rem;
  padding-block: var(--space-lg) var(--space-2xl);
}
.gallery-lookup .field { flex: 1 1 12rem; }

.gallery-result {
  max-width: 32rem;
  padding: var(--space-xl);
  border: var(--rule-hair) solid var(--color-rule);
  border-radius: var(--radius-sm);
  margin-block-end: var(--space-3xl);
}
.gallery-result__name {
  font-size: var(--text-2xl);
  margin-bottom: var(--space-xs);
}
.gallery-result__meta {
  color: var(--color-ink-2);
  font-size: var(--text-sm);
  margin-bottom: var(--space-lg);
}

/* Inline photo grid — only populated when CONFIG.googleApiKey is set
   (see js/script.js renderPhotos); otherwise stays hidden and the
   .gallery-result__link above is the only way to reach the photos. */
.gallery-photos { max-width: 64rem; margin-block-end: var(--space-3xl); }
.gallery-photos__status {
  color: var(--color-ink-2);
  font-size: var(--text-sm);
  margin-bottom: var(--space-md);
}
.gallery-photos__status:empty { display: none; }

.gallery-photos__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-md);
}
@media (max-width: 60rem) {
  .gallery-photos__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 40rem) {
  .gallery-photos__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.photo-card {
  position: relative;
  margin: 0;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--color-paper-2);
}
.photo-card img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.photo-card__download {
  position: absolute;
  right: var(--space-xs);
  bottom: var(--space-xs);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background: color-mix(in oklch, var(--color-ink) 70%, transparent);
  color: var(--color-paper);
  font-family: var(--font-outlier);
  font-size: var(--text-sm);
  line-height: 1;
  text-decoration: none;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity var(--dur-short) var(--ease-out),
              transform var(--dur-short) var(--ease-out),
              background var(--dur-short) var(--ease-out);
}
.photo-card:hover .photo-card__download,
.photo-card__download:focus-visible {
  opacity: 1;
  transform: none;
}
.photo-card__download:hover,
.photo-card__download:focus-visible {
  background: var(--color-accent);
}
@media (hover: none) {
  /* Touch devices have no hover to reveal the button — keep it visible. */
  .photo-card__download { opacity: 1; transform: none; }
}

/* ---------- Admin panel (admin.html) — local testing only ---------- */

.admin-notice {
  max-width: 40rem;
  margin-block: var(--space-lg) var(--space-2xl);
  padding: var(--space-md) var(--space-lg);
  border: var(--rule-hair) solid var(--color-accent);
  border-radius: var(--radius-sm);
  background: color-mix(in oklch, var(--color-accent) 6%, var(--color-paper));
  font-size: var(--text-sm);
  color: var(--color-ink-2);
  line-height: 1.6;
}
.admin-notice code {
  font-family: var(--font-outlier);
  font-size: 0.9em;
}

.admin-gate {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: var(--space-lg);
  max-width: 32rem;
  margin-bottom: var(--space-3xl);
}
.admin-gate .field { flex: 1 1 12rem; }
/* A class-based `display` always beats the UA [hidden] rule at equal
   specificity, so it has to be restated explicitly here once toggled. */
.admin-gate[hidden] { display: none; }

.admin-panel { max-width: 32rem; }
.admin-panel__heading {
  font-size: var(--text-xl);
  margin-block: var(--space-2xl) var(--space-lg);
}
.admin-panel__heading:first-child { margin-top: 0; }

.admin-form { display: grid; gap: var(--space-lg); }
.admin-form .booking-submit,
.admin-form button[type="submit"] { margin-top: var(--space-sm); justify-self: start; }

.admin-list__empty { padding-block: var(--space-md); }

.admin-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding-block: var(--space-md);
  border-bottom: var(--rule-hair) solid var(--color-rule);
}
.admin-row__info {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2xs);
  min-width: 0;
}
.admin-row__info strong { font-family: var(--font-display); font-size: var(--text-md); }
.admin-row__delete { color: var(--color-muted); flex-shrink: 0; }
.admin-row__delete:hover { color: var(--color-accent); border-color: var(--color-accent); }

/* ---------- Reduced motion — safety net for every transition/animation ---------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 150ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 150ms !important;
  }
}
