/* ==========================================================================
   SITE SYSTEMS (V3) — global vertical grid + theme/grid utility controls
   Loaded by every page that carries the V3 chrome. Reads tokens.css only.
   The inline <head> bootstrap sets html[data-theme] and html[data-grid]
   before first paint; /js/system.js owns the controls and persistence.
   ========================================================================== */

/* ---- Global vertical grid ------------------------------------------------
   The visible grid is the real layout grid, not a decorative repeat: a
   .container (same max-width and --page-margin padding as every content
   container) holding --grid-columns columns separated by --grid-gutter, so
   each line sits on a real column edge (1200px: x=120, pairs at every 32px
   gutter, x=1320 — Figma V3). position:fixed makes it span every page at any
   scroll position without repainting on scroll; the fixed containing block is
   the viewport minus the reserved scrollbar gutter, exactly the width the
   page's own containers resolve against, so lines and content never drift.
   pointer-events:none + aria-hidden: it can't catch clicks, selection or
   assistive tech, and it never touches the layout of anything else. */
.site-grid {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.site-grid__cols {
  height: 100%;
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), minmax(0, 1fr));
  column-gap: var(--grid-gutter);
}
.site-grid__cols > span {
  border-inline: var(--grid-line-width) solid var(--color-grid-line);
}
/* The mobile grid has 4 columns; the extra 8 spans are simply not drawn. */
@media (max-width: 839px) {
  .site-grid__cols > span:nth-child(n + 5) { display: none; }
}
html[data-grid="off"] .site-grid { display: none; }

/* Everything the page renders paints above the grid. Only stacking changes
   here — no size, position offset or layout mode. The sticky nav keeps its
   own z-index (100) from components.css. */
main,
.site-footer { position: relative; z-index: 1; }

/* ---- Logo ----------------------------------------------------------------------
   The nav logo is inline SVG with fill="currentColor", so it follows the
   blue-text role: #0946B8 in light, #BEDEFF in dark. No filters. */
.site-nav__logo { color: var(--color-blue-text); }

/* ---- Theme + grid controls -----------------------------------------------
   Two small utility buttons, not navigation (they stay outside <nav> in the
   markup, right after the header, so they follow WORK and ABOUT in the tab
   order).
   Desktop and tablet (840px+): they sit at the right end of the sticky nav
   bar, after the links:   WORK   ABOUT        [theme] [grid]
   A wider gap than the one between the links separates display preferences
   from navigation: no divider, no container. They are position:fixed on the
   nav's own band (the nav is sticky at top:0, so this is the same place at
   every scroll position), right-aligned to the content column using the
   same tokens .container uses, and the menu reserves their width so nothing
   overlaps. Bottom-left was retired on desktop because the full-bleed More
   Things strip runs under that corner.
   Phones (below 840px): unchanged, a floating bottom-left stack (see the
   max-width: 839px block below). */
:root {
  --control-size: 36px;
  --controls-gap: var(--space-sm);      /* between the two buttons */
  --controls-nav-gap: 48px;             /* ABOUT -> first control (links are 32px apart) */
  /* The content column's inset from the viewport edge, either side: the
     .container's centring offset plus its --page-margin padding. */
  --content-inset: calc((100% - min(100%, var(--container-max) + 2 * var(--space-gutter))) / 2 + var(--page-margin));
}
.site-controls {
  position: fixed;
  z-index: 101; /* above the sticky nav (100) */
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
@media (min-width: 840px) {
  .site-controls {
    top: calc((var(--nav-height) - var(--control-size)) / 2);
    /* The OUTER EDGE of the last circle sits on the content/grid boundary,
       so neither control (nor its "on" ring) hangs outside the layout. */
    right: var(--content-inset);
    flex-direction: row;
    gap: var(--controls-gap);
  }
  /* The nav menu makes room for the two controls and the wider gap. */
  .site-nav__menu {
    margin-right: calc(2 * var(--control-size) + var(--controls-gap) + var(--controls-nav-gap));
  }
  /* Minimal in the nav: no resting surface, just the icon. Hover still
     shows the surface, and the "on" ring still marks an active setting. */
  .site-controls .site-control:not(:hover) {
    background: transparent;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}
/* Three distinct states, never one treatment for two meanings:
     inactive (feature off): frosted surface, quiet icon (45%)
     active (feature on):    same surface + a 1px ring, full-strength icon
     hover:                  a darker (dark theme: brighter) surface
   Focus keeps the site's 2px blue-text ring, stronger than any of these.
   "Active" = dark mode on / grid on, read from <html>, so it can't drift. */
.site-control {
  position: relative;
  width: var(--control-size);
  height: var(--control-size);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  background: var(--color-control-surface);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  color: var(--color-control-fg);
  --control-ring: 0 0 0 0 transparent;
  --control-float: 0 0 0 0 transparent;
  box-shadow: var(--control-ring), var(--control-float);
  transition: background-color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}
.site-control:hover { background: var(--color-control-surface-hover); }
/* base.css gives focus rings a 2px radius; keep this one round. */
.site-control:focus-visible { border-radius: var(--radius-pill); }
/* The visible circle is 36px; the hit area is at least 44x44. */
.site-control::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}
.site-control .site-control__icon {
  opacity: 0.45;
  transition: opacity var(--duration-base) var(--ease-standard);
}
:root[data-theme="dark"] .site-control[data-theme-toggle],
:root:not([data-grid="off"]) .site-control[data-grid-toggle] {
  --control-ring: inset 0 0 0 1px var(--color-control-ring);
}
:root[data-theme="dark"] .site-control[data-theme-toggle] .site-control__icon,
:root:not([data-grid="off"]) .site-control[data-grid-toggle] .site-control__icon { opacity: 1; }
/* Figma glyphs as masks, painted in currentColor so they follow the theme. */
.site-control__icon {
  display: block;
  background: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
  mask: var(--icon) center / contain no-repeat;
}
.site-control__icon--theme { width: 20px; height: 20px; --icon: url("/images/icons/sun.svg"); }
.site-control__icon--grid  { width: 14px; height: 19px; --icon: url("/images/icons/grid.svg"); }

@media (max-width: 839px) {
  .site-controls {
    left: max(var(--space-lg), calc(env(safe-area-inset-left) + var(--space-sm)));
    bottom: max(var(--space-lg), calc(env(safe-area-inset-bottom) + var(--space-sm)));
    gap: var(--space-sm);
  }
  .site-control {
    width: 44px;
    height: 44px;
    /* Quiet, but separable from whatever scrolls underneath. */
    --control-float: 0 0 0 1px var(--color-rule), 0 2px 8px rgba(0, 0, 0, 0.08);
  }
  /* Footer end spacing. The floating stack only covers a 44px column at the
     left edge. The legal row's content is centred and never sits under it,
     and with this padding the blue bar's links stay clear above the stack
     (11px at 340-390px) when the page is scrolled to the end. The old rule
     reserved the stack's full height (120px) here, which left a large blank
     area under the copyright. */
  .site-footer__legal {
    padding-bottom: calc(var(--space-2xl) + env(safe-area-inset-bottom));
  }
}

/* ---- Cursor: one inverting circle, sitewide (system.js) -------------------
   A 12px white circle with mix-blend-mode: difference, so it shows as the
   inverse of whatever is under it (black on white, white on black, the
   complement over colour and photos). It grows to 36px over anything
   interactive; only its size changes. Fine pointers with blend support only:
   system.js adds html.has-cursor-dot, otherwise the native cursor stays.
   Text fields keep the native text cursor (system.js hides the circle). */
.cursor-dot {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 2147483647;
  width: 36px;
  height: 36px;
  margin: -18px 0 0 -18px;
  border-radius: 50%;
  background: #FFFFFF;
  mix-blend-mode: difference;
  opacity: 0;
  pointer-events: none;
  scale: 0.3333;   /* 12px at rest; scaling keeps it crisp and off the layout */
  transition: opacity var(--duration-base) var(--ease-standard),
              scale 180ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.cursor-dot.is-visible { opacity: 1; }
.cursor-dot.is-hover { scale: 1; }
html:not(.motion-ok) .cursor-dot { transition: opacity var(--duration-base) var(--ease-standard); }   /* reduced motion: the size changes without animating */
html.has-cursor-dot,
html.has-cursor-dot body,
html.has-cursor-dot body * { cursor: none; }
html.has-cursor-dot :is(input:not([type="button"]):not([type="submit"]):not([type="checkbox"]):not([type="radio"]), textarea, [contenteditable="true"]) { cursor: text; }


/* ---- Topic tag (V3 taxonomy chip) ----------------------------------------
   PRODUCTS / BRANDS / AI EXPERIENCES on the homepage intro and the category
   chips on More Things cards. Informational only for now (no filtering):
   plain inline content, default cursor, no hover state. data-topic on each
   chip is the future filter key. Distinct from the case-study .tag pill. */
.topic-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-md);
  border-radius: 4px;
  background: var(--color-light-blue-50);
  color: var(--color-blue-gray);
  font-family: var(--font-detail);
  font-size: var(--text-details200-size);
  font-weight: var(--weight-regular);
  line-height: 1.35;
  text-transform: uppercase;
  white-space: nowrap;
  vertical-align: middle;
}
/* Secondary level (case-study skills, methods, tools): the same chip in a
   quiet gray, no icon, so it never competes with the blue category chip. */
.topic-tag--muted {
  background: var(--color-bg-gray);
  color: var(--color-gray);
}
.topic-tag__icon {
  flex: none;
  width: 9px;
  height: 9px;
  background: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
  mask: var(--icon) center / contain no-repeat;
}
.topic-tag__icon[data-icon="globe"]  { --icon: url("/images/icons/globe.svg"); }
.topic-tag__icon[data-icon="heart"]  { --icon: url("/images/icons/heart.svg"); }
.topic-tag__icon[data-icon="cpu"]    { --icon: url("/images/icons/cpu.svg"); }
.topic-tag__icon[data-icon="mouse"]  { --icon: url("/images/icons/mouse.svg"); }
.topic-tag__icon[data-icon="layout"] { --icon: url("/images/icons/layout.svg"); }
.topic-tag__icon[data-icon="zap"]    { --icon: url("/images/icons/zap.svg"); }
.topic-tag__icon[data-icon="pen"]    { --icon: url("/images/icons/pen.svg"); }

/* ---- Numbered section header ------------------------------------------------
   "01 FEATURED THINGS" over "Case Studies": a content-width hairline, a
   Nitti label with a blue number, then an H200 title. Shared, so any page
   can open a section the same way. 16px under the rule, 48px to the
   section's content (Figma). */
.section-head {
  border-top: var(--border-width) solid var(--color-rule);
  padding-top: var(--space-lg);
  margin-bottom: 48px;
}
.section-head__label {
  display: flex;
  gap: 10px;
  font-family: var(--font-detail);
  font-size: var(--text-details100-size);
  line-height: var(--text-details100-leading);
  text-transform: uppercase;
  color: var(--color-blue-gray);
}
.section-head__num { color: var(--color-blue-text); }
.section-head__title {
  font-family: var(--text-h200-font);
  font-size: var(--text-h200-size);
  font-weight: var(--text-h200-weight);
  line-height: var(--text-h200-leading);
  color: var(--color-ink);
}
@media (max-width: 839px) {
  .section-head { margin-bottom: var(--space-2xl); }
  .section-head__title { font-size: 24px; }
}

/* ---- Footer signature + copyright ------------------------------------------
   The footer's last row: "Built with Claude" and "Fueled by caffeine" on the
   left, the copyright on the right. The whole caffeine item is the hover
   target; only the mug moves (transform only, off under reduced motion). */
.site-footer__legal-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs) var(--space-xl);
  color: var(--color-slate);
}
.site-sign { display: flex; flex-wrap: wrap; gap: var(--space-xs) var(--space-lg); }
.site-sign__glyph { color: var(--color-blue-text); }
.wiggle { display: inline-block; }
.site-sign__item:hover .wiggle { animation: wiggle 1s ease-in-out infinite; }
@keyframes wiggle {
  0%, 80%, 100% { transform: translate(0, 0) rotate(0deg); }
  85%  { transform: translate(-1px, 0) rotate(-4deg); }
  90%  { transform: translate(1px, -1px) rotate(4deg); }
  95%  { transform: translate(0, 0) rotate(-1deg); }
}
@media (prefers-reduced-motion: reduce) {
  .site-sign__item:hover .wiggle { animation: none; }
}
@media (max-width: 839px) {
  .site-footer__legal-inner { flex-direction: column; justify-content: center; }
  .site-sign { justify-content: center; }
}

