@import url("/tokens.css");

/* Sagar Patel — base layer.
   Reset, faces, type scale, layout primitives, masthead, footer, controls.
   Everything here is shared by every route. Section-specific rules live in
   home.css / work.css / interaction.css and never redefine what is here. */

/* ============================================================ faces ==== */

/* Self-hosted, subset to latin, no network font call. Archivo carries a width
   axis (62–125) and that is the whole reason it is here: broadcast lower
   thirds are condensed, and a real width axis condenses by redrawing the
   letterforms instead of squashing them like a transform would. */
@font-face {
  font-family: "Archivo";
  src: url("/fonts/archivo-var.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Schibsted Grotesk";
  src: url("/fonts/schibsted-grotesk-var.woff2") format("woff2-variations");
  font-weight: 400 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Schibsted Grotesk";
  src: url("/fonts/schibsted-grotesk-italic-var.woff2") format("woff2-variations");
  font-weight: 400 900;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: "Fragment Mono";
  src: url("/fonts/fragment-mono-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* Metric-matched fallbacks. Without these the swap from system-ui to Archivo
   reflows every headline on a slow connection, which on a page whose hero is
   one enormous word is the most visible possible jank. */
@font-face {
  font-family: "Archivo Fallback";
  src: local("Helvetica Neue"), local("Arial"), local("Roboto");
  ascent-override: 92%;
  descent-override: 24%;
  size-adjust: 101%;
}

@font-face {
  font-family: "Schibsted Fallback";
  src: local("Helvetica Neue"), local("Arial"), local("Roboto");
  ascent-override: 96%;
  descent-override: 25%;
  size-adjust: 100%;
}

/* ============================================================= reset ==== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* The sticky masthead would otherwise cover the top of any anchored
     section arrived at from the nav. */
  scroll-padding-top: 5.5rem;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--face-text);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

h1, h2, h3, h4, p, figure, blockquote, ul, ol, dl, dd { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg, video, canvas { display: block; max-width: 100%; }
img, video { height: auto; }
button, input, textarea, select { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }

:focus-visible {
  outline: 2px solid var(--preview);
  outline-offset: 3px;
  border-radius: var(--r-1);
}

::selection {
  background: var(--tally);
  color: var(--ink);
}

.skip {
  position: absolute;
  left: var(--s-4);
  top: -4rem;
  z-index: var(--z-overlay);
  padding: var(--s-3) var(--s-5);
  background: var(--paper);
  color: var(--ink);
  border-radius: var(--r-2);
  transition: top var(--motion-fast) var(--ease-out-soft);
}
.skip:focus { top: var(--s-4); }

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

/* ============================================================== type ==== */

/* One display class, driven by custom properties, rather than a class per
   size. Every headline on the site is condensed caps at some scale; the only
   things that vary are the scale and the width axis. */
.display {
  font-family: var(--face-display);
  font-size: var(--t-display);
  font-weight: 700;
  font-stretch: 82%;
  line-height: var(--lh-head);
  letter-spacing: var(--track-head);
  text-transform: uppercase;
  text-wrap: balance;
}

.display--mega {
  font-size: var(--t-mega);
  font-weight: 800;
  font-stretch: 74%;
  line-height: var(--lh-tight);
  letter-spacing: var(--track-mega);
}

.display--head { font-size: var(--t-head); }
.display--sub  { font-size: var(--t-sub); font-weight: 600; font-stretch: 88%; }

/* The label is the site's connective tissue: every section, every field, every
   status chip is one of these. Mono, caps, tracked — a broadcast UI legend. */
.label {
  font-family: var(--face-mono);
  font-size: var(--t-micro);
  line-height: 1;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--paper-faint);
}

.label--paper { color: var(--paper-dim); }
.label--tally { color: var(--tally); }
.label--preview { color: var(--preview); }

.lede {
  font-size: var(--t-lede);
  line-height: 1.45;
  color: var(--paper-dim);
  max-width: var(--measure);
  text-wrap: pretty;
}

.prose { max-width: var(--measure); color: var(--paper-dim); text-wrap: pretty; }
.prose + .prose { margin-top: var(--s-4); }

.mono { font-family: var(--face-mono); font-size: var(--t-small); }

/* Numerals in tabular contexts — timecode, runtimes, roll numbers — must not
   jitter as they change. */
.tnum { font-variant-numeric: tabular-nums; }

/* ============================================================ layout ==== */

.page {
  width: 100%;
  max-width: var(--page);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.column {
  width: 100%;
  max-width: var(--column);
  margin-inline: auto;
}

.band { padding-block: var(--section); position: relative; }
.band--tight { padding-block: calc(var(--section) * 0.6); }
.band + .band { border-top: 1px solid var(--line-soft); }

/* Section head: a numbered label on a hairline, the way a rundown sheet
   separates blocks. Used verbatim by every band on every route. */
.band__head {
  display: flex;
  align-items: baseline;
  gap: var(--s-4);
  padding-bottom: var(--s-4);
  margin-bottom: var(--s-7);
  border-bottom: 1px solid var(--line);
}

.band__head::after {
  content: "";
  flex: 1;
  height: 1px;
  background: repeating-linear-gradient(
    to right,
    var(--line-loud) 0 2px,
    transparent 2px 6px
  );
  transform: translateY(-0.35em);
}

.stack > * + * { margin-top: var(--s-4); }
.stack--wide > * + * { margin-top: var(--s-6); }

/* ========================================================= masthead ==== */

.masthead {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: color-mix(in oklab, var(--ink) 82%, transparent);
  backdrop-filter: blur(14px) saturate(1.3);
  border-bottom: 1px solid transparent;
  transition:
    border-color var(--motion-fast) var(--ease-out-soft),
    background var(--motion-fast) var(--ease-out-soft);
}

/* Set by the interaction layer once the page has scrolled past the hero. The
   border is absent at rest so the hero reads full-bleed to the top edge. */
.masthead[data-lifted="true"] {
  border-bottom-color: var(--line);
  background: color-mix(in oklab, var(--ink) 94%, transparent);
}

.masthead__in {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-5);
  min-height: 4.25rem;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  font-family: var(--face-display);
  font-weight: 800;
  font-stretch: 72%;
  font-size: 1.0625rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* The tally lamp in the wordmark is the site's only ambient animation. It is
   a real referent — the lamp is lit because the site is live — and it is the
   thing that makes the mark feel like equipment rather than a logo. */
.tally-lamp {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: var(--r-pill);
  background: var(--tally);
  box-shadow: var(--glow-tally);
  animation: tally-breathe var(--motion-ambient) var(--ease-linear) infinite;
}

@keyframes tally-breathe {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

@media (prefers-reduced-motion: reduce) {
  .tally-lamp { animation: none; }
}

.nav {
  display: flex;
  align-items: center;
  gap: var(--s-5);
}

.nav a {
  font-family: var(--face-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--paper-dim);
  padding-block: var(--s-2);
  position: relative;
  transition: color var(--motion-instant) var(--ease-out-soft);
}

.nav a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--tally);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--motion-fast) var(--ease-out-expressive);
}

.nav a:hover,
.nav a[aria-current="page"] { color: var(--paper); }
.nav a:hover::after,
.nav a[aria-current="page"]::after { transform: scaleX(1); }

@media (max-width: 46rem) {
  .nav { gap: var(--s-4); }
  .nav a[data-optional] { display: none; }
}

/* ========================================================== controls ==== */

.btn {
  --btn-bg: transparent;
  --btn-fg: var(--paper);
  --btn-line: var(--line-loud);
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-5);
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: 1px solid var(--btn-line);
  border-radius: var(--r-2);
  font-family: var(--face-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition:
    color var(--motion-fast) var(--ease-out-soft),
    border-color var(--motion-fast) var(--ease-out-soft),
    transform var(--motion-instant) var(--ease-out-soft);
}

/* The wipe is a broadcast transition, not a generic hover fill: it enters from
   the left edge like a wipe on a switcher and the label rides above it. */
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--btn-fg);
  transform: translateX(-101%);
  transition: transform var(--motion-base) var(--ease-out-expressive);
  z-index: 0;
}

.btn > * { position: relative; z-index: 1; }

.btn:hover { border-color: var(--btn-fg); }
.btn:hover::before { transform: translateX(0); }
.btn:hover { color: var(--ink); }
.btn:active { transform: translateY(1px); }

.btn--tally {
  --btn-fg: var(--tally);
  --btn-line: color-mix(in oklab, var(--tally) 55%, transparent);
}

.btn--solid {
  --btn-bg: var(--paper);
  --btn-fg: var(--ink);
  --btn-line: var(--paper);
}
.btn--solid::before { background: var(--tally); }
.btn--solid:hover { color: var(--ink); border-color: var(--tally); }

/* ============================================================ chrome ==== */

/* Corner crop marks. Applied to any element that should read as a framed
   shot — the hero well, a work tile, the contact card. */
.framed { position: relative; }

.framed::before,
.framed::after {
  content: "";
  position: absolute;
  width: 14px;
  height: 14px;
  border: 1px solid var(--line-loud);
  pointer-events: none;
}

.framed::before {
  top: -1px; left: -1px;
  border-right: 0; border-bottom: 0;
}

.framed::after {
  bottom: -1px; right: -1px;
  border-left: 0; border-top: 0;
}

.rule {
  height: 1px;
  border: 0;
  background: var(--line);
  margin: 0;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-1) var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font-family: var(--face-mono);
  font-size: var(--t-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper-dim);
}

.chip--live {
  border-color: color-mix(in oklab, var(--tally) 45%, transparent);
  color: var(--tally);
}

.chip--ready {
  border-color: color-mix(in oklab, var(--preview) 40%, transparent);
  color: var(--preview);
}

.chip__dot {
  width: 6px; height: 6px;
  border-radius: var(--r-pill);
  background: currentColor;
}

/* Film grain. One fixed overlay for the whole document rather than a texture
   per surface — it is what stops large flat dark areas from banding on cheap
   panels, and it is the single cheapest thing that makes a dark page read as
   photographed rather than rendered. */
.grain {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  pointer-events: none;
  opacity: 0.035;
  mix-blend-mode: screen;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ============================================================ footer ==== */

.colophon {
  border-top: 1px solid var(--line);
  padding-block: var(--s-8) var(--s-6);
}

/* The timecode rail is fixed to the bottom edge above 64rem, so the last
   thing on the page needs clearance or the rail sits on top of the colophon's
   final line. Matches the rail's own breakpoint exactly. */
@media (min-width: 64.0625rem) {
  .colophon { padding-bottom: var(--s-8); }
}

.colophon__grid {
  display: grid;
  gap: var(--s-7);
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  align-items: start;
}

.colophon__mark {
  font-family: var(--face-display);
  font-weight: 800;
  font-stretch: 68%;
  font-size: clamp(2.5rem, 1rem + 6vw, 5rem);
  line-height: 0.9;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: var(--paper);
  grid-column: 1 / -1;
}

.colophon__list > li + li { margin-top: var(--s-2); }

.colophon__list a {
  color: var(--paper-dim);
  transition: color var(--motion-instant) var(--ease-out-soft);
}
.colophon__list a:hover { color: var(--tally); }

.colophon__base {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
  justify-content: space-between;
  margin-top: var(--s-8);
  padding-top: var(--s-4);
  border-top: 1px solid var(--line-soft);
}

/* ====================================================== reveal state ==== */

/* The interaction layer adds [data-reveal] to elements and flips
   [data-revealed] when they enter the viewport. Defined here so the resting
   (hidden) state ships with the base stylesheet and there is never a frame
   where content is visible before the observer attaches. */
[data-reveal] {
  opacity: 0;
  transform: translateY(1.25rem);
  transition:
    opacity var(--motion-base) var(--ease-out-expressive),
    transform var(--motion-base) var(--ease-out-expressive);
  transition-delay: var(--reveal-delay, 0ms);
}

[data-reveal][data-revealed] {
  opacity: 1;
  transform: none;
}

/* Two safety nets, because a hidden-by-default pattern fails closed and an
   invisible site is worse than an unanimated one: no-JS, and reduced motion. */
.no-js [data-reveal] { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1; transform: none; transition: none; }
}
