/* ---- site.css — shared chrome: the nav, buttons and the footer ------------
   The furniture that appears on every page. base.css sets the floor (reset,
   type scale, accessibility); this styles the components the shell renders
   around the content.

   Replaced the prototype's nav.css/nav.js page-switcher, which built its markup
   in JavaScript and linked four experiment pages. This nav is real HTML linking
   real routes; both prototype files were deleted in Section 3 along with
   hero.html, when the hero became the homepage. */

/* ---- Nav -----------------------------------------------------------------
   Pinned to the top and floating over the content, carried across from the
   prototype's treatment. The bar is solid rather than frosted, so the labels
   stay legible over whatever is moving underneath: on the homepage that is the
   hero's blobs.

   There is no hamburger. Three items (Home, Work, Get in touch) measure about
   275px at the small-screen padding, so the whole bar fits inside 375px with
   room to spare, and hiding a phone-first audience's route to contact behind a
   button they have to find first would be worse design than a tighter bar. */
.site-nav {
  position: fixed;
  top: var(--space-4);
  left: 50%;
  transform: translateX(-50%);
  z-index: 100;

  display: flex;
  align-items: center;
  gap: var(--space-1);

  max-width: calc(100% - (var(--space-4) * 2));
  /* Even padding all round, so the round badge and the pill button sit inside
     the bar with the same margin on every side. That even inset is most of
     what makes a floating bar look made rather than assembled. */
  padding: var(--space-2);
  border-radius: var(--radius-pill);

  /* Solid, not frosted. The bar used to be translucent with a blur behind it,
     which meant the hero's blobs slid about underneath the labels. A solid bar
     is calmer, is legible over anything, and costs no per-frame blur. One soft
     shadow, not a stack: the bar genuinely floats above the page and the shadow
     is what says so. */
  background: var(--bg-raised);
  border: 1px solid var(--line);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.45);
}

/* ---- The rolling label ---------------------------------------------------
   The window: exactly one line tall, with the overflow clipped. The label is
   printed twice inside it, so sliding the pair up by one line swaps the word
   for an identical one arriving from below.

   It started in the nav, where every label is one short word, and the first
   version was built for exactly that: a window with a hard height of one line,
   two copies stacked inside it, and a travel of half the pair. That works right
   up until the text wraps, and then the window is one line tall around
   something two lines tall and the label is sliced in half.

   It is used on project titles now, which are real names of real businesses and
   not a word chosen to fit. "AJC Removals & Clearances" is 272px at the card's
   size, which fits a 343px card on a 375px phone, but only just, and the next
   client Picsel signs does not know that. So nothing here states a height.

   How it works instead: the SECOND copy is taken out of the flow and pinned
   directly under the first. The window then takes its height from the first
   copy alone, whatever that turns out to be, and one line or three is decided
   by the text rather than by this file. The travel is -100% of the inner
   element, and the inner element is exactly one copy tall, so the distance is
   always precisely one copy. Nothing has to be told the line height and nothing
   can disagree with anything else about it.

   The whole-pixel argument that picked 1.6 for the nav is preserved and moved
   to the nav itself, which is the only place that knows its own font size. */
.roll {
  display: block;
  overflow: hidden;
}

.roll__inner {
  display: block;
  /* The containing block for the pinned second copy below, so that copy is
     positioned against the inner element and is carried along by its
     transform rather than staying put while the first copy slides off. */
  position: relative;
  /* The whole point is that the eye follows one word out and the next one in.
     This started at 140ms, which read as a flicker, then 260ms, which Ben still
     clocked as too fast: at that speed you register that something moved rather
     than watching it move. --dur-roll is 480ms, roughly double, which is slow
     enough to read as a deliberate turn and still short enough that the label
     has settled before you decide to click. */
  transition: transform var(--dur-roll) var(--ease);
}

.roll__line {
  display: block;
}

/* The second copy, and only ever the second: pinned immediately below the
   first and stretched to the same width so it wraps into the same shape. Out
   of the flow, so it adds nothing to the height the window measures. */
.roll__line + .roll__line {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
}

/* One copy exactly, however tall a copy turned out to be. Focus gets the same
   treatment as hover throughout: a control that only responds to a mouse is a
   control that looks broken to anyone using a keyboard. */
.roll-trigger:hover .roll__inner,
.roll-trigger:focus-visible .roll__inner {
  transform: translateY(-100%);
}

/* Under reduced motion the label must not travel. base.css already collapses
   every transition to near-zero, which would make the swap instant rather than
   absent, and an instant swap of a word for the same word is invisible, which
   is the right outcome. This makes it explicit rather than relying on a
   duration of 0.01ms landing exactly on the second copy. */
@media (prefers-reduced-motion: reduce) {
  .roll-trigger:hover .roll__inner,
  .roll-trigger:focus-visible .roll__inner {
    transform: none;
  }
}

.site-nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-nav__link {
  display: flex;
  align-items: center;
  /* The containing block for the underline below. */
  position: relative;
  /* Tap targets must clear 44px. The min-height guarantees it outright rather
     than relying on padding plus an inherited line-height to add up. */
  min-height: var(--tap-min);
  padding-inline: var(--space-4);
  border-radius: var(--radius-pill);

  font-size: var(--text-sm);
  /* The roll's travel is one line box, so the line height decides it, and it
     wants to be a whole number of pixels: the label is 15px, and the site's
     1.5 would give a 22.5px line, leaving the text settled on a half pixel
     after the roll and rendering faintly soft. 1.6 makes it 24px exactly.

     This lived on .roll until project titles started using the same effect at
     a different size. Forcing 1.6 on them would have re-led every card title
     to 32px and moved the grid around, so the rule now sits with the nav,
     which is the only thing that knows the nav's font size. */
  line-height: 1.6;
  color: var(--ink-muted);
  text-decoration: none;
  white-space: nowrap;
  /* Same duration as the roll inside the label. Hovering an item is one
     gesture, so the pill fading in and the word turning over have to be one
     response: at 140ms against the roll's 480ms the background snapped into
     place and then the word caught up, which read as two separate reactions to
     the same hover. */
  transition: color var(--dur-roll) var(--ease),
              background-color var(--dur-roll) var(--ease);
}

.site-nav__link:hover {
  color: var(--ink);
  background: rgba(255, 255, 255, 0.07);
}

/* ---- The accent underline ------------------------------------------------
   One rule doing two jobs: it marks the page you are on, and it answers a
   hover. Both are the accent, so they read as the same language, and they are
   told apart by weight and by behaviour rather than by colour. The current page
   holds a full-strength line the whole time you are on that page. A hover draws
   a fainter one, and draws it: it wipes in from the left over the roll's own
   duration, so the line arriving and the word turning over are one movement.

   Deliberately a pseudo-element on the LINK rather than a text-decoration on
   the label. This is the mistake that had to be undone on the work cards: an
   underline drawn on the text rides up with the text when the roll runs, and
   ends up as a line hanging above the label with nothing beneath it. Anchored
   to the link, it stays where it is put while the word travels past it.

   Inset to match the link's own padding, so the line spans the label rather
   than the whole tap target, which is 44px tall and much wider than the word. */
.site-nav__link::after {
  content: '';
  position: absolute;
  left: var(--space-4);
  right: var(--space-4);
  bottom: 9px;
  height: 2px;
  border-radius: var(--radius-pill);
  background: var(--accent);

  opacity: 0.55;
  transform: scaleX(0);
  /* From the left, so the line is drawn in the direction the label is read. */
  transform-origin: left center;
  transition: transform var(--dur-roll) var(--ease),
              opacity var(--dur-roll) var(--ease);
}

.site-nav__link:hover::after,
.site-nav__link:focus-visible::after {
  transform: scaleX(1);
}

/* The current page: already drawn, and at full strength. Sitting at scaleX(1)
   from the first paint rather than transitioning to it, so the line is simply
   there when the page arrives instead of wiping itself in on every load. */
.site-nav__link[aria-current='page']::after {
  opacity: 1;
  transform: scaleX(1);
}

/* Not on the call to action. It is a solid cyan button, so a cyan line on top
   of it is invisible, and it does not need a second way of standing out. */
.site-nav__link--accent::after {
  content: none;
}

/* The page you are on, marked for the eye and, via aria-current in the markup,
   announced for anyone listening rather than looking.

   No background here. It used to carry the same pill as hover, one step
   brighter, which gave the bar two filled shapes at rest: the current page and
   the cyan call to action. Ben's read was that the Home bubble was competing
   with the button for the one bit of emphasis the bar has, and he is right, so
   the pill is now purely a hover response. Full-strength text against the muted
   labels is what marks the current page instead. That is a real difference, not
   colour alone in the WCAG sense, since it is the same distinction the hover
   state uses and aria-current carries it independently. */
.site-nav__link[aria-current='page'] {
  color: var(--ink);
}

/* The one call to action, and now the only filled shape in the bar at rest.
   Wider padding than the plain links so it reads as a button rather than a link
   that happens to be coloured in. Black text on cyan measures about 11:1, well
   past the AA floor. */
.site-nav__link--accent {
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: var(--weight-medium);
  padding-inline: var(--space-6);
}

/* Keeps the black label when the button is also the current page. Without this
   the [aria-current] rule above wins on specificity and turns the text on the
   cyan button white, which is the one place on the site where the accent colour
   would fail contrast. No background needed here any more: aria-current stopped
   setting one when the Home bubble came off, so nothing is competing for it. */
.site-nav__link--accent[aria-current='page'] {
  color: var(--accent-ink);
}

.site-nav__link--accent:hover {
  background: var(--accent);
  color: var(--accent-ink);
  /* Lifting brightness rather than swapping to a second accent tone keeps the
     palette at one accent value, which is the rule. */
  filter: brightness(1.12);
}

/* ---- Buttons -------------------------------------------------------------
   Two kinds and no more: the one thing you want people to do, and everything
   else. A third button style is almost always a sign the page is asking for
   too much at once. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);

  min-height: var(--tap-min);
  padding: var(--space-3) var(--space-6);
  border: 1px solid transparent;
  border-radius: var(--radius-pill);

  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              filter var(--dur-fast) var(--ease);
}

.btn--primary {
  background: var(--accent);
  color: var(--accent-ink);
}

.btn--primary:hover {
  filter: brightness(1.12);
}

.btn--secondary {
  background: transparent;
  border-color: var(--line-strong);
  color: var(--ink);
}

.btn--secondary:hover {
  border-color: var(--ink);
  background: rgba(255, 255, 255, 0.05);
}

/* ---- Section heads -------------------------------------------------------
   A heading with a link opposite it — "Selected work" and "See every project".
   Baseline-aligned rather than centre-aligned so the two sit on the same line
   of type despite being different sizes. */
.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-8);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--line);
}

.section-head__link {
  min-height: var(--tap-min);
  display: inline-flex;
  align-items: center;
  font-size: var(--text-sm);
  color: var(--ink-muted);
  text-decoration-color: var(--line-strong);
  text-underline-offset: 0.25em;
  transition: color var(--dur-fast) var(--ease),
              text-decoration-color var(--dur-fast) var(--ease);
}

.section-head__link:hover {
  color: var(--ink);
  text-decoration-color: var(--accent);
}

/* ---- The opening statement ------------------------------------------------
   Not a centred stack. The statement sits at reading width against the left
   edge of the grid, with the quieter line set below and indented on wide
   screens — the shape says "this is the important sentence" without making it
   bigger or louder. */
.intro__inner > * + * {
  margin-top: var(--space-4);
}

.intro__statement {
  /* Wider than --measure: this is the one sentence on the page that is meant to
     be read as a statement rather than as body copy, and breaking it into four
     short lines makes it look like a paragraph. */
  max-width: 46rem;
}

.intro__aside {
  max-width: var(--measure);
  color: var(--ink-faint);
}

@media (min-width: 64rem) {
  .intro__aside {
    /* Steps in to sit under the body of the statement rather than under its
       first word, which is what makes the pair read as one composition instead
       of two stacked paragraphs. */
    margin-left: var(--space-16);
  }
}

/* ---- Work grid -----------------------------------------------------------
   Six columns, and the cards take different spans in a repeating cycle — see
   SHAPES in tools/partials/work-card.js, which must agree with the classes
   below. Five identical rectangles would be a list; the varied rhythm is what
   makes it a composition.

   Mobile first: one column, every card full width. The spans only exist from
   the tablet breakpoint up, where there is room for two things side by side. */
.work-grid {
  display: grid;
  gap: var(--space-6);
}

/* The index variant on /work: a plain two-up catalogue rather than the
   homepage's composed rhythm. Same cards, different job — see INDEX_SHAPE in
   tools/partials/work-card.js. Two columns and not three, because a card
   narrower than about 300px turns a full desktop screenshot into an unreadable
   stamp. Every card here is identical, so it works from the tablet width up. */
@media (min-width: 48rem) {
  .work-grid--index {
    grid-template-columns: repeat(2, 1fr);
  }

  .work-card--index {
    grid-column: auto;
  }
}

/* The homepage's composed rhythm needs a desktop, and starts at 64rem rather
   than the 48rem the rest of the site uses.

   It was tried at 48rem and the narrowest card in the cycle — the phone shape,
   two columns of six — came out 220px wide. At that width the caption under it
   wraps onto a second line while its neighbour's stays on one, so the two
   cards in that row could not be made to line up: one of the caption and the
   frame had to sit 25px lower than the other. The composed layout only earns
   its place when there is room for it, and below 64rem there is not. One column
   of full-width screenshots is the better answer on a portrait tablet anyway.
   At 64rem the phone card is 299px, where the caption fits on one line. */
@media (min-width: 64rem) {
  /* :not(--index) is load-bearing, not tidiness. Both selectors are one class,
     so they have identical specificity and the later one simply wins — which
     silently turned the catalogue into a six-column grid whose cards each
     occupied one column, 137px wide. Naming who this applies to means the two
     layouts cannot fight over source order again. */
  .work-grid:not(.work-grid--index) {
    grid-template-columns: repeat(6, 1fr);
    gap: var(--space-6) var(--space-6);
  }

  .work-card--lead { grid-column: span 4; }
  .work-card--phone { grid-column: span 2; }
  .work-card--half { grid-column: span 3; }
  .work-card--banner { grid-column: span 6; }
}

/* ---- /work page intro ---------------------------------------------------- */

.work-intro__inner > * + * {
  margin-top: var(--space-4);
}

.work-intro__title {
  font-size: var(--text-3xl);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--ink);
}

/* ---- Work card -----------------------------------------------------------
   The whole card is one link: a bigger, more forgiving target than a link on
   the title alone, which matters most on a phone. */

/* Two cards sharing a row must end on the same line.

   The grid already stretches every cell in a row to the same height, so this
   was never a grid problem. It was that the frame inside held its exact
   aspect ratio and left the slack at the bottom of the cell — so the lead card
   (16/10 across four columns) finished 13px short of the phone card (3/4
   across two), and the captions under them sat at different heights. Thirteen
   pixels is the worst possible amount: too small to read as composition, big
   enough to look like a mistake.

   So the card fills its cell and the frame takes up whatever is left over. The
   aspect ratio still sets each frame's natural height, which is what gives the
   grid its rhythm; it just stops being the last word when a neighbour is
   slightly taller. The image is object-fit: cover already, so the extra height
   costs a few pixels of crop and nothing else.

   Only the lead-and-phone row is affected. The two halves share a ratio and a
   span, so they already matched, and on a phone every card is its own row. */
.work-card {
  display: flex;
}

.work-card__link {
  display: flex;
  flex-direction: column;
  width: 100%;
  text-decoration: none;
  color: inherit;
}

/* The screenshot's frame. Deliberately plain — a hairline and a small radius,
   no glass, no glow, no stacked shadows. Five client sites in five different
   palettes are the colour on this page; anything decorative around them turns
   the grid into noise. */
.work-card__frame {
  display: block;
  /* Grows into whatever height the row has spare — see the note above. The
     aspect-ratio below is the natural size it grows from, not a ceiling. */
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg-raised);
  /* Set per card from the shape — the ratio is what gives the grid its rhythm,
     so it travels with the markup rather than being guessed here. */
  aspect-ratio: var(--card-ratio, 16 / 10);
  transition: border-color var(--dur-base) var(--ease);
}

.work-card__shot {
  width: 100%;
  height: 100%;
  /* cover, anchored to the top: a screenshot cropped from the bottom still
     shows the site's front door, which is the part worth showing. Cropping
     from the middle would cut the header off. */
  object-fit: cover;
  object-position: top center;
  transition: transform var(--dur-base) var(--ease);
}

/* Hover and keyboard focus get the SAME treatment. A card that only responds
   to a mouse is a card that looks broken to anyone using a keyboard. */
.work-card__link:hover .work-card__frame,
.work-card__link:focus-visible .work-card__frame {
  border-color: var(--line-strong);
}

.work-card__link:hover .work-card__shot,
.work-card__link:focus-visible .work-card__shot {
  /* A small push in, not a lift out. Scaling the image inside a fixed frame
     reads as looking closer at the work; scaling the whole card would shove
     its neighbours around. */
  transform: scale(1.02);
}

.work-card__meta {
  display: block;
  margin-top: var(--space-3);
}

.work-card__name {
  font-size: var(--text-lg);
  color: var(--ink);
}

/* The title used to take an accent underline on hover. It cannot now: the
   underline is drawn on the text, the text is what slides, so the rule travels
   up with the first copy and ends up as a line floating above the title with
   nothing under it. Two hover affordances on one title was one too many in any
   case, and the roll is the more distinctive of them. The card still answers a
   hover in two other ways, the frame border lifting and the screenshot easing
   in, so nothing is lost by taking this out. */

.work-card__sector {
  margin-top: var(--space-1);
  font-size: var(--text-sm);
  color: var(--ink-faint);
}

/* ---- Services ------------------------------------------------------------
   Two columns from tablet up: the heading and its sentence hold the left, the
   list runs down the right. A single centred column of four items would be the
   default shape every generated site uses. */
.services__inner {
  display: grid;
  gap: var(--space-8);
}

@media (min-width: 64rem) {
  .services__inner {
    grid-template-columns: 2fr 3fr;
    gap: var(--space-16);
  }

  /* The closing promise spans both columns and sits below the pair. */
  .services__exclusive {
    grid-column: 1 / -1;
  }
}

.services__intro > * + * {
  margin-top: var(--space-4);
}

.services__list {
  margin: 0;
  display: grid;
  gap: var(--space-6);
}

/* A real <dl>: each service is a term and its description, which is exactly
   what this is. Screen readers announce the pairing; a stack of divs would
   leave a listener to infer it. */
.services__item {
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

.services__item dt {
  font-size: var(--text-lg);
  color: var(--ink);
}

.services__item dd {
  margin: var(--space-2) 0 0;
  max-width: var(--measure);
}

/* The promise is not a fifth service. It used to carry the same hairline as
   .services__item above, which on desktop reads as a closing note under two
   columns, and on mobile reads as item five under a sentence that says there
   are four. Same markup, opposite meaning, purely because the single column
   put it in the list's rhythm.

   So it gets a rule down the side in the accent rather than across the top in
   the line colour. The eye sorts it as a different kind of thing before it
   reads a word of it, at every width. */
.services__exclusive {
  margin-top: var(--space-12);
  padding: var(--space-1) 0 var(--space-1) var(--space-6);
  border-left: 2px solid var(--accent);
}

.services__exclusive strong {
  color: var(--ink);
  font-weight: var(--weight-medium);
}

/* ---- Common questions ----------------------------------------------------
   Two across from the tablet breakpoint. The services list directly above is a
   vertical stack, and a second vertical stack under it would make the whole
   bottom half of the page read as one long undifferentiated list. */
.faq__grid {
  display: grid;
  gap: var(--space-8);
}

@media (min-width: 48rem) {
  .faq__grid {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-12) var(--space-16);
  }
}

.faq__item {
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

/* The question is the thing being scanned, so it sits forward in full ink
   while the answer stays at reading weight. No larger, though: a question set
   big enough to compete with the section heading turns the section into a list
   of headlines. */
.faq__q {
  font-size: var(--text-lg);
  color: var(--ink);
}

.faq__a {
  margin-top: var(--space-3);
  max-width: var(--measure);
}

/* ---- Contact band --------------------------------------------------------
   The closing band on the homepage, the work index and every project page. One
   sentence, one button, and the number itself — the two ways this audience
   actually makes contact, side by side. */
.contact-band {
  margin-top: var(--section-y);
  padding-block: var(--section-y);
  border-top: 1px solid var(--line);
}

.contact-band__inner {
  display: grid;
  gap: var(--space-8);
  align-items: end;
}

@media (min-width: 64rem) {
  .contact-band__inner {
    grid-template-columns: 3fr 2fr;
    gap: var(--space-16);
  }
}

.contact-band__heading {
  font-size: var(--text-2xl);
}

.contact-band__body {
  margin-top: var(--space-4);
  max-width: var(--measure);
}

.contact-band__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4) var(--space-6);
}

/* The number reads as a number, at a size worth looking at — not as a second
   button competing with the accent one beside it. */
.contact-band__phone {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  font-size: var(--text-xl);
  color: var(--ink);
  text-decoration-color: var(--line-strong);
  text-underline-offset: 0.25em;
  white-space: nowrap;
  transition: text-decoration-color var(--dur-fast) var(--ease);
}

.contact-band__phone:hover {
  text-decoration-color: var(--accent);
}

/* ---- Footer --------------------------------------------------------------
   Carries the phone number and email on every page. The audience is trades and
   small businesses, often reading on a phone between jobs — whatever page
   someone stops on, the way to make contact is right there. */
.site-footer {
  margin-top: var(--section-y);
  padding-block: var(--space-12);
  border-top: 1px solid var(--line);
}

.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  align-items: baseline;
  justify-content: space-between;
}

.site-footer__contact {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
}

/* The phone number is the single most important link on the site for this
   audience — trades and small businesses, usually on a phone, often between
   jobs. It sits in the footer of every page and must be a comfortable tap,
   not a 26px line of text someone has to aim at. */
.site-footer__contact a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);

  color: var(--ink);
  text-decoration-color: var(--line-strong);
  text-underline-offset: 0.25em;
  transition: text-decoration-color var(--dur-fast) var(--ease);
}

.site-footer__contact a:hover {
  text-decoration-color: var(--accent);
}

.site-footer__meta {
  font-size: var(--text-sm);
  color: var(--ink-faint);
}

/* ---- Small screens -------------------------------------------------------
   The bar tightens rather than collapsing. All three items stay reachable and
   only the padding gives ground: the labels keep their size, and tap heights
   keep theirs too, held at 44px by min-height, which padding cannot undercut.
   The accent loses its wider inset here so it matches the plain links, which
   is the difference between the bar fitting 375px and crowding it. */
@media (max-width: 640px) {
  .site-nav {
    padding: var(--space-1);
  }

  .site-nav__link {
    padding-inline: var(--space-4);
  }

  .site-nav__link--accent {
    padding-inline: var(--space-4);
  }

  .site-footer__inner {
    flex-direction: column;
    gap: var(--space-4);
  }
}

/* ==========================================================================
   Project page  (/work/<slug>)
   ========================================================================== */

.project__head > .wrap > * + * {
  margin-top: var(--space-4);
}

/* The one place the pixel face carries a whole heading. It is rationed on
   purpose — see .display in base.css — and this is the moment it earns its
   keep: the project name is the title of the page and short enough to stay
   readable in a face drawn on a grid. Capped smaller than the /work heading
   because pixel letterforms at a large size start to look like a logo rather
   than a title. */
.project__name {
  font-size: clamp(1.75rem, 3vw + 0.9rem, 2.75rem);
  color: var(--ink);
}

/* Pulled tight to the heading: the screenshot IS the page's opening statement,
   so the gap above it should read as part of the same block rather than as a
   new section. */
.project__shot {
  padding-top: 0;
}

/* ---- The browser frame ---------------------------------------------------
   A bar, three dots, the domain. The plainest thing that reads as "a website".
   No gloss, no reflection, no frosted panel — the client's own palette is the
   colour here, and a decorated frame competes with what it is framing. */
.browser {
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-raised);
}

.browser__bar {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--line);
}

.browser__dots {
  display: flex;
  gap: var(--space-2);
  flex: none;
}

.browser__dots i {
  width: 10px;
  height: 10px;
  border-radius: var(--radius-pill);
  background: var(--line-strong);
}

.browser__host {
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wide);
  color: var(--ink-faint);
}

.browser__shot {
  display: block;
  width: 100%;
  height: auto;
}

/* ---- Body: words beside the phone --------------------------------------- */

.project__body-inner {
  display: grid;
  gap: var(--space-12);
}

@media (min-width: 64rem) {
  .project__body-inner {
    /* The words lead and the phone sits beside them, narrower — a phone shot
       given equal width to a paragraph reads as the more important of the
       two, which it is not. */
    grid-template-columns: 1fr 18rem;
    gap: var(--space-16);
    align-items: start;
  }
}

.project__words > * + * {
  margin-top: var(--space-6);
}

.project__tags-title {
  font-size: var(--text-xs);
  font-weight: var(--weight-regular);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--space-3);
}

.tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  list-style: none;
  padding: 0;
  margin: 0;
}

/* An outline, not a filled pill. These are quiet labels stating a fact, and a
   row of solid badges under a heading is the pill-clutter CLAUDE.md bans. */
.tag {
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  color: var(--ink-muted);
}

.project__visit {
  display: inline-flex;
}

.project__host {
  font-size: var(--text-xs);
  color: var(--ink-faint);
}

/* ---- The phone ----------------------------------------------------------- */

.phone {
  margin: 0;
  border: 1px solid var(--line);
  /* A larger radius than the browser frame, because that is the one detail
     that says "phone" without drawing a notch, a speaker and a home button
     nobody asked for. */
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--bg-raised);
}

.phone__shot {
  display: block;
  width: 100%;
  height: auto;
}

/* ---- Previous / next ----------------------------------------------------- */

.project-nav__inner {
  display: grid;
  gap: var(--space-4);
  border-top: 1px solid var(--line);
  padding-top: var(--space-8);
}

@media (min-width: 48rem) {
  .project-nav__inner {
    grid-template-columns: 1fr 1fr;
  }

  /* The next link sits at the right-hand end and reads right-aligned, so the
     pair reads as a spread rather than as two left-aligned links that happen
     to be in columns. */
  .project-nav__link--next {
    text-align: right;
  }
}

.project-nav__link {
  display: block;
  text-decoration: none;
  color: inherit;
  padding: var(--space-3) 0;
}

.project-nav__direction {
  display: block;
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-faint);
}

.project-nav__name {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--text-lg);
  color: var(--ink);
}

.project-nav__link:hover .project-nav__name {
  color: var(--accent);
}
