/*
  base.css — page-wide rules that belong to no single component.
  Loaded after tokens.css and before the component files.
  Rule kept from component.json: page rules reach semantic tokens only through
  a component token. The only values defined here are derived from component
  tokens (the nav clearance) or are plain CSS facts (resets).
*/

/* ---------- Fonts (self-hosted copies of the Google Fonts variable files) ----------
   Atkinson Hyperlegible Next: variable weight 200-800 (the site uses 400, 600, 700).
   Fraunces: variable weight AND optical size 9-144. Optical size is left on
   automatic, so the H1 always gets the headline shapes (FRAUNCES LOADING RULE).
   Latin only: every character on the site is in it. Both fonts: SIL Open Font License. */
@font-face {
  font-family: "Atkinson Hyperlegible Next";
  src: url("../fonts/atkinson-hyperlegible-next-latin-wght-normal.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Fraunces";
  src: url("../fonts/fraunces-latin-opsz-wght-normal.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ---------- Page ---------- */
:root {
  color-scheme: light; /* dark mode explicitly opted out */

  /* NAV CLEARANCE (not a token; component.json HERO NAV CLEARANCE note).
     The nav is position: fixed and takes no space, so whatever sits under it
     needs this much room: top gap + nav padding top and bottom + control height
     = 16 + 8 + 8 + 48 = 80px. Built from the nav's own tokens, so it can never drift. */
  --nav-clearance: calc(
    var(--nav-container-offset-top-all) +
    var(--nav-container-padding-all) * 2 +
    var(--nav-item-height-all)
  );

  /* Links to a spot on the page, and keyboard focus, never land hidden under the nav. */
  scroll-padding-top: var(--nav-clearance);

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

*,
*::before,
*::after {
  box-sizing: border-box; /* widths include padding and border, so sizes add up the way they do in Figma */
}

body {
  margin: 0;
  /* macOS browsers thicken text slightly by default; Figma doesn't. This matches Figma's
     rendering so weight 400 looks like 400 (Vivek, 2026-09-26: body text looked like 500). */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Shows only where no section covers the page (e.g. when scrolling past the ends). */
  background-color: var(--section-fill-default);
}

/* Browsers add their own spacing to these; every gap on the site comes from tokens instead. */
h1, h2, h3, h4, h5, h6, p, figure, blockquote, ul, ol, dl, dd {
  margin: 0;
}

/* Form controls don't pick up the page's font by default. */
button, input, select, textarea {
  font: inherit;
  color: inherit;
}

img, picture, video {
  display: block;
  max-width: 100%;
  height: auto;
}

svg {
  flex-shrink: 0; /* icons never get squashed inside a row */
}

/* Text that screen readers announce but nobody sees (e.g. the nav's full name
   "Vivek Saigal" when the pill shows "Vivek"). */
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- Rim-light edge (the glass border) ----------
   Used by the button, nav container, nav item and dropdown panel. Not a flat CSS
   border: a thin gradient ring (bright top and bottom, clear in the middle),
   drawn on a layer over the element and masked so only a ring of --rim-width shows.
   Each component sets two values from its own tokens:
     --rim-image  the gradient (its border token), or none
     --rim-width  the ring's thickness (its borderWidth token) */
.rim {
  position: relative;
}
.rim::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: var(--rim-width, 0);
  background: var(--rim-image, none);
  /* keep the outer box, cut out the inner (content) box: what's left is the ring */
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  pointer-events: none;
  /* MOTION (BUILD 19): a gradient can't be blended into another, so a rim that comes and
     goes (the nav item's) fades this layer's opacity instead. Optional, set per component:
     --rim-opacity (1 = shown), --rim-duration and --rim-easing (its transition token). */
  opacity: var(--rim-opacity, 1);
  transition: opacity var(--rim-duration, 0s) var(--rim-easing, linear);
}

/* ---------- Motion: page-wide rules (claude/build-decisions.txt BUILD 19) ----------
   Taps on phones: iPhone Safari paints a grey box over any tapped link or button. Turned off;
   each component shows its own tap look instead (its hover look while the finger is down). */
a, button {
  -webkit-tap-highlight-color: transparent;
}

/* PAGE CROSSFADE (page.transition.all): moving to another page on this site, the old page
   fades into the new one. The whole page crossfades, nav included: identical pixels blend
   into themselves, so a nav in the same place looks perfectly still (DECISION 6).
   Browsers without this feature change pages as before. Off for visitors who have asked
   their system to reduce motion (DECISION 4): a plain page change. Needs a real web address
   (e.g. python3 -m http.server), not a file opened straight from disk. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }
  ::view-transition-group(root),
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: var(--page-transition-all-duration);
    animation-timing-function: var(--page-transition-all-easing);
  }
}
