/* ==========================================================================
   Base: reset, typography, chrome, shared primitives
   ========================================================================== */

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100svh;
  font-family: var(--font);
  font-weight: 400;
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--ink);
  background: var(--paper-mid);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow: hidden;              /* the home view is a fixed stage */
  overflow-x: clip;              /* the artwork bleeds; the page never scrolls sideways */
}

body[data-scrollable="true"] { overflow: auto; }

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

button {
  font: inherit; color: inherit; margin: 0;
  background: none; border: 0; padding: 0; cursor: pointer;
}

a { color: inherit; text-decoration: none; }

/* The ring has to hold on a pale document *and* on the contact skyline, so it
   carries its own light halo rather than relying on the backdrop for contrast. */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(246, 244, 241, .92);
  border-radius: 2px;
}
/* On the dark destination layer the halo inverts. */
.stage[data-mounted="true"] .cs :focus-visible { box-shadow: 0 0 0 5px rgba(246, 244, 241, .92); }

.u-label {
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.u-visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.skip-link {
  position: fixed; top: .5rem; left: .5rem; z-index: 999;
  padding: .625rem 1rem; background: var(--ink); color: var(--on-dark);
  transform: translateY(-160%);
  transition: transform var(--dur-s) var(--ease-out);
}
.skip-link:focus-visible { transform: none; }

/* --- backdrop ------------------------------------------------------------
   Reproduces the render's own diagonal gradient so the feathered edges of the
   hero plate have nothing to blend against but themselves. */
/* --- backdrop -----------------------------------------------------------
   The home screen's sky. A photograph rather than a gradient, lifted toward
   the paper by tools/prep-home-sky.py so it stays behind the portrait instead
   of competing with it, and so the headline column clears AA on it.

   `cover` with the position low: the cloud bank sits in the lower half of the
   source, and putting it under the portrait rather than behind the headline
   keeps the left column open. The gradient underneath is the fallback and the
   colour the image is graded toward, so there is no flash of the wrong hue. */
.backdrop {
  position: fixed; inset: 0; z-index: 0;
  background:
    image-set(url("/assets/img/home-sky-1280.webp") 1x,
              url("/assets/img/home-sky-1536.webp") 1.6x) center 78% / cover no-repeat,
    linear-gradient(107deg, var(--paper-warm) 0%, #DCD7D5 46%, #CFCFD1 100%);
}
/* Film grain. Kept extremely low: it stops the large flat gradient from
   banding on 8-bit panels, which is the only reason it is here. */
.backdrop::after {
  content: ""; position: absolute; inset: 0;
  opacity: .5; mix-blend-mode: multiply; pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'>\
<filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3'/>\
<feColorMatrix type='saturate' values='0'/></filter>\
<rect width='140' height='140' filter='url(%23n)' opacity='.22'/></svg>");
}

/* Darkening veil that closes in as the camera pushes into the mind. */
.veil {
  position: fixed; inset: 0; z-index: 6; pointer-events: none;
  opacity: var(--veil, 0);
  background:
    radial-gradient(78% 62% at 50% 44%, rgba(8,10,14,0) 0%, rgba(8,10,14,.86) 100%),
    rgba(8,10,14,.42);
}

/* --- header -------------------------------------------------------------- */
.site-header {
  position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-chrome);
  height: var(--header-h);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 var(--gutter);
  mix-blend-mode: multiply;     /* keeps the chrome sitting *in* the image */
  transition: opacity var(--dur-m) var(--ease-out);
}
.site-header[data-hidden="true"] { opacity: 0; pointer-events: none; }

.brand { display: flex; align-items: center; gap: .875rem; }
.brand__mark {
  font-family: 'Cinzel Decorative', serif;
  font-size: 1.0625rem; font-weight: 400; letter-spacing: -.02em; line-height: 1;
}
.brand__rule { width: 2.25rem; height: 1px; background: var(--rule); }
.brand__name {
  font-family: 'Cinzel', serif;
  font-weight: 400;
  font-size: 1rem;
  color: var(--ink-soft);
}

.site-nav { display: flex; align-items: center; gap: clamp(1.25rem, 2.6vw, 2.75rem); }
/* The nav inherits the label tracking from its parent, which at 0.16em makes
   three short words read as spread apart. These are controls, not eyebrows, so
   they come back most of the way in — enough to keep small uppercase legible
   without the letters drifting. */
.site-nav a {
  color: var(--ink-soft); position: relative; padding-block: .35rem;
  letter-spacing: .07em;
}
.site-nav a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--dur-s) var(--ease-out);
}
.site-nav a:hover::after,
.site-nav a[aria-current="page"]::after { transform: scaleX(1); }
.site-nav a[aria-current="page"] { color: var(--ink); }

/* Small screens: the wordmark gives way to the monogram rather than letting
   the header wrap or overflow. */
@media (max-width: 46rem) {
  :root { --header-h: 3.75rem; }
  /* The full wordmark, as on the desktop, sized to share the row with the
     links: N, a short rule, NIKOLE UGUETO. */
  .brand { gap: .5rem; flex: none; }
  .brand__rule { display: none; }
  .brand__name { font-size: .75rem; letter-spacing: .1em; white-space: nowrap; }
  .site-nav { gap: .2rem; }
  /* Finger-sized: each link is a 44px target, the words stay the same */
  .site-nav a { font-size: var(--t-label); display: inline-flex; align-items: center; min-height: 44px; padding: 0 .3rem; }
  .site-nav a::after { left: .3rem; right: .3rem; bottom: .55rem; }
  .brand { min-height: 44px; min-width: 44px; }
}
/* Larger phones have room for the rule between the N and the name. */
@media (min-width: 25.5rem) and (max-width: 46rem) {
  .brand__rule { display: block; width: 1rem; }
}
/* The narrowest phones: a touch tighter, so the row still fits. */
@media (max-width: 22.5rem) {
  body .site-header { padding-left: max(1rem, env(safe-area-inset-left)); padding-right: max(1rem, env(safe-area-inset-right)); }
  .brand { gap: .4rem; }
  .brand__name { font-size: .6875rem; letter-spacing: .08em; }
  .site-nav { gap: 0; }
  .site-nav a { padding: 0 .28rem; letter-spacing: .05em; }
  .site-nav a::after { left: .28rem; right: .28rem; }
}

/* ==========================================================================
   Below the desktop composition (under 1180px) and on any touch screen.
   Everything here is scoped by these conditions, so the desktop is untouched.
   ========================================================================== */
@media (max-width: 1179px), (hover: none) and (pointer: coarse) {
  /* Modern phones: keep the chrome clear of the notch and the home bar. */
  /* The edges of the page (overscroll, and what the browser tints its own
     bars from) are the page's own colour: Home's sky here, the current page
     once one arrives (stage.js sets --page-edge). */
  body { background-color: var(--page-edge, #BBBCC0); }

  /* The navigation stays fully transparent, scrolled or not: no panel
     behind it, just the words over the page, as on the desktop. */
  .site-header {
    padding-left: max(var(--gutter), env(safe-area-inset-left));
    padding-right: max(var(--gutter), env(safe-area-inset-right));
  }

  /* The small portrait is a brand accent, not a control: taps go through it,
     it clears the home bar, and it steps aside on inner pages, where it would
     otherwise sit on top of text and buttons. */
  .portrait-badge {
    pointer-events: none;
    bottom: calc(1.25rem + env(safe-area-inset-bottom));
    right: max(var(--gutter), env(safe-area-inset-right));
    width: 46px; height: 46px;
    transition: opacity var(--dur-m) var(--ease-out), visibility var(--dur-m);
  }
  body:has(#stage[data-mounted="true"]) .portrait-badge { opacity: 0; visibility: hidden; }
}

/* Company names in testimonial credits: full everywhere, short on phones. */
.co-short { display: none; }
@media (max-width: 46rem) {
  .co-full { display: none; }
  .co-short { display: inline; }
}

/* Below the desktop width: headlines balance their lines, so a phrase such
   as "of their own" or "dream home" stays together instead of leaving one
   word alone on the last line; body text avoids single-word last lines. The
   copy is untouched; only where the lines break changes. */
@media (max-width: 1179px) {
  h1, h2, h3, h4, blockquote,
  .ct__title, .abt-hello, .abt-close__title, .chg-film__h { text-wrap: balance; }
  p, li, dd, figcaption { text-wrap: pretty; }
}

/* While a product prototype is open full screen, the site chrome steps
   aside so the app has the whole screen (see the case-study views). */
html[data-app-full] .site-header,
html[data-app-full] .portrait-badge { visibility: hidden; }

/* Touch: small text links become finger-sized targets without moving a pixel
   (the padding is paid back by an equal negative margin). */
@media (hover: none) and (pointer: coarse) {
  .crumb__link { padding: .8rem .3rem; margin: -.8rem -.3rem; }
}

/* ==========================================================================
   .world-row — the four worlds as a readable index.
   Used by the Home hero and the Worlds scene in the stacked composition, so it
   lives here rather than in either one's stylesheet.
   ========================================================================== */
@media not all and (min-width: 1180px) and (min-aspect-ratio: 3/2) {
  .world-row {
    display: grid; grid-template-columns: 2.5rem 1fr auto;
    align-items: baseline; gap: 1rem; width: 100%;
    padding: 1.15rem 0; text-align: left;
    border-bottom: 1px solid var(--rule-soft);
  }
  .world-row__no    { color: var(--ink-muted); font-weight: 600; }
  .world-row__name  { font-size: 1.125rem; font-weight: 600; letter-spacing: -.01em; }
  .world-row__sub   { display: block; color: var(--ink-muted); margin-top: .2rem; }
  .world-row__go {
    color: var(--ink-muted); letter-spacing: 0; text-transform: none;
    font-size: .875rem; font-weight: 500;
    transition: transform var(--dur-s) var(--ease-out), color var(--dur-s) var(--ease-out);
  }
  .world-row:hover .world-row__go,
  .world-row:active .world-row__go { transform: translateX(.35rem); color: var(--ink); }
}

/* --- no scripting --------------------------------------------------------
   The router and every destination are scripted, so the nav would appear to do
   nothing. Say so plainly and give the one thing that still works: a way to
   make contact. */
.noscript {
  position: relative; z-index: var(--z-chrome);
  max-width: 34rem;
  margin: calc(var(--header-h) + 1.5rem) var(--gutter) 0;
  padding: 1rem 1.15rem;
  border-left: 2px solid var(--focus);
  background: rgba(31, 92, 107, .06);
  font-size: .875rem; line-height: 1.6; color: var(--ink-soft);
}
.noscript p { margin: 0; }
.noscript p + p { margin-top: .6rem; }
.noscript a { text-decoration: underline; text-underline-offset: .2em; }
