/* ==========================================================================
   Northline Works - site.css
   The entire design system. Shared by every page on the site.

   Contents
     01  Design tokens
     02  Base and reset
     03  Skip link
     04  Site header
     05  Buttons
     06  Site footer
     07  Home - hero
     08  Home - studio panel
     09  Home - work section and project cards
     10  Home - about section
     11  Home - contact section and form
     12  Project page - hero
     13  Project page - facts strip
     14  Project page - features
     15  Project page - gallery
     16  Project page - closing call to action
     17  Error page
     18  Responsive - shared and home
     19  Responsive - project page
     20  Reduced motion

   Every colour, font and spacing value comes from the tokens in section 01.
   Reach for a token before typing a raw hex value.
   ========================================================================== */


/* ==========================================================================
   01  Design tokens
   ========================================================================== */

:root {
  --ink: #080808;        /* page background. Near black, not pure, so the red reads warmer against it */
  --ink-soft: #111111;   /* form fields and image wells, one step up from the page so inputs are findable */
  --paper: #f2f0ea;      /* body text on dark, and the background of the two light sections */
  --paper-bright: #fffef9;  /* headings and buttons that need to sit above --paper */
  --muted: #aaa8a2;      /* micro-labels and secondary text */

  /* Translucent rather than a flat grey on purpose: the same value is used
     for hairlines on the dark page and inside the light sections, and it
     picks up whatever is behind it instead of needing a second token. */
  --line: rgba(255, 255, 255, 0.17);

  --red: #ff2a18;        /* the studio accent. Used sparingly - eyebrows, one panel, primary buttons */
  --red-dark: #d91f0d;   /* hover state for anything filled with --red */

  /* Condensed display face for headings. All system fonts, no webfont
     loading anywhere on this site, which is why there is no flash of
     unstyled text and no font request on page load. Arial Narrow is the
     one doing the work; the rest are fallbacks for machines without it. */
  --display: "Arial Narrow", "Franklin Gothic Condensed", Impact, sans-serif;
  --sans: Arial, Helvetica, sans-serif;
}


/* ==========================================================================
   02  Base and reset
   ========================================================================== */

* { box-sizing: border-box; }
html { scroll-behavior: smooth; background: var(--ink); }

body {
  margin: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.55;

  /* Load bearing. The hero swoosh is positioned deliberately outside the
     viewport at some widths, and this stops it adding a horizontal
     scrollbar. Be aware it also hides the known headline clipping on narrow
     phones - see the note on .headline-line in section 07. Removing this
     line will not fix that; it will just turn clipping into sideways
     scrolling. */
  overflow-x: hidden;
}

body::selection { color: var(--paper-bright); background: var(--red); }
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
/* Form controls do not inherit font by default, so without this the contact
   form renders in the browser's UI font instead of the site's. */
button, input, select, textarea { font: inherit; }

/* :focus-visible rather than :focus, so the ring appears for keyboard users
   but not on mouse clicks. This is the site's only focus indicator - if you
   remove an outline anywhere, make sure this still applies or replace it
   locally. The offset keeps the ring clear of the element's own border. */
:focus-visible { outline: 2px solid var(--paper-bright); outline-offset: 4px; }

/* Shared micro-label. Used for the red uppercase strap above most headings. */
.eyebrow,
.section-index,
.project-type,
.hero-mark-label {
  margin: 0 0 1.4rem;
  color: var(--red);
  font-size: .72rem;
  font-weight: 800;
  letter-spacing: .18em;
  text-transform: uppercase;
}


/* ==========================================================================
   03  Skip link
   Hidden off screen until it receives keyboard focus.
   ========================================================================== */

/* Moved off screen with a transform rather than display:none or
   visibility:hidden, because both of those remove it from the tab order and
   it would never receive focus in the first place. It is the first focusable
   element on every page, so it must stay reachable while invisible. */
.skip-link {
  position: fixed;
  z-index: 100;         /* above the header, which sits at 20 */
  top: .75rem;
  left: .75rem;
  padding: .75rem 1rem;
  color: var(--ink);
  background: var(--paper-bright);
  transform: translateY(-180%);
}

.skip-link:focus { transform: translateY(0); }


/* ==========================================================================
   04  Site header
   Identical markup on every page. Only the link targets differ.
   ========================================================================== */

/* Absolute, not fixed and not sticky. It sits over the top of the hero rather
   than pushing it down, which is why the hero and project heroes both carry a
   large top padding to clear it. It scrolls away with the page on purpose -
   there is no sticky nav on this site.

   Because it is taken out of flow, the top padding on .hero (section 07) and
   .project-hero (section 12) is the only thing stopping the header from
   covering the content. Change one and check the other. */
.site-header {
  position: absolute;
  z-index: 20;
  top: 0;
  left: 0;
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  padding: 1.5rem clamp(1.25rem, 4vw, 4.5rem);
  border-bottom: 1px solid var(--line);
}

.brand { display: inline-flex; align-items: center; gap: .7rem; width: fit-content; }
.brand > img { width: 2.5rem; height: 2rem; object-fit: contain; }
.brand-name { display: grid; line-height: .85; text-transform: uppercase; }
.brand-name strong { font-family: var(--display); font-size: 1.05rem; letter-spacing: .055em; }
.brand-name small { margin-top: .28rem; font-size: .58rem; letter-spacing: .42em; }

.site-header nav {
  display: flex;
  align-items: center;
  gap: clamp(1.1rem, 3vw, 2.75rem);
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.site-header nav a { transition: color 160ms ease; }
.site-header nav a:hover,
.site-header nav a:focus-visible { color: var(--red); }
.nav-contact { padding: .65rem .9rem; border: 1px solid var(--line); }


/* ==========================================================================
   05  Buttons
   ========================================================================== */

.button {
  display: inline-flex;
  min-height: 3.2rem;
  align-items: center;
  justify-content: center;
  gap: .75rem;
  padding: .85rem 1.25rem;
  border: 1px solid var(--line);
  border-radius: 0;
  background: transparent;
  color: var(--paper-bright);
  font-size: .78rem;
  font-weight: 800;
  letter-spacing: .105em;
  text-transform: uppercase;
  transition: 160ms ease;
  cursor: pointer;
}

.button:hover,
.button:focus-visible { border-color: var(--paper-bright); transform: translateY(-2px); }
.button--primary { border-color: var(--red); background: var(--red); }
.button--primary:hover,
.button--primary:focus-visible { border-color: var(--red-dark); background: var(--red-dark); }
.button--quiet { color: #d2d0cb; }


/* ==========================================================================
   06  Site footer
   ========================================================================== */

footer {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 1.1rem 3rem;
  align-items: end;
  padding: 2rem clamp(1.25rem, 4vw, 4.5rem);
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: .72rem;
}

.footer-brand { color: var(--paper); }
footer > p:not(.copyright) { grid-column: 1; margin: 0; }
.footer-links { grid-column: 2; grid-row: 1; display: flex; gap: 1.5rem; color: var(--paper); font-weight: 700; }
.footer-links a:hover,
.footer-links a:focus-visible { color: var(--red); }
.copyright { grid-column: 2; margin: 0; text-align: right; }


/* ==========================================================================
   07  Home - hero
   The headline is manually broken across four .headline-line spans, each
   set to nowrap. Changing the headline wording means re-checking the breaks.
   ========================================================================== */

.hero {
  position: relative;
  display: grid;

  /* svh, not vh. On mobile browsers vh is the height with the address bar
     hidden, so a 100vh hero gets cut off on first paint and only fits after
     the bar collapses. svh uses the smallest state, so it fits immediately. */
  min-height: 100svh;

  grid-template-columns: minmax(0, 1.3fr) minmax(18rem, .7fr);
  grid-template-rows: 1fr auto;
  align-items: center;
  gap: 1.25rem 4rem;
  padding: clamp(7.5rem, 10vw, 9rem) clamp(1.25rem, 6vw, 7rem) 1.75rem;

  /* Faint red glow behind the swoosh, sitting over the flat ink base. */
  background: radial-gradient(circle at 82% 36%, rgba(255, 42, 24, .13), transparent 22rem), var(--ink);

  /* Creates a new stacking context so the .hero-grid overlay's z-index:-1
     stays trapped inside the hero. Without it that overlay slips behind the
     page background and disappears entirely. */
  isolation: isolate;

  overflow: hidden;     /* clips the oversized swoosh, which is wider than the column */
}

/* Decorative graph-paper overlay. Two 1px gradients crossed to make a grid,
   rather than a background image, so it costs no request and scales cleanly. */
.hero-grid {
  position: absolute;
  z-index: -1;          /* behind the hero text but above the hero background. See isolation above. */
  inset: 0;
  background-image: linear-gradient(var(--line) 1px, transparent 1px), linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 7.5rem 7.5rem;

  /* Fades the grid out at the top and bottom so it does not collide with the
     header rule or hard-stop against the panel below. */
  mask-image: linear-gradient(to bottom, transparent 5%, black 45%, transparent 92%);

  opacity: .18;
}

.hero-copy { max-width: 66rem; }

.hero h1 {
  max-width: none;
  margin: 0;
  font-family: var(--display);
  font-size: clamp(3.75rem, 7.25vw, 7.6rem);
  font-stretch: condensed;
  font-weight: 900;
  letter-spacing: -.055em;
  line-height: .8;
  text-transform: uppercase;
}

/* Each line is its own block and never wraps, so the four-line stack in the
   markup is exactly what renders. This is why the line breaks are a content
   decision made in index.html rather than something the browser works out.

   KNOWN ISSUE: nowrap means the headline cannot shrink to fit. At a 375px
   viewport "Games, tools" needs 384px and has 340px, so about 44px is cut off
   and hidden by the overflow-x:hidden on body. Inherited from the previous
   React build, which clips identically. The fix is to lower the phone clamp
   in section 18 to roughly clamp(2.2rem, 11.5vw, 4.5rem), which fits down to
   about 320px - it is left alone because it changes the type scale and that
   is Peter's call. */
.hero h1 .headline-line { display: block; color: inherit; white-space: nowrap; }
.hero h1 .headline-line--accent { color: var(--red); }

.hero-intro {
  max-width: 39rem;
  margin: 1.4rem 0 0;
  color: #d4d2cc;
  font-size: clamp(.98rem, 1.25vw, 1.14rem);
  line-height: 1.48;
}

.hero-actions { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: 1.35rem; }

.hero-mark { position: relative; align-self: stretch; min-height: 20rem; border-left: 1px solid var(--line); }
.hero-mark-label { position: absolute; top: 0; right: 0; color: var(--muted); }

.hero-mark img {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(39vw, 32rem);
  max-width: none;
  opacity: .95;
  filter: drop-shadow(0 2.2rem 4.5rem rgba(255, 42, 24, .15));
  transform: translate(-49%, -52%) rotate(-3deg);
}

.hero-stats { display: grid; grid-column: 1 / -1; grid-template-columns: repeat(3, 1fr); border-top: 1px solid var(--line); }
.hero-stats div { display: flex; align-items: baseline; gap: .8rem; padding: 1rem 1.5rem 0 0; }
.hero-stats div + div { padding-left: 1.5rem; border-left: 1px solid var(--line); }
.hero-stats strong { font-family: var(--display); font-size: clamp(1.8rem, 3.5vw, 3rem); line-height: 1; }
.hero-stats span { color: var(--muted); font-size: .68rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; }


/* ==========================================================================
   08  Home - studio panel
   The full-width red band under the hero.
   ========================================================================== */

.intro-panel {
  display: grid;
  grid-template-columns: .34fr 1fr;
  gap: 3rem;
  padding: clamp(5rem, 10vw, 10rem) clamp(1.25rem, 6vw, 7rem);
  background: var(--red);
  color: var(--paper-bright);
}

.intro-panel .section-index,
.about-section .section-index { color: currentColor; }
.intro-panel > div { max-width: 70rem; }

/* Shared display heading, used by every major section on the home page. */
.intro-panel h2,
.section-heading h2,
.about-section h2,
.contact-section h2 {
  margin: 0;
  font-family: var(--display);
  font-size: clamp(3.4rem, 7vw, 7.2rem);
  font-stretch: condensed;
  font-weight: 900;
  letter-spacing: -.052em;
  line-height: .88;
  text-transform: uppercase;
}

.intro-panel h2 { max-width: 11ch; }
.intro-panel div > p { max-width: 44rem; margin: 2rem 0 0; font-size: clamp(1.1rem, 1.6vw, 1.35rem); }


/* ==========================================================================
   09  Home - work section and project cards
   Four .project-card articles in a two column grid. Crystal Drive Z spans
   the full width via --feature, PowerUp uses the three phone --phones layout.
   ========================================================================== */

.work-section { padding: clamp(5rem, 10vw, 9rem) clamp(1.25rem, 6vw, 7rem); }
.section-heading { display: grid; grid-template-columns: 1fr .55fr; align-items: end; gap: 3rem; margin-bottom: clamp(3rem, 6vw, 5rem); }
.section-heading > p { max-width: 32rem; margin: 0 0 .6rem; color: var(--muted); font-size: 1.05rem; }

/* The hairlines between cards are the grid's own background showing through a
   1px gap, not borders on the cards. Doing it this way avoids doubled-up
   borders where two cards meet and keeps every line exactly 1px. It only
   works because .project-card sets an opaque background - remove that and
   the whole grid turns grey. */
.project-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px; background: var(--line); border: 1px solid var(--line); }

/* min-width:0 lets a card shrink below its content width. Grid items default
   to min-width:auto, which would let a long project title push the column
   wider than its share and break the two-up layout. */
.project-card { min-width: 0; background: var(--ink); }

/* Crystal Drive Z is the lead project and spans both columns. */
.project-card--feature { grid-column: 1 / -1; }

.project-media { position: relative; aspect-ratio: 16 / 9; background: #171717; overflow: hidden; }
.project-card--feature .project-media { aspect-ratio: 2.35 / 1; }

.project-media > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(.82) contrast(1.06);
  transition: transform 420ms cubic-bezier(.22, 1, .36, 1), filter 240ms ease;
}

.project-card:hover .project-media > img { filter: saturate(1) contrast(1.04); transform: scale(1.025); }

/* Darkens the top of every screenshot so the white index and status labels
   stay readable whatever the image behind them happens to be.
   pointer-events:none is required: without it this overlay sits on top of the
   card and swallows the hover that drives the image zoom. */
.project-media::after {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(0, 0, 0, .33), transparent 32%, rgba(0, 0, 0, .06));
  content: "";
  pointer-events: none;
}

.project-media-topline {
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  display: flex;
  width: 100%;
  justify-content: space-between;
  padding: 1rem;
  color: white;
  font-size: .67rem;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
}

.project-media-topline span:last-child { padding: .28rem .52rem; background: var(--red); }

.project-copy { padding: clamp(1.6rem, 3vw, 2.75rem); }
.project-card--feature .project-copy { display: grid; grid-template-columns: .72fr 1.1fr .8fr; grid-template-rows: auto 1fr auto; gap: 0 2rem; }
.project-card--feature .project-type,
.project-card--feature h3 { grid-column: 1; }
.project-card--feature .project-description,
.project-card--feature .project-tags { grid-column: 2; }
.project-card--feature .project-description { grid-row: 1 / span 2; }
.project-card--feature .project-tags { grid-row: 3; }
.project-card--feature .project-links { grid-column: 3; grid-row: 1 / span 3; align-self: end; justify-self: end; }

.project-type { margin-bottom: .8rem; }

.project-copy h3 {
  margin: 0;
  font-family: var(--display);
  font-size: clamp(2.3rem, 4vw, 4.6rem);
  font-weight: 900;
  letter-spacing: -.045em;
  line-height: .92;
  text-transform: uppercase;
}

.project-description { max-width: 43rem; margin: 1.35rem 0; color: #c0beb8; }
.project-tags { display: flex; flex-wrap: wrap; gap: .45rem; margin: 0; padding: 0; list-style: none; }
.project-tags li { padding: .32rem .55rem; border: 1px solid var(--line); color: var(--muted); font-size: .65rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; }
.project-links { display: flex; align-items: center; gap: 1.25rem; margin-top: 1.8rem; }
.project-links a { border-bottom: 1px solid var(--red); padding-bottom: .25rem; font-size: .72rem; font-weight: 800; letter-spacing: .11em; text-transform: uppercase; }
.project-links a:hover,
.project-links a:focus-visible { color: var(--red); }
.project-links .project-secondary-link { border-color: transparent; color: var(--muted); }

/* PowerUp is the only phone app, so its card shows three portrait
   screenshots instead of one landscape image. The tinted gradient stands in
   for the missing full-bleed screenshot. */
.project-card--phones .project-media { background: linear-gradient(135deg, rgba(255, 42, 24, .14), transparent 52%), #151515; }

/* The negative bottom inset (-19%) is deliberate: the phones are taller than
   the 16:9 card, so they run past the bottom edge and get clipped by the
   overflow:hidden on .project-media. That crop is the effect. */
.phone-triptych { position: absolute; inset: 8% 8% -19%; display: flex; align-items: start; justify-content: center; gap: 3%; }

.phone-triptych img { width: 31.33%; height: auto; border: 1px solid rgba(255, 255, 255, .22); box-shadow: 0 1.3rem 3rem rgba(0, 0, 0, .42); }

/* Drops the middle phone so the three are not a flat row. */
.phone-triptych img:nth-child(2) { margin-top: 7%; }


/* ==========================================================================
   10  Home - about section
   Light section. The swoosh is a faint rotated watermark behind the text.
   ========================================================================== */

.about-section {
  position: relative;
  display: grid;
  grid-template-columns: .8fr 1fr;
  gap: clamp(3rem, 8vw, 9rem);
  padding: clamp(6rem, 11vw, 11rem) clamp(1.25rem, 6vw, 7rem);
  background: var(--paper);
  color: var(--ink);
  overflow: hidden;
}

.about-heading,
.about-body { position: relative; z-index: 1; }
.about-section h2 { max-width: 9ch; }
.about-body > p { max-width: 44rem; margin: 0 0 3.5rem; font-size: clamp(1.15rem, 1.8vw, 1.48rem); }

.capability-list { border-top: 1px solid rgba(0, 0, 0, .22); }
.capability-list > div { display: grid; grid-template-columns: 3rem .6fr 1fr; gap: 1rem; padding: 1.35rem 0; border-bottom: 1px solid rgba(0, 0, 0, .22); }
.capability-list span { color: var(--red); font-size: .7rem; font-weight: 800; }
.capability-list strong { font-family: var(--display); font-size: 1.7rem; line-height: 1; text-transform: uppercase; }
.capability-list p { margin: 0; color: #4f4e4a; }

/* Oversized logo bleeding off the bottom right corner as a watermark. The
   opacity is very low on purpose - it must stay under the body text without
   ever competing with it, and the text sits above it via the z-index:1 on
   .about-heading and .about-body. The <img> is aria-hidden in the markup, so
   it is decorative in the accessibility tree too, not just visually. */
.about-stamp { position: absolute; right: -9rem; bottom: -10rem; width: min(40vw, 35rem); opacity: .055; transform: rotate(-8deg); }


/* ==========================================================================
   11  Home - contact section and form
   The form is wired up in assets/js/site.js. .form-status is the live region
   the script writes into, so it must keep its role and aria-live attributes.
   ========================================================================== */

.contact-section { display: grid; grid-template-columns: .75fr 1fr; gap: clamp(3rem, 8vw, 9rem); padding: clamp(6rem, 11vw, 10rem) clamp(1.25rem, 6vw, 7rem); }
.contact-copy { align-self: start; }
.contact-copy h2 { max-width: 8ch; }
.contact-copy > p:not(.section-index) { max-width: 30rem; margin: 1.6rem 0; color: var(--muted); font-size: 1.08rem; }
.contact-email { display: inline-block; border-bottom: 1px solid var(--red); padding-bottom: .25rem; font-weight: 700; }

.contact-form { display: grid; gap: 1.15rem; }
.form-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.15rem; }
.contact-form label { display: grid; gap: .5rem; }
.contact-form label > span { color: var(--muted); font-size: .68rem; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; }

.contact-form input,
.contact-form select,
.contact-form textarea {
  width: 100%;
  border: 1px solid var(--line);

  /* Explicit zero. iOS Safari rounds input corners by default, which would
     make the fields the only curved thing on a site with no curves anywhere. */
  border-radius: 0;

  /* Safe to remove the default outline only because the :focus rule below
     replaces it with a visible red border and ring. Never drop one without
     the other. */
  outline: none;

  background: var(--ink-soft);
  color: var(--paper-bright);
  padding: .95rem 1rem;
}

/* box-shadow rather than a thicker border, so gaining focus does not change
   the element's size and nudge the layout. */
.contact-form input:focus,
.contact-form select:focus,
.contact-form textarea:focus { border-color: var(--red); box-shadow: 0 0 0 1px var(--red); }

/* Vertical only. Horizontal resizing would let the textarea escape the grid
   column and overlap the section beside it. */
.contact-form textarea { min-height: 10rem; resize: vertical; }

.form-submit { width: 100%; }
.form-note,
.form-status { margin: 0; color: #85837f; font-size: .75rem; }

/* Empty until site.js writes into it. The min-height reserves the one line it
   will need, so the button does not jump down the page the moment somebody
   presses send. Brighter than .form-note because it carries the real outcome. */
.form-status { min-height: 1.2em; color: var(--paper); }


/* ==========================================================================
   12  Project page - hero
   Everything from here down only appears on projects/<slug>/index.html.
   ========================================================================== */

/* The one place the body class earns its keep. The project pages end on a
   full-bleed red section, and without an explicit background on main the ink
   behind it is the body's, which shows through differently while the page is
   still painting. Everything else on these pages is scoped by its own
   .project-* class name and needs no page qualifier. */
.page-project main { background: var(--ink); color: var(--paper); }

.project-hero {
  display: grid;
  min-height: 100svh;
  grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr);
  padding-top: 5.8rem;
}

.project-hero-copy {
  display: flex;
  min-width: 0;
  flex-direction: column;
  justify-content: center;
  padding: clamp(4rem, 7vw, 7rem) clamp(1.25rem, 5vw, 5.5rem);
}

.project-hero-copy .button { align-self: flex-start; }

.project-back-link {
  width: fit-content;
  margin-bottom: clamp(3rem, 7vw, 6rem);
  color: var(--muted);
  font-size: .7rem;
  font-weight: 800;
  letter-spacing: .13em;
  text-transform: uppercase;
}

.project-back-link:hover,
.project-back-link:focus-visible { color: var(--red); }

.project-eyebrow {
  margin: 0 0 1.25rem;
  color: var(--red);
  font-size: .7rem;
  font-weight: 800;
  letter-spacing: .17em;
  text-transform: uppercase;
}

.project-hero h1,
.project-features h2,
.project-cta h2 {
  margin: 0;
  font-family: var(--display);
  font-weight: 900;
  letter-spacing: -.055em;
  line-height: .86;
  text-transform: uppercase;
}

.project-hero h1 { max-width: 8ch; font-size: clamp(4.4rem, 8.2vw, 10rem); }

/* Opt-in modifier for project names too long for the default hero size.
   Currently used by Resonance Social only. Add the class in the HTML.

   The h1.title-long compound selector is what makes this work without
   !important. It scores one class higher than the plain .project-hero h1
   above and higher than the phone override in section 19, so it wins on
   specificity wherever it applies, regardless of source order. The previous
   React build did the same job with a separate CSS module full of
   !important - do not reintroduce that. */
.project-hero h1.title-long {
  max-width: 100%;
  font-size: clamp(3.75rem, 5vw, 6.25rem);
  letter-spacing: -.045em;
}

.project-hero-line { max-width: 27rem; margin: 2rem 0 .8rem; font-size: clamp(1.3rem, 2.1vw, 2rem); font-weight: 700; line-height: 1.15; }
.project-lede { max-width: 37rem; margin: 0 0 1.8rem; color: #bbb9b3; }
.project-hero-tags { display: flex; flex-wrap: wrap; gap: .45rem; margin-bottom: 2rem; }
.project-hero-tags span { padding: .35rem .55rem; border: 1px solid var(--line); color: var(--muted); font-size: .62rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; }

.project-hero-media { position: relative; min-height: 34rem; overflow: hidden; background: #171717; }
.project-hero-media::after { position: absolute; inset: 0; background: linear-gradient(90deg, rgba(0, 0, 0, .38), transparent 38%), linear-gradient(0deg, rgba(0, 0, 0, .25), transparent 35%); content: ""; }
.project-hero-media img { width: 100%; height: 100%; object-fit: cover; }
.project-media-index { position: absolute; z-index: 1; top: 2rem; right: 2rem; color: white; font-size: .65rem; font-weight: 800; letter-spacing: .16em; }


/* ==========================================================================
   13  Project page - facts strip
   ========================================================================== */

.project-facts { display: grid; grid-template-columns: repeat(3, 1fr); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.project-facts div { display: grid; gap: .4rem; padding: clamp(1.5rem, 3vw, 2.5rem) clamp(1.25rem, 5vw, 5.5rem); }
.project-facts div + div { border-left: 1px solid var(--line); }
.project-facts span { color: var(--red); font-size: .65rem; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; }
.project-facts strong { font-family: var(--display); font-size: clamp(1.7rem, 3vw, 3rem); line-height: 1; text-transform: uppercase; }


/* ==========================================================================
   14  Project page - features
   Light section, three numbered rows.
   ========================================================================== */

.project-features { display: grid; grid-template-columns: .65fr 1fr; gap: clamp(3rem, 8vw, 9rem); padding: clamp(6rem, 11vw, 11rem) clamp(1.25rem, 6vw, 7rem); background: var(--paper); color: var(--ink); }
.project-features h2,
.project-cta h2 { font-size: clamp(4rem, 7vw, 8rem); }

.project-feature-list { border-top: 1px solid rgba(0, 0, 0, .22); }
.project-feature-list article { display: grid; grid-template-columns: 2.8rem minmax(10rem, .8fr) 1fr; gap: 1.2rem; padding: 1.6rem 0; border-bottom: 1px solid rgba(0, 0, 0, .22); }
.project-feature-list article > span { color: var(--red); font-size: .68rem; font-weight: 800; }
.project-feature-list h3 { margin: 0; font-family: var(--display); font-size: clamp(1.7rem, 2.7vw, 2.7rem); line-height: .95; text-transform: uppercase; }
.project-feature-list p { margin: 0; color: #4f4e4a; }


/* ==========================================================================
   15  Project page - gallery
   Two variants. The default is a landscape grid. Add --portrait for phone
   screenshots, which are shown as three floating devices instead.
   ========================================================================== */

.project-gallery { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px; padding: 1px 0; background: var(--line); }
.project-gallery figure { min-width: 0; margin: 0; aspect-ratio: 16 / 9; background: #171717; overflow: hidden; }
.project-gallery img { width: 100%; height: 100%; object-fit: cover; }

/* Portrait variant, for phone screenshots. Switching to flex overrides the
   grid set above, so the two classes stack on the same element in the markup:
   class="project-gallery project-gallery--portrait". This block must stay
   after the base .project-gallery rules - it relies on source order, not
   specificity, since both selectors score the same. */
.project-gallery--portrait { display: flex; justify-content: center; gap: clamp(1rem, 4vw, 4rem); padding: clamp(4rem, 8vw, 8rem) clamp(1.25rem, 8vw, 10rem); background: radial-gradient(circle at 50% 50%, rgba(255, 42, 24, .12), transparent 42%), #111; }

/* aspect-ratio matches the source screenshots exactly (745x1341), so the
   frames are the right shape before the images have loaded and nothing
   reflows when they arrive. */
.project-gallery--portrait figure { flex: 0 1 22rem; aspect-ratio: 745 / 1341; border: 1px solid var(--line); box-shadow: 0 2rem 5rem rgba(0, 0, 0, .45); }

/* Drops the middle device, same trick as the .phone-triptych on the home page. */
.project-gallery--portrait figure:nth-child(2) { transform: translateY(2.5rem); }


/* ==========================================================================
   16  Project page - closing call to action
   ========================================================================== */

.project-cta { display: grid; grid-template-columns: 1fr .5fr; align-items: end; gap: 3rem; padding: clamp(6rem, 11vw, 11rem) clamp(1.25rem, 6vw, 7rem); background: var(--red); }
.project-cta .project-eyebrow { color: currentColor; }
.project-cta p { max-width: 26rem; margin: 0 0 1.5rem; font-size: 1.1rem; }
.project-cta .button--primary { border-color: var(--ink); background: var(--ink); }


/* ==========================================================================
   17  Error page
   ========================================================================== */

.error-section {
  display: flex;
  min-height: 100svh;
  flex-direction: column;
  justify-content: center;
  padding: clamp(7.5rem, 10vw, 9rem) clamp(1.25rem, 6vw, 7rem) 4rem;
}

.error-section h1 {
  max-width: 12ch;
  margin: 0;
  font-family: var(--display);
  font-size: clamp(3.75rem, 9vw, 8rem);
  font-stretch: condensed;
  font-weight: 900;
  letter-spacing: -.055em;
  line-height: .84;
  text-transform: uppercase;
}

.error-section > p { max-width: 34rem; margin: 1.6rem 0 2rem; color: var(--muted); font-size: 1.08rem; }
.error-actions { display: flex; flex-wrap: wrap; gap: .8rem; }


/* ==========================================================================
   18  Responsive - shared and home
   Two breakpoints only: 960px drops the multi column layouts to one column,
   700px is the phone pass.
   ========================================================================== */

@media (max-width: 960px) {
  .hero { grid-template-columns: 1fr; grid-template-rows: auto auto auto; align-content: center; }
  .hero-copy { z-index: 1; }
  .hero-mark { position: absolute; z-index: -1; top: 16%; right: -5%; width: 48%; min-height: 19rem; border: 0; opacity: .35; }
  .hero-mark-label { display: none; }
  .hero-mark img { width: 100%; }
  .hero-stats { grid-row: 3; }
  .project-card--feature .project-copy { display: block; }
  .project-card--feature .project-links { justify-self: start; }
  .intro-panel,
  .section-heading,
  .about-section,
  .contact-section { grid-template-columns: 1fr; gap: 2rem; }
  .section-heading > p { margin: 0; }
}

@media (max-width: 700px) {
  .site-header { padding: 1rem 1.1rem; }

  /* There is no hamburger menu. Below 700px the Work and About links are
     simply hidden and only the Contact button remains. That is a deliberate
     simplification, not a missing feature: both hidden links point at
     anchors further down the same page, so scrolling reaches them anyway. */
  .site-header nav a:not(.nav-contact) { display: none; }

  /* Swaps 100svh for a fixed height. On a phone the full viewport height
     leaves the hero stats stranded below the fold, and 42rem fits the whole
     hero including them. */
  .hero { min-height: 42rem; padding: 6.5rem 1.1rem 1.25rem; }

  /* The phone headline size, and it is measured rather than chosen by eye.

     The lines are nowrap (section 07), so the headline cannot shrink to fit -
     if the type is too big the longest line is simply cut off by the
     overflow-x:hidden on body. The old value, clamp(2.65rem, 13.2vw, 4.5rem),
     did exactly that: at a 375px viewport "Games, tools" needed 384px and had
     340px, so about 44px went missing off the right edge.

     The arithmetic, should the headline wording ever change:
       widest line width = font-size x 7.75   (measured for "Games, tools")
       room              = viewport - 35.2px  (2 x 1.1rem padding, below 700px)
     so the largest font that fits is (viewport - 35.2) / 7.75. That is about
     11.5vw and it tightens as the viewport narrows, so 11.2vw sits just under
     it across the whole phone range with a little slack.

     Re-measure if you change the headline text. Section 07 explains why. */
  .hero h1 { font-size: clamp(2.1rem, 11.2vw, 4.5rem); }
  .hero-mark { top: 11%; right: -20%; width: 65%; }
  .hero-stats { gap: 0; }
  .hero-stats div { display: grid; gap: .4rem; padding: 1rem .4rem 0; }
  .hero-stats div + div { padding-left: .7rem; }
  .hero-stats strong { font-size: 1.55rem; }
  .hero-stats span { font-size: .55rem; }
  .intro-panel,
  .work-section,
  .about-section,
  .contact-section { padding-right: 1.1rem; padding-left: 1.1rem; }
  .project-grid { grid-template-columns: 1fr; }
  .project-card--feature { grid-column: auto; }
  .project-card--feature .project-media { aspect-ratio: 16 / 9; }
  .form-row { grid-template-columns: 1fr; }
  .capability-list > div { grid-template-columns: 2rem .75fr 1.2fr; }
  .error-section { padding-right: 1.1rem; padding-left: 1.1rem; }
  footer { display: flex; flex-direction: column; align-items: flex-start; }
  .footer-links { flex-wrap: wrap; }
  .copyright { text-align: left; }
}


/* ==========================================================================
   19  Responsive - project page
   These pages have their own first breakpoint at 1100px, because the hero
   splits copy and image side by side and runs out of room sooner.
   ========================================================================== */

@media (max-width: 1100px) {
  .project-hero { grid-template-columns: 1fr; }
  .project-hero-media { min-height: 50vw; }
  .project-features,
  .project-cta { grid-template-columns: 1fr; }
}

@media (max-width: 700px) {
  .project-hero { padding-top: 4.8rem; }
  .project-hero-copy { padding-top: 3rem; }
  .project-back-link { margin-bottom: 2.5rem; }
  .project-hero h1 { font-size: clamp(3.6rem, 19vw, 6rem); }
  .project-hero h1.title-long { font-size: clamp(2.25rem, 13vw, 5rem); }
  .project-facts { grid-template-columns: 1fr; }
  .project-facts div + div { border-top: 1px solid var(--line); border-left: 0; }
  .project-feature-list article { grid-template-columns: 2rem 1fr; }
  .project-feature-list p { grid-column: 2; }
  .project-gallery { grid-template-columns: 1fr; }
  .project-gallery--portrait { gap: .6rem; padding-right: .75rem; padding-left: .75rem; }
  .project-gallery--portrait figure:nth-child(2) { transform: translateY(1.25rem); }
}


/* ==========================================================================
   20  Reduced motion
   ========================================================================== */

/* Honours the operating system setting for people who get motion sickness or
   migraines from movement. Turns off the smooth scrolling on the anchor nav
   and flattens every transition.

   The !important and the universal selector are both justified here: this has
   to beat any transition declared anywhere above it, including ones added
   later by someone who has not read this block.

   .01ms rather than 0s on purpose - a zero duration stops transitionend from
   firing in some browsers, which would break any future code waiting on it.
   Nothing on this site listens for it today, but the cost of the safer value
   is nothing. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after { scroll-behavior: auto !important; transition-duration: .01ms !important; }
}
