/* ============================================================================
   Design system — shared by the homepage and the case-study template.
   Loaded after style.css and before the page-specific sheet.
   Tokens live on :root; chrome is scoped to the pages that opt in, so the
   pages still on the old layout are untouched.
   ========================================================================== */

:root {
  /* A dark site: native scrollbars, form controls and overscroll follow. */
  color-scheme: dark;
  --serif: 'Instrument Serif', Georgia, 'Times New Roman', serif;
  /* Wide grotesque for every small, set-in-caps element: labels, captions,
     credits. Replaces the monospace, which read as generic. */
  --micro: 'Archivo', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --micro-wide: 115%;
  --ease: cubic-bezier(.22, .8, .26, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --rule: #2a2622;
  --rule-hi: #413b33;
  --ink-dim: #837c6e;
  --on-accent: #1b0f08;
  /* Translucent layers of the two neutrals, named once instead of respelt. */
  --hairline: rgba(243, 241, 236, 0.1);
  --hairline-hi: rgba(243, 241, 236, 0.2);
  --veil: rgba(14, 10, 8, 0.24);
  --veil-dark: rgba(14, 10, 8, 0.42);
  --mark: rgba(226, 87, 46, 0.16);
  --col-gap: clamp(1rem, 2vw, 2rem);
  /* Fixed type steps below body size. Labels, captions and UI text use one
     of these; nothing is set smaller than --fs-micro (11px). */
  --fs-micro: 0.6875rem;   /* footer, strip, captions, small caps links */
  --fs-label: 0.75rem;     /* section tabs, credits */
  --fs-small: 0.875rem;    /* nav, card meta, secondary UI */
  --fs-note: 0.95rem;      /* lists and supporting prose */
  --fs-body: 1rem;
  --fs-lead: 1.0625rem;
}

/* ---------- Display face: Open Sauce Sans ----------
   Shared by any page that opts a section into it (see .project's
   data-display="stack" pages and .home main). Four static cuts, because the
   pages ask for 600, 700 and 800 (400 is the nav name) and this family
   ships statics rather than a variable file — declaring each weight exactly
   means the browser never fakes one. Open Sauce Sans is SIL OFL 1.1; the
   licence ships beside the files in /assets/fonts. */
@font-face {
  font-family: 'Open Sauce Sans';
  src: url('/assets/fonts/OpenSauceSans-400.woff') format('woff');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Open Sauce Sans';
  src: url('/assets/fonts/OpenSauceSans-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Open Sauce Sans';
  src: url('/assets/fonts/OpenSauceSans-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Open Sauce Sans';
  src: url('/assets/fonts/OpenSauceSans-800.woff2') format('woff2');
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}

/* ---------- Browser surfaces ----------
   The parts we did not draw still carry the design: selection, caret, the
   focus ring and in-page jumps all ship with defaults that belong to no
   design system. This sheet only loads on the pages that opt in, so the
   older templates keep their own behaviour. */
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

::selection { background: var(--accent); color: var(--on-accent); }
:is(.home, .project) { caret-color: var(--accent); }

:is(.home, .project) :focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}
/* The plates carry their own rounded media, so the ring should follow it. */
:is(.home, .project) .hp-plate:focus-visible { outline-offset: 5px; border-radius: 10px; }

/* Anchor targets clear the sticky nav instead of hiding under it. */
:is(.home, .project) .ds-section,
:is(.home, .project) [id] { scroll-margin-top: 96px; }

/* ---------- Custom cursor ----------
   A solid accent dot that trails the pointer and swells over links and
   buttons, in place of the browser's arrow/hand. Shared by every page that
   includes both js/cursor.js and a #hpCursorDot element in its markup.
   Opt-in via body.hp-cursor-on, added by that script only on fine-pointer
   desktops — touch and no-JS visitors keep the native cursor untouched.

   Fixed DIRECTLY on body — not nested inside a transformed wrapper —
   because mix-blend-mode stops blending with the page once any transformed
   ancestor sits between it and the root; js/cursor.js positions it via its
   own translate3d for exactly this reason. Position AND hover scale are
   both driven from the same rAF loop in JS (see initCursor), so there's no
   CSS transition on transform here. */
body.hp-cursor-on,
body.hp-cursor-on * { cursor: none; }
.hp-cursor-dot {
  position: fixed;
  top: 0;
  left: 0;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--accent);
  mix-blend-mode: exclusion;
  pointer-events: none;
  z-index: 300;
  opacity: 0;
  transition: opacity 0.3s var(--ease-out);
}
body.hp-cursor-on .hp-cursor-dot { opacity: 1; }

/* ---------- Skip link ---------- */
.ds-skip {
  position: fixed;
  top: 0.75rem;
  left: 0.75rem;
  z-index: 200;
  transform: translateY(-160%);
  background: var(--accent);
  color: var(--on-accent);
  font-family: var(--micro);
  font-stretch: var(--micro-wide);
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 0.7rem 1.1rem;
  border-radius: 999px;
  /* Parked above the window AND transparent: macOS rubber-band scrolling
     pulls the page down past its top and would otherwise flash the pill.
     Opacity keeps it in the accessibility tree; visibility would not. */
  opacity: 0;
  transition: transform 0.3s var(--ease-out), opacity 0.2s var(--ease-out);
}
.ds-skip:focus-visible { transform: translateY(0); opacity: 1; }
@media (prefers-reduced-motion: reduce) { .ds-skip { transition: none; } }

/* ---------- Film grain ---------- */
.ds-grain {
  position: fixed;
  inset: 0;
  z-index: 100;
  pointer-events: none;
  opacity: 0.035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
}
@media (prefers-reduced-motion: reduce) { .ds-grain { display: none; } }

/* ---------- Micro label ---------- */
.ds-label {
  font-family: var(--micro);
  font-stretch: var(--micro-wide);
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0;
}
.ds-label--bright { color: var(--ink); }
.ds-label .ds-bracket { color: var(--accent); }

/* ---------- Accent band: a full-bleed block of colour ----------
   Must sit outside .wrap; it supplies its own padding and inner measure. */
.ds-band {
  background: var(--accent);
  color: var(--on-accent);
  padding: clamp(3rem, 7vw, 6rem) 0;
  position: relative;
}
.ds-band a { color: inherit; }
/* Band labels ([ In one line ], [ In the end ]), brackets included, in the
   dark on-accent ink: at label size, white on the orange falls to 3.3:1. */
.ds-band .ds-label,
.ds-band .ds-label .ds-bracket { color: var(--on-accent); }

/* ---------- Section rhythm ---------- */
:is(.home, .project) .ds-section {
  padding: clamp(5rem, 11vh, 9rem) 0;
  border-top: 1px solid var(--rule);
}
:is(.home, .project) .ds-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5rem;
  margin-bottom: clamp(2.5rem, 5vw, 4rem);
}

/* ---------- Navigation ---------- */
:is(.home, .project) .site-nav { border-bottom: 1px solid var(--rule); }
:is(.home, .project) .site-nav .wrap { height: 72px; }
:is(.home, .project) .brand {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  letter-spacing: -0.015em;
}
.ds-brand-role {
  font-family: var(--micro);
  font-stretch: var(--micro-wide);
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-top: 0.25rem;
}
:is(.home, .project) .nav-links { align-items: center; gap: 2rem; font-size: var(--fs-small); }
:is(.home, .project) .nav-links a { position: relative; padding-bottom: 2px; }
:is(.home, .project) .nav-links a::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform 0.45s var(--ease);
}
:is(.home, .project) .nav-links a:hover::after,
:is(.home, .project) .nav-links a[aria-current="page"]::after {
  transform: scaleX(1);
  transform-origin: left center;
}
.ds-nav-cta {
  color: var(--ink) !important;
  border: 1px solid var(--rule-hi);
  border-radius: 999px;
  padding: 0.45rem 0.95rem !important;
  font-size: var(--fs-small);
  transition: background-color 0.35s var(--ease), border-color 0.35s var(--ease);
}
.ds-nav-cta::after { display: none !important; }
.ds-nav-cta:hover { background: var(--accent); border-color: var(--accent); color: #17100c !important; }

/* Copy-to-clipboard: sits beside a mailto link so the address still works
   for people without a mail app. Hidden until js/ui.js confirms it can run. */
.ds-copy {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--micro);
  font-stretch: var(--micro-wide);
  font-weight: 500;
  font-size: var(--fs-micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  cursor: pointer;
  text-underline-offset: 4px;
  transition: color 0.35s var(--ease);
}
.ds-copy-i,
.ds-copy-ok { width: 16px; height: 16px; flex: none; }
/* After a successful copy the tick stands in for the copy icon. */
.ds-copy-ok,
.ds-copy.is-done .ds-copy-i { display: none; }
.ds-copy.is-done .ds-copy-ok { display: block; }
.ds-copy:hover [data-copy-label] { text-decoration: underline; text-decoration-thickness: 1px; }
.ds-copy:hover { color: var(--ink); }
.ds-copy.is-done { color: var(--accent); }
.ds-sr {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Reading progress — fills across the bottom of the nav. */
.ds-progress {
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 2px;
  background: var(--accent);
  transform: scaleX(var(--p, 0));
  transform-origin: left center;
  will-change: transform;
  pointer-events: none;
}

/* ---------- Footer ---------- */
:is(.home, .project) .site-footer { border-top: 1px solid var(--rule); }
:is(.home, .project) .site-footer .wrap {
  font-family: var(--micro);
  font-stretch: var(--micro-wide);
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
:is(.home, .project) .site-footer nav { display: flex; flex-wrap: wrap; gap: 0.75rem 1.5rem; }
:is(.home, .project) .site-footer a { transition: color 0.35s var(--ease); }
:is(.home, .project) .site-footer a:hover { color: var(--ink); text-decoration: none; }

/* ---------- Scroll reveal ---------- */
:is(.home, .project) [data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition:
    opacity 0.95s var(--ease-out),
    transform 0.95s var(--ease-out);
  transition-delay: calc(var(--d, 0) * 90ms);
}
:is(.home, .project) [data-reveal].is-visible { opacity: 1; transform: none; }

:is(.home, .project) [data-rule] { transform: scaleX(0); transition: transform 1.1s var(--ease-out); }
:is(.home, .project) [data-rule].is-visible { transform: scaleX(1); }

/* Figures are uncovered rather than faded: the frame wipes open while the
   photograph inside settles back from a slight overscale. */
:is(.home, .project) [data-wipe] .pj-fig-media,
:is(.home, .project) [data-wipe].pj-fig-media {
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1.05s var(--ease-out);
  transition-delay: calc(var(--d, 0) * 110ms);
}
:is(.home, .project) [data-wipe] .pj-fig-media img,
:is(.home, .project) [data-wipe].pj-fig-media img {
  transform: scale(1.09);
  transition: transform 1.4s var(--ease-out);
  transition-delay: calc(var(--d, 0) * 110ms);
}
:is(.home, .project) [data-wipe].is-visible .pj-fig-media,
:is(.home, .project) [data-wipe].pj-fig-media.is-visible { clip-path: inset(0 0 0 0); }
:is(.home, .project) [data-wipe].is-visible .pj-fig-media img,
:is(.home, .project) [data-wipe].pj-fig-media.is-visible img { transform: scale(1); }

@media (prefers-reduced-motion: reduce) {
  :is(.home, .project) [data-reveal],
  :is(.home, .project) [data-rule],
  :is(.home, .project) [data-wipe] .pj-fig-media,
  :is(.home, .project) [data-wipe] .pj-fig-media img {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    transition: none !important;
  }
  /* The progress hairline tracks scroll position 1:1 — it reports where you
     are rather than animating, so reduced motion keeps it. */
}

/* ---------- Narrow viewports ---------- */
@media (max-width: 759px) {
  :is(.home, .project) .site-nav .wrap { height: 60px; }
  .ds-brand-role { display: none; }
  /* The pill folds into a plain link rather than disappearing: contact is
     the one action the nav must never lose. */
  .ds-nav-cta,
  .ds-nav-cta:hover { border-color: transparent; background: none; color: var(--ink) !important; padding: 0 !important; }
  :is(.home, .project) .nav-links { gap: 1.35rem; }
  :is(.home, .project) .site-footer .wrap { gap: 0.9rem 1.25rem; }
  :is(.home, .project) .ds-section-head { flex-direction: column; align-items: flex-start; gap: 0.5rem; }
}
@media (max-width: 420px) {
  :is(.home, .project) .nav-links { gap: 1.1rem; font-size: var(--fs-small); }
}
/* Touch: the small text links keep their size but get a finger-sized hit
   area (about 44px tall), drawn as an invisible layer around the text. */
@media (pointer: coarse) {
  :is(.home, .project) :is(.brand, .nav-links a, .site-footer a, .pj-back) { position: relative; }
  :is(.home, .project) :is(.brand, .nav-links a, .site-footer a, .pj-back)::before {
    content: "";
    position: absolute;
    inset: -12px -6px;
  }
}

/* ---------- Brand logo ----------
   On hover an orange copy of the mark (the same SVG used as a mask) is
   revealed by a circle growing from the bottom-left corner to the top-right. */
:is(.home, .project) .brand { position: relative; flex-direction: row; align-items: center; }
.brand-logo { display: block; height: 36px; width: auto; flex: none; }
.brand::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--accent);
  -webkit-mask: url('/assets/brand/logo-lg.svg') left center / contain no-repeat;
          mask: url('/assets/brand/logo-lg.svg') left center / contain no-repeat;
  clip-path: circle(0% at 0% 100%);
  transition: clip-path 0.6s var(--ease-out);
  pointer-events: none;
}
.brand:hover::after,
.brand:focus-visible::after { clip-path: circle(150% at 0% 100%); }
@media (max-width: 759px) { .brand-logo { height: 30px; } }
@media (prefers-reduced-motion: reduce) { .brand::after { transition: none; } }
