/* ==========================================================================
   Project case-study pages

   The layout is the editorial structure from the old site — masthead, lead
   with a standfirst beside a hero figure, then blocks of "heading rail left,
   content right", closing on a prev/next footer. What changes here is every
   value it was built from: that page had its own paper/ink/accent palette, its
   own Helvetica stack and its own px scale. All three are replaced by the
   tokens the rest of this site already runs on, so a project page inherits the
   theme switcher, the cream ground, General Sans, and the 1rem = 10px scale
   without knowing any of them exist.

   No grid is declared here beyond what these pages need. The 16-column grid in
   base.css is for the home page's full-bleed sections; a reading column wants
   a measure, not sixteen columns.
   ========================================================================== */

/* Home is a scroll experience with a fixed pill header; a case study is a
   document. It gets a normal page flow and a masthead that scrolls away. */
.project {
  padding-bottom: 0;
}

.project__wrap {
  width: 100%;
  max-width: 118rem;
  margin-inline: auto;
  padding-inline: var(--base-padding);
}

/* --- Masthead ------------------------------------------------------------
   Deliberately not the home page's floating pill nav. That header is tied to
   the intro overlay, the fullscreen menu and the scroll-driven theme flips —
   none of which exist here. A rule and two labels is the whole thing. */

.project__masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  padding: 2.4rem 0 2rem;
  border-bottom: 1px solid var(--rule);
}

.project__back {
  display: inline-flex;
  align-items: center;
  gap: 0.8rem;
  transition: color .3s var(--ease-snappy);
}
.project__back:hover { color: var(--color-accent); }
.project__back-arrow { transition: var(--link-transition); }
.project__back:hover .project__back-arrow { transform: translateX(-0.4rem); }

.project__who { opacity: .55; }

/* --- Lead ---------------------------------------------------------------- */

.project__lead {
  padding: clamp(6rem, 9vw, 12rem) 0 clamp(5rem, 6vw, 8rem);
}

/* The title runs at the display scale but wants a longer measure than a
   hero headline, so it takes its own size rather than .t-h1. */
.project__title {
  font-family: var(--font-display);
  font-weight: 550;
  font-size: clamp(4rem, 7vw, 9.6rem);
  line-height: 0.97;
  letter-spacing: -0.035em;
  max-width: 16ch;
}
/* The second clause of the title, set back so the first clause carries. */
.project__title .soft { opacity: .45; }

.project__lead-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(3.2rem, 5vw, 6.4rem);
  align-items: start;
  margin-top: clamp(3.2rem, 4vw, 4.8rem);
}

.project__standfirst {
  font-size: clamp(1.8rem, 1.6vw, 2.1rem);
  line-height: 1.5;
  letter-spacing: -0.015em;
  opacity: .82;
  max-width: 52ch;
}

/* --- Blocks --------------------------------------------------------------
   Heading rail on the left, content on the right. The rule on top of each
   block is what gives the page its editorial rhythm. */

.project__block {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(2.4rem, 4vw, 6.4rem);
  padding: clamp(6rem, 8vw, 10.4rem) 0;
  border-top: 1px solid var(--rule);
}

.project__block-head { min-width: 0; }
.project__block-body { min-width: 0; }
.project__block-body > :first-child { margin-top: 0; }

.project__label {
  display: flex;
  align-items: center;
  gap: 1.3rem;
  font-size: 1.2rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-bottom: 2.4rem;
}
.project__label::before {
  content: "";
  width: 2.6rem;
  height: 1px;
  background: currentColor;
  flex: none;
}

.project__block h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2.8rem, 3.4vw, 4.4rem);
  line-height: 1.03;
  letter-spacing: -0.03em;
  max-width: 15ch;
}

.project__block p {
  font-size: 1.7rem;
  line-height: 1.62;
  letter-spacing: -0.015em;
  opacity: .82;
  max-width: 66ch;
  margin-top: 2.2rem;
}
.project__block p strong { font-weight: 550; opacity: 1; }

/* Opens a block at a larger size — a thesis sentence, not body copy. */
.project__lead-in {
  font-family: var(--font-display);
  font-size: clamp(2rem, 2vw, 2.6rem) !important;
  font-weight: 500;
  line-height: 1.34 !important;
  letter-spacing: -0.025em;
  opacity: 1 !important;
  max-width: 30ch !important;
  margin-top: 2.6rem !important;
}

/* --- Figures ------------------------------------------------------------- */

.project__fig { margin-top: clamp(4rem, 5vw, 6.4rem); }

.project__fig img {
  width: 100%;
  border-radius: var(--radius-card);
  /* An inset ring rather than a border: these are pale screenshots on a pale
     ground, and a 1px outline is what stops them dissolving into the page. */
  box-shadow: inset 0 0 0 1px var(--rule);
}

.project__fig figcaption {
  margin-top: 1.4rem;
  font-size: 1.3rem;
  line-height: 1.55;
  opacity: .55;
  max-width: 64ch;
}

/* For a figure that opens a block, where the block's own padding already
   supplies the space the default top margin would add. */
.project__fig--flush { margin-top: 0; }

/* --- Step sequence -------------------------------------------------------
   A process shown as steps rather than as a series of full-width plates.
   Stacked, four screenshots cost about four screens of scroll and the reader
   loses the thread between them; paired, the whole sequence is legible at
   once and reads as one progression. The figures keep their own captions, so
   the numbering stays implicit in the reading order rather than being drawn. */
.project__seq {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.6rem, 2.5vw, 2.8rem);
  margin-top: clamp(3.2rem, 4vw, 4.8rem);
}
/* The grid owns the spacing; the children must not add their own. */
.project__seq .project__fig { margin-top: 0; }
.project__seq figcaption { margin-top: 1rem; font-size: 1.2rem; }

@media (max-width: 700px) {
  .project__seq { grid-template-columns: minmax(0, 1fr); }
}

.project__hero-fig { margin-top: 0; }
.project__hero-fig img { border-radius: var(--radius-media); }

/* --- Formula / code ------------------------------------------------------ */

.project__formula {
  display: block;
  margin-top: 3.4rem;
  padding: 2.2rem 2.6rem;
  border-left: 2px solid var(--color-accent);
  border-radius: 0 var(--radius-card) var(--radius-card) 0;
  background: var(--sunk);
  font-family: var(--font-mono);
  font-size: clamp(1.3rem, 1.4vw, 1.6rem);
  line-height: 1.5;
  overflow-x: auto;
}

.project__code {
  margin-top: 3.4rem;
  border-radius: var(--radius-card);
  background: var(--sunk);
  box-shadow: inset 0 0 0 1px var(--rule);
  overflow: hidden;
}

.project__code-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.6rem;
  padding: 1.2rem 1.8rem;
  border-bottom: 1px solid var(--rule);
}
.project__code-bar .fn { font-family: var(--font-mono); font-size: 1.25rem; }
.project__code-bar .tag {
  font-size: 1.1rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: .5;
}

.project__code-body {
  margin: 0;
  padding: 2.2rem 2.4rem;
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: 1.3rem;
  line-height: 1.78;
  opacity: .85;
  tab-size: 4;
}
/* Syntax tints derive from the theme accent so the sample recolours with the
   rest of the page instead of carrying a fixed editor palette. */
.project__code-body .c { opacity: .45; font-style: italic; }
.project__code-body .k { color: var(--color-accent); }
.project__code-body .f { color: color-mix(in srgb, var(--color-accent) 70%, var(--fg)); }
.project__code-body .s { color: color-mix(in srgb, var(--color-accent) 40%, var(--fg)); opacity: .9; }
.project__code-body .n { opacity: .7; }

/* --- Metrics ------------------------------------------------------------- */

.project__data {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(3rem, 6vw, 8rem);
  align-items: flex-end;
  margin-top: 4rem;
}

.project__metric .v {
  font-family: var(--font-display);
  font-size: clamp(5.6rem, 9vw, 11rem);
  font-weight: 550;
  letter-spacing: -0.04em;
  line-height: 0.9;
}
.project__metric .v span { font-size: .5em; }
.project__metric--accent .v { color: var(--color-accent); }

.project__metric .l {
  margin-top: 1.6rem;
  font-size: 1.2rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  opacity: .55;
}

/* --- Spec table ---------------------------------------------------------- */

.project__spec {
  margin-top: 4rem;
  width: 100%;
  max-width: 72rem;
  border-collapse: collapse;
}
.project__spec tr { border-top: 1px solid var(--rule); }
.project__spec tr:last-child { border-bottom: 1px solid var(--rule); }
.project__spec th {
  text-align: left;
  font-weight: 400;
  font-size: 1.5rem;
  padding: 1.5rem 0;
}
.project__spec td {
  text-align: right;
  font-family: var(--font-mono);
  font-size: 1.4rem;
  opacity: .7;
  padding: 1.5rem 0;
}

.project__note {
  margin-top: 3.4rem !important;
  font-size: 1.25rem !important;
  line-height: 1.6 !important;
  opacity: .5 !important;
  max-width: 68ch !important;
}

/* --- Footer -------------------------------------------------------------- */

.project__foot {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2.4rem;
  padding: clamp(5.6rem, 8vw, 9.6rem) 0 6rem;
  border-top: 1px solid var(--rule);
}

.project__foot-back { opacity: .55; transition: opacity .3s var(--ease-snappy); }
.project__foot-back:hover { opacity: 1; }

.project__next { text-align: right; }
.project__next .lbl { margin-bottom: 1rem; }
.project__next a {
  font-family: var(--font-display);
  font-size: clamp(2rem, 3vw, 3.4rem);
  font-weight: 500;
  letter-spacing: -0.025em;
  transition: color .3s var(--ease-snappy);
}
.project__next a:hover { color: var(--color-accent); }

/* ==========================================================================
   Gallery

   For the miscellaneous page, where the work is the picture rather than a
   written case. Nothing is cropped: these are photographs of real objects,
   and a fixed crop was cutting the top off a press and the ends off a car.

   Masonry via CSS multi-column rather than grid. Grid cannot do masonry
   without either forcing a uniform row height (which crops) or leaving ragged
   holes under the short tiles; columns lets every image keep its own
   proportions and simply flow. The trade is reading order — columns fill
   top-to-bottom before moving right — which costs nothing here, because these
   are independent images rather than a sequence.
   ========================================================================== */

.gallery {
  --gallery-gap: clamp(1.6rem, 2.5vw, 3.2rem);
  columns: 3;
  column-gap: var(--gallery-gap);
  padding-bottom: clamp(6rem, 8vw, 10rem);
}

/* A button, not a bare image: it opens the lightbox, so it must be reachable
   by keyboard and announce itself as activatable. The tile IS the button —
   with no caption there is nothing for a <figure> wrapper to caption. */
.gallery__shot {
  display: block;
  width: 100%;
  /* Column layout has no row gap, so the vertical rhythm is this margin. */
  margin: 0 0 var(--gallery-gap);
  /* Without this a tall image splits across a column boundary. */
  break-inside: avoid;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--sunk);
  box-shadow: inset 0 0 0 1px var(--rule);
  cursor: zoom-in;
}

/* height:auto, no object-fit — the image sets its own height from its own
   aspect ratio, which is the whole point of not cropping. */
.gallery__shot img {
  width: 100%;
  height: auto;
  transition: transform .6s var(--ease-snappy), opacity .4s var(--ease-snappy);
}
.gallery__shot:hover img { transform: scale(1.03); }

/* --- Lightbox ------------------------------------------------------------
   Kept out of the document flow and inert until opened, so it never traps
   focus or shows up in the tab order while closed. */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(2rem, 5vw, 6rem);
  background: color-mix(in srgb, var(--color-black) 92%, transparent);
  opacity: 0;
  visibility: hidden;
  transition: opacity .4s var(--ease-snappy), visibility 0s linear .4s;
}
.lightbox.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity .4s var(--ease-snappy), visibility 0s;
}

.lightbox__img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: var(--radius-card);
}

.lightbox__close {
  position: absolute;
  top: clamp(1.6rem, 3vw, 3rem);
  right: clamp(1.6rem, 3vw, 3rem);
  width: 4.4rem;
  height: 4.4rem;
  border-radius: 50%;
  color: var(--color-white);
  font-size: 2rem;
  line-height: 1;
  display: grid;
  place-items: center;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-white) 35%, transparent);
  transition: background .3s var(--ease-snappy);
}
.lightbox__close:hover { background: color-mix(in srgb, var(--color-white) 15%, transparent); }

@media (max-width: 1100px) { .gallery { columns: 2; } }
@media (max-width: 760px)  { .gallery { columns: 1; } }

/* Sits under the prev/next block, which already carries the closing rule. */
.project__colophon {
  padding-bottom: 6rem;
  opacity: .45;
}

/* --- Reveal --------------------------------------------------------------
   Hidden only once JS has confirmed it is running (html.js), so a failed
   script load leaves a fully readable document rather than a blank one. */

@media (prefers-reduced-motion: no-preference) {
  .js .project__reveal {
    opacity: 0;
    transform: translateY(1.6rem);
    transition: opacity .7s var(--ease-out-expo), transform .7s var(--ease-snappy);
  }
  .js .project__reveal.is-in {
    opacity: 1;
    transform: none;
  }
}

/* --- Responsive ---------------------------------------------------------- */

@media (max-width: 900px) {
  .project__lead-grid { grid-template-columns: minmax(0, 1fr); }
  .project__block { grid-template-columns: minmax(0, 1fr); }
  .project__block-body { margin-top: 2.6rem; }
  .project__title { max-width: none; }
}

@media (max-width: 560px) {
  .project__data { gap: 3.4rem; }
  .project__foot { flex-direction: column; align-items: flex-start; gap: 3.2rem; }
  .project__next { text-align: left; }
}

/* ==========================================================================
   Playable strings — project-telecaster.html
   ==========================================================================
   The overlay sits on the render itself rather than beside it: the point is
   that the guitar in the drawing is the thing you play, not a widget that
   happens to be nearby.
   ========================================================================== */

/* The image gets its own positioned box so the string overlay can match it
   exactly. Anchoring to the <figure> instead includes the figcaption's height
   and stretches the overlay. */
.guitar__stage { position: relative; display: block; line-height: 0; }
.guitar__stage img { display: block; width: 100%; height: auto; }

.guitar__strings {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  /* pan-y, NOT none. Blocking touch entirely would trap a phone user on the
     hero image with no way to scroll past it — a fun feature is not worth
     breaking the page. Taps still pluck; vertical scrolling still works. */
  touch-action: pan-y;
}

/* The visible string must never swallow the pointer — elementFromPoint would
   return it instead of the hit zone underneath, and nothing would ever play. */
.guitar__string {
  pointer-events: none;
  stroke: rgba(255, 255, 255, 0.0);
  transition: stroke .25s var(--ease-snappy);
  vector-effect: non-scaling-stroke;
}
.guitar__hit {
  stroke: transparent;
  stroke-width: 16;            /* a real target, including on a phone */
  cursor: pointer;
  vector-effect: non-scaling-stroke;
}

/* At rest the overlay is invisible. Painting all six permanently laid a milky
   band over the fretboard and drew attention to every pixel of misalignment
   between the overlay and the render's own geometry. Lighting up only the
   string under the pointer reads as correct — and is the affordance anyway. */
.guitar__hit:hover + .guitar__string {
  stroke: rgba(255, 255, 255, 0.92);
}
/* A single faint cue that the render is interactive, on hover of the figure. */
.project__fig[data-guitar]:hover .guitar__string {
  stroke: rgba(255, 255, 255, 0.16);
}
.project__fig[data-guitar]:hover .guitar__hit:hover + .guitar__string {
  stroke: rgba(255, 255, 255, 0.92);
}

@keyframes guitar-pluck {
  0%   { transform: translateY(0); }
  22%  { transform: translateY(-2.2px); }
  45%  { transform: translateY(1.6px); }
  68%  { transform: translateY(-0.9px); }
  85%  { transform: translateY(0.4px); }
  100% { transform: translateY(0); }
}
.guitar__string.is-plucked {
  animation: guitar-pluck .42s var(--ease-snappy);
  stroke: #fff;
}

/* --- the panel under the render ------------------------------------------ */

.guitar__panel {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.2rem 2rem;
  margin-top: 1.2rem;
}
.guitar__hint { color: var(--muted); margin: 0; }
.guitar__keys { display: flex; gap: .6rem; }
.guitar__key {
  /* 4.4rem, not 3.6: the root font size is 10px, so 3.6rem rendered as a
     36px button — under the 44px minimum touch target and measurably so. */
  min-width: 4.4rem;
  min-height: 4.4rem;
  border: 1px solid var(--rule);
  border-radius: 0.6rem;
  background: transparent;
  color: var(--fg);
  font: inherit;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background .2s var(--ease-snappy), color .2s var(--ease-snappy);
}
.guitar__key:hover { background: var(--fg); color: var(--bg); }
.guitar__key:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .guitar__string.is-plucked { animation: none; }
}
