/* ==========================================================================
   Lava New Media, 2026 design: shared stylesheet

   Tokens, fonts, base, the masthead and phone menu, buttons, links, and the
   footer. Page sections live in the page's own sheet (home.css). Values are
   read straight off the approved boards in design/home-spec; where the boards
   are silent, SPEC.md section 6 fills in.
   ========================================================================== */

/* --------------------------------------------------------------- tokens -- */
:root {
  --ink:            #1A1A1A;   /* body text on light */
  --near-black:     #0B0B0C;   /* dark grounds: masthead, hero, footer, and the page itself */
  --charcoal:       #141416;   /* the dark one step above the near-black ground: the phone menu panel, the Work index scenes */
  --red:            #E10005;   /* logo red: final word, big quote mark, buttons, the nav underline (red bands: --lava-red-band) */
  --red-hover:      #C40000;   /* button hover, link colour on white */
  --gold:           #D9A544;   /* links and numerals on dark, rules on white */
  --gray-1:         #4A4A4A;   /* secondary text on light */
  --gray-2:         #6B6B6B;   /* captions, eyebrows on light */
  --gray-3:         #98989D;   /* secondary text on dark */
  --gray-4:         #A9A9AD;   /* hero sub on dark */
  --gray-5:         #C9C9CE;   /* footer nav on dark */
  --hairline-light: #E6E6E6;
  --hairline-dark:  #242426;
  --edge-dark:      rgba(255, 255, 255, 0.10);   /* object edges on dark */
  --edge-light:     #E3E3E3;                     /* object edge on white */
  --white:          #FFFFFF;

  /* Every red band on the site (the home page's red act, a case study's
     "What changed"): dark at the top, logo red at the bottom. Supersedes the
     flat --red band (case study template, 2026-09-30). */
  --lava-red-band: linear-gradient(180deg, #B50004 0%, #E10005 100%);

  --display: "Raleway", sans-serif;
  --body:    "Inter", sans-serif;

  /* Content margin: 84 >= 1024, 48 at 768, 24 on the phone. The page runs
     edge to edge (viewport-fit=cover), so a phone held sideways adds its
     side inset to the margin and nothing sits under the island. */
  --safe-x: env(safe-area-inset-left, 0px);
  --margin: calc(84px + var(--safe-x));
  --content: 1272px;       /* content column, centred above 1440 */
  --r-object: 20px;        /* screenshots */
  --r-button: 12px;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);   /* ease-out */

  /* Focus ring colour: gold on dark, red on white, white on red. Sections
     set it for their ground. */
  --focus: var(--gold);

  /* Hover underlines. On the dark grounds (header, footer, menu, close) every
     underline is red, whatever the link's colour, so there is one underline
     colour on dark; .is-light and .is-red switch it to the link's own
     colour. The thickness is a whole number of device pixels: lava.js sets
     --underline-w to round(devicePixelRatio) device pixels expressed in CSS
     px (0.8px at 1.25, 1.333px at 1.5, 1.2px at 2.5), so the line never
     lands on a fractional row, which an OLED renders as a pale, off-colour
     edge. 1px until the script runs. */
  --underline:   var(--red);
  --underline-w: 1px;
}
@media (max-width: 1023px) { :root { --margin: calc(48px + var(--safe-x)); } }
@media (max-width: 767px)  { :root { --margin: calc(24px + var(--safe-x)); --r-object: 16px; } }

/* ---------------------------------------------------------------- fonts -- */
/* Six files, self-hosted. Nothing else is loaded; a weight not listed here
   is not used anywhere. */
@font-face { font-family: "Raleway"; font-weight: 500; font-style: normal; font-display: swap; src: url(/assets/fonts/raleway-latin-500-normal.woff2) format("woff2"); }
@font-face { font-family: "Raleway"; font-weight: 600; font-style: normal; font-display: swap; src: url(/assets/fonts/raleway-latin-600-normal.woff2) format("woff2"); }
@font-face { font-family: "Raleway"; font-weight: 700; font-style: normal; font-display: swap; src: url(/assets/fonts/raleway-latin-700-normal.woff2) format("woff2"); }
@font-face { font-family: "Inter";   font-weight: 400; font-style: normal; font-display: swap; src: url(/assets/fonts/inter-latin-400-normal.woff2) format("woff2"); }
@font-face { font-family: "Inter";   font-weight: 500; font-style: normal; font-display: swap; src: url(/assets/fonts/inter-latin-500-normal.woff2) format("woff2"); }
@font-face { font-family: "Inter";   font-weight: 600; font-style: normal; font-display: swap; src: url(/assets/fonts/inter-latin-600-normal.woff2) format("woff2"); }

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

/* The page itself is near-black: the root and the body both paint it, so the
   strip a phone shows when the page is pulled past its top or bottom is
   black, and Safari's own bars follow the theme-color head.php sets to the
   same value. The white sections paint their own ground (.is-light) and the
   red act paints its own. color-scheme: dark keeps the browser's scrollbars
   and form controls on the dark side of the page. */
html {
  -webkit-text-size-adjust: 100%;
  color-scheme: dark;
  background: var(--near-black);
}

/* Nothing scrolls sideways. clip, not hidden: no scroll container, and it is
   a safety net only; every bleed is clipped at its own section. */
html, body { overflow-x: clip; }

body {
  margin: 0;
  background: var(--near-black);
  color: var(--ink);
  font: 400 16px/1.5 var(--body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
body.menu-open { overflow: hidden; }

h1, h2, h3, p, ul, figure, blockquote { margin: 0; }
ul { padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; }

/* Widows, sitewide. Headings balance (h1-h3, and .balance on a Raleway
   heading set in another element); running text, list items and captions
   avoid a short last line. The last two words of every heading, caption line
   and act line are wrapped in .nw so no line can end on one word. A break is
   never fixed with a hard return in the copy. */
h1, h2, h3, .balance { text-wrap: balance; }
p, li, figcaption, .caption { text-wrap: pretty; }
.nw { white-space: nowrap; }
.line { display: block; }   /* one sentence per line, each free to wrap inside itself */
.phone-line { display: inline; }   /* a sentence that takes its own line on the phone only */
@media (max-width: 430px) { .phone-line { display: block; } }

/* Links rest with no underline, ever. Hover and focus draw one in (below). */
a { color: inherit; text-decoration: none; }

:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
/* The white ground: a section on it paints its own white over the page's
   near-black. The red act paints its own red. */
.is-light { --focus: var(--red);   --underline: currentColor; background: var(--white); }
.is-red   { --focus: var(--white); --underline: currentColor; }

.skip-link {
  position: absolute; top: 0; left: -200%; z-index: 100;
  padding: 12px 24px; border-radius: 0 0 var(--r-button) 0;
  background: var(--red); color: var(--white); font-weight: 600;
}
.skip-link:focus { left: 0; }

/* ----------------------------------------------------------- primitives -- */
/* The content column: 1272 wide, centred, with the margin outside it. */
.shell {
  width: 100%;
  max-width: calc(var(--content) + 2 * var(--margin));
  margin-inline: auto;
  padding-inline: var(--margin);
}

/* Text links. One hover for every text link on the site: an underline draws
   in from the left over 200ms and retreats the same way. Nothing else
   changes on hover, no colour shift, no weight. Its colour is --underline
   (red on the dark grounds, the link's own colour on white and on red) and
   its thickness --underline-w (whole device pixels), both set in :root.

   .link is a standalone text link: gold on dark; --red and --white set the
   colour for the other grounds. Its words sit in .link__text, which carries
   the underline, and it ends in .link__arrow (helpers.php arrowhead()): a
   space and the small solid triangle U+25B8 in the link's own colour and
   weight. It steps 3px right on hover and is never underlined. Buttons and
   nav items carry no arrowhead. The underline is a background drawn per
   line box, so a link that wraps is underlined on every line. */
.link { color: var(--gold); font-weight: 600; }
.link--red   { color: var(--red-hover); }
.link--white { color: var(--white); }

/* Every underline is drawn on a .link__text span, an inline box holding the
   link's words and nothing else, so its content box is exactly the font's
   ascent plus descent and its bottom edge is the font's descender line. The
   line's top edge sits 0.18em below that edge (SPEC.md section 7): the same
   proportional gap on every link, clear of every g, p, q, y and j at any
   size, never on the baseline. The padding below the content box is the
   gap, the line, and 1px of room for lava.js to nudge the line one CSS
   pixel (--snap: -1px, 0 or 1px) onto a device pixel. */
.link__text {
  padding-bottom: calc(0.18em + var(--underline-w) + 1px);
  background-image: linear-gradient(var(--underline), var(--underline));
  background-origin: content-box;
  background-size: 0 var(--underline-w); background-repeat: no-repeat;
  background-position: 0 calc(100% + 0.18em + var(--underline-w) + var(--snap, 0px));
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
  transition: background-size 200ms var(--ease);
}
a:hover .link__text, a:focus-visible .link__text { background-size: 100% var(--underline-w); }

.link__arrow { position: relative; left: 0; white-space: nowrap; transition: left 200ms var(--ease); }
.link:hover .link__arrow, .link:focus-visible .link__arrow { left: 3px; }

/* Buttons: one style, red. Full width on the phone. */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 16px 30px;
  border: 0; border-radius: var(--r-button);
  background: var(--red); color: var(--white);
  font: 600 16px/1.2 var(--body);
  transition: background-color 160ms var(--ease), transform 160ms var(--ease);
}
.btn:hover, .btn:focus-visible { background: var(--red-hover); transform: translateY(-1px); }
@media (max-width: 767px) { .btn { width: 100%; padding: 16px 24px; } }

/* A phone object (includes/device.php), the sitewide device rule: the whole
   screen, uncropped, at its own height, inside a thin ink bezel. The
   bezel's colour fills the corners, which is what a phone's corners are;
   nothing inside the screen is ever cropped to make a corner work. 6px
   bezel and 30px outer radius; 4px and 22px on the phone. */
.device {
  display: block; box-sizing: border-box;
  padding: 6px; border-radius: 30px;
  background: var(--near-black); border: 1px solid rgba(255, 255, 255, 0.18);
}
.device__screen { display: block; border-radius: 24px; overflow: hidden; background: var(--near-black); }
.device img { display: block; width: 100%; height: auto; }
@media (max-width: 767px) {
  .device { padding: 4px; border-radius: 22px; }
  .device__screen { border-radius: 18px; }
}

/* Eyebrow: gold rule, then small caps-spaced label. */
.eyebrow { display: flex; flex-direction: column; gap: 18px; }
.eyebrow::before { content: ""; width: 56px; height: 2px; background: var(--gold); }
.eyebrow__label {
  font: 600 12px/1 var(--body); letter-spacing: 3px; text-transform: uppercase;
  color: var(--gray-2);
}
@media (max-width: 767px) {
  .eyebrow { gap: 14px; }
  .eyebrow::before { width: 48px; }
  .eyebrow__label { font-size: 11px; }
}

/* ------------------------------------------------------------- masthead -- */
/* Fixed to the top of the viewport on every page. At the very top it looks
   as it always has: no ground of its own over the page's near-black (the
   home page paints it near-black, home.css). The body is padded by its
   height (--header-h: 76, 80 from 768 to 1023, 62 on the phone, plus the
   top inset), so every page's content starts exactly where it did when the
   header sat in the flow. lava.js does the rest, from one scroll listener:
   .is-scrolled once the page is off its top (solid near-black, a soft drop
   shadow so it parts from what runs under it), .is-hidden while scrolling
   down (slid up out of view, 260ms), cleared by any scroll up. Never hidden
   at the top, while the phone menu is open, or while focus is inside it.
   With reduced motion it shows and hides without the slide. The top padding
   grows by the phone's top inset when the page runs under the status bar
   (viewport-fit=cover), so the logo never sits under the island. */
:root { --header-h: calc(36px + 40px + env(safe-area-inset-top, 0px)); }
@media (max-width: 1023px) { :root { --header-h: var(--masthead-h); } }
body { padding-top: var(--header-h); }
html { scroll-padding-top: var(--header-h); }
.masthead {
  position: fixed; top: 0; left: 0; right: 0; z-index: 40; color: var(--white);
  transition: transform 260ms ease, background-color 260ms ease, box-shadow 260ms ease;
}
.masthead.is-scrolled { background: var(--near-black); box-shadow: 0 2px 14px rgba(0, 0, 0, 0.35); }
.masthead.is-hidden { transform: translateY(-100%); }
/* On the solid bar the row is centred in the same height (the top padding
   split above and below it), so the logo does not sit on the bar's edge. */
.masthead__inner { transition: padding 260ms ease; }
.masthead.is-scrolled .masthead__inner { padding-top: calc(18px + env(safe-area-inset-top, 0px)); padding-bottom: 18px; }
@media (max-width: 767px) {
  .masthead.is-scrolled .masthead__inner { padding-top: calc(9px + env(safe-area-inset-top, 0px)); padding-bottom: 9px; }
}
@media (prefers-reduced-motion: reduce) { .masthead, .masthead__inner { transition: none; } }
.masthead__inner {
  display: flex; align-items: center; justify-content: space-between;
  padding-top: calc(36px + env(safe-area-inset-top, 0px));
}
.masthead__logo { display: block; }
.masthead__logo img { height: 40px; width: auto; }

.nav__list { display: flex; gap: 44px; font: 500 15px/1 var(--body); }
/* The nav is the reference for the underline rule above: its words sit in a
   .link__text like every other link's, so the line lands the same way. */
.nav__link { display: inline-block; padding: 4px 0; }

/* Hamburger: 44 square target, three 2px lines that become an X over 240ms. */
.nav-toggle {
  display: none; position: relative; z-index: 41;
  width: 44px; height: 44px;
  padding: 0; border: 0; background: none; cursor: pointer;
}
.nav-toggle__bar {
  position: absolute; left: 11px; width: 22px; height: 2px; border-radius: 1px;
  background: var(--white);
  transition: transform 240ms var(--ease), opacity 240ms var(--ease);
}
.nav-toggle__bar:nth-child(1) { top: 14px; }
.nav-toggle__bar:nth-child(2) { top: 21px; }
.nav-toggle__bar:nth-child(3) { top: 28px; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Phone menu, the way the iPhone page's opens: a charcoal panel drops down
   from under the header row, as tall as its items plus padding, with 20px
   bottom corners. The header row and the status bar above it keep the
   page's near-black; the rest of the screen below the panel is near-black
   at 88% over the page, and the strip under the home indicator stays solid.
   Open: the overlay fades in over 320ms while the panel slides down 12px
   into place, and the items follow, each 40ms after the one before. Close:
   the same in reverse over 240ms, all together. Ease-out only, nothing
   bounces. The overlay scrolls inside itself when the phone is shorter than
   the panel (held sideways) and its scroll ends clear of the home indicator.
   Focus and keyboard behaviour are in lava.js and untouched: the overlay is
   visible the instant it opens, so the first link can take focus while it is
   still fading in. lava.js sets top to the header row's exact bottom on
   open; --masthead-h is the same value from the stylesheet. */
:root { --masthead-h: calc(36px + 44px + env(safe-area-inset-top, 0px)); }

/* While the menu is open the page is locked where it is. iOS Safari ignores
   overflow: hidden on the body, so lava.js fixes the body at its scroll
   offset (top: -scrollY); the header, fixed already, stays in place and
   shown, near-black, without its shadow (the panel hangs from it). lava.js
   puts it all back and restores the scroll position on close. */
body.menu-open .masthead { transform: none; background: var(--near-black); box-shadow: none; }

/* The backdrop: fixed under the header, refuses touches (touch-action: none,
   so a drag on it cannot move the page) and contains overscroll. It does
   not scroll; the panel inside it does. */
.menu {
  position: fixed; top: var(--masthead-h); left: 0; right: 0; bottom: 0; z-index: 30;
  display: none;
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: rgba(11, 11, 12, 0.88); color: var(--white);
  touch-action: none; overscroll-behavior: contain;
  visibility: hidden; opacity: 0;
  transition: opacity 240ms var(--ease), visibility 0s linear 240ms;
}
.menu::after {
  content: ""; position: fixed; left: 0; right: 0; bottom: 0;
  height: env(safe-area-inset-bottom, 0px); background: var(--near-black);
}
.menu.is-open { visibility: visible; opacity: 1; transition: opacity 320ms var(--ease); }
.menu__inner {
  display: flex; flex-direction: column;
  padding-top: 28px; padding-bottom: 36px;
  /* 24px of solid near-black between the header row and the panel, so the
     logo has air before the charcoal starts; the panel's own top edge and
     its bottom corners are unchanged. */
  border-top: 24px solid var(--near-black);
  background: var(--charcoal); border-radius: 0 0 20px 20px;
  /* The panel is the one thing that scrolls (a phone held sideways is
     shorter than it), and its scroll never chains to the page. */
  max-height: 100%; overflow-y: auto;
  overscroll-behavior: contain; touch-action: pan-y;
  transform: translateY(-12px);
  transition: transform 240ms var(--ease);
}
.menu.is-open .menu__inner { transform: none; transition: transform 320ms var(--ease); }
.menu__list { display: flex; flex-direction: column; gap: 28px; }
.menu__link { font: 600 32px/1.1 var(--display); }
.menu__rule { height: 1px; margin: 40px 0 28px; background: var(--edge-dark); --i: 5; }
.menu__email, .menu__phone { align-self: flex-start; font: 500 16px/1.4 var(--body); }
.menu__email { color: var(--gold); margin-bottom: 14px; --i: 5; }
.menu__phone { --i: 6; }

/* The items: each fades and rises 8px into place, --i steps of 40ms after
   the panel starts. Closing drops the stagger so everything leaves together. */
.menu__list li:nth-child(1) { --i: 1; }
.menu__list li:nth-child(2) { --i: 2; }
.menu__list li:nth-child(3) { --i: 3; }
.menu__list li:nth-child(4) { --i: 4; }
.menu__list li, .menu__rule, .menu__email, .menu__phone {
  opacity: 0; transform: translateY(8px);
  transition: opacity 240ms var(--ease), transform 240ms var(--ease);
}
.menu.is-open .menu__list li, .menu.is-open .menu__rule, .menu.is-open .menu__email, .menu.is-open .menu__phone {
  opacity: 1; transform: none;
  transition: opacity 320ms var(--ease) calc(var(--i, 0) * 40ms), transform 320ms var(--ease) calc(var(--i, 0) * 40ms);
}

@media (max-width: 1023px) {
  .nav { display: none; }
  .nav-toggle { display: block; }
  .menu { display: block; }
}
@media (max-width: 767px) {
  :root { --masthead-h: calc(18px + 44px + env(safe-area-inset-top, 0px)); }
  .masthead__inner { padding-top: calc(18px + env(safe-area-inset-top, 0px)); }
  .masthead__logo img { height: 30px; }
}

/* --------------------------------------------------------------- footer -- */
.site-footer { background: var(--near-black); color: var(--gray-3); font: 400 14px/1.5 var(--body); }
.site-footer__rule { height: 1px; background: var(--hairline-dark); }
.site-footer__top {
  display: flex; align-items: center; justify-content: space-between;
  padding-top: 48px;
}
.site-footer__logo img { height: 36px; width: auto; }
.site-footer__nav { display: flex; gap: 36px; font: 500 15px/1 var(--body); color: var(--gray-5); }
.site-footer__nav a, .site-footer__contact a { display: inline-block; }
/* The bottom padding grows by the phone's bottom inset so the copyright line
   never sits under the home indicator. */
.site-footer__meta {
  display: flex; align-items: baseline; justify-content: space-between;
  padding: 32px 0 calc(56px + env(safe-area-inset-bottom, 0px));
}
.site-footer__contact { display: flex; gap: 28px; }
.site-footer__contact a, .site-footer__address { color: var(--gray-5); }

/* 768 to 1279: one row does not fit (the phone number wrapped and LinkedIn
   ran into the copyright), so the meta stacks: the contact links on the
   first row, each kept whole and wrapping as a unit, the copyright under
   them. 1280 and up is the single row above, untouched. */
@media (min-width: 768px) and (max-width: 1279px) {
  .site-footer__meta { flex-direction: column; align-items: flex-start; gap: 14px; }
  .site-footer__contact { flex-wrap: wrap; row-gap: 8px; }
  .site-footer__contact a, .site-footer__address { white-space: nowrap; }
}

@media (max-width: 767px) {
  .site-footer { font-size: 13px; }
  .site-footer__top { flex-direction: column; align-items: flex-start; gap: 24px; padding-top: 40px; }
  .site-footer__logo img { height: 30px; }
  .site-footer__nav { gap: 22px; font-size: 14px; }
  .site-footer__meta { flex-direction: column; align-items: flex-start; gap: 8px; padding: 24px 0 calc(40px + env(safe-area-inset-bottom, 0px)); }
  .site-footer__contact { flex-direction: column; gap: 8px; }
  .site-footer__address { max-width: 22em; }
}

/* ------------------------------------------------------- reduced motion -- */
/* Nothing moves and nothing fades: the menu appears and disappears at once,
   underlines and arrows snap, buttons stay put. Last in the file so it wins
   every tie on specificity. */
@media (prefers-reduced-motion: reduce) {
  .btn, .link__text, .link__arrow, .nav__link, .nav-toggle__bar,
  .menu, .menu.is-open, .menu__inner, .menu.is-open .menu__inner,
  .menu__list li, .menu__rule, .menu__email, .menu__phone, .menu__link,
  .menu.is-open .menu__list li, .menu.is-open .menu__rule, .menu.is-open .menu__email, .menu.is-open .menu__phone,
  .site-footer__nav a, .site-footer__contact a { transition: none; }
  .btn:hover, .btn:focus-visible { transform: none; }
}
