/* ═══════════════════════════════════════════════════════════
   SCENE 05 — THE PROJECTS
   Two carousels on paper: residential, then commercial. The same
   white-and-gold ground as the statement and approach sections, so
   the page returns to its own surface after the photograph.

   NOT PINNED. A row of cards is read at the visitor's pace and
   nothing here is a position on the scroll — the cards arrive as
   they are reached and then belong to the visitor.

   TWO LOOPS, ONE CARD. Both groups use identical markup down to the
   class names; only their content differs. Each group becomes a
   WP_Query and each card the inside of its loop, so anything true
   of one card has to be true of every card. See CONVERSION.md.
   ═══════════════════════════════════════════════════════════ */

.wk {
  position: relative;
  /* ABOVE THE TRAVELLING PICTURE, which sits at z-index 5 and would
     otherwise paint over this section for the whole viewport it
     spends rising into view — an explicit positive z-index beats
     every auto one regardless of document order. Covered as it
     rises, the picture's removal at the end is invisible; left
     underneath, it was a photograph hiding a section and then
     vanishing off it. Same lift as .fx and .tl. */
  z-index: 6;
  background: var(--paper);
  color: var(--ink);
  overflow: hidden;
  padding: 14rem 0 16rem;
}

/* The same contained washes as the sections before it — neither
   reaches an edge, so the joins above and below stay plain paper.
   See the long note on .st-stage::before in statement.css. */
.wk::before,
.wk::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.wk::before {
  background:
    radial-gradient(54% 40% at 88% 12%, rgba(217,179,120,.20) 0%, rgba(217,179,120,0) 64%),
    radial-gradient(48% 38% at 6% 82%, rgba(27,39,66,.05) 0%, rgba(27,39,66,0) 62%);
}
.wk::after {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  background-size: 200px 200px;
  opacity: .04;
}

.wk-inner { position: relative; z-index: 1; row-gap: 0; }

/* ── The head ────────────────────────────────────────────
   ACROSS THE WHOLE MEASURE, not a column with a void beside it.
   Capped at 46ch the claim used the left half of the page and the
   right half was empty paper — which on a section this wide reads
   as something missing rather than as space. The claim takes the
   left of the row and the note sits at the right of it, so the head
   spans what the cards below it span.

   CENTRED AGAINST EACH OTHER, not sat on a shared baseline. The
   two blocks are different heights — a two-line claim at 5.6rem
   beside three lines of 1.65rem — so aligning their bottoms hung
   the note off the foot of the title with all the slack above it.
   Centred, the difference is split and the two read as one row. */
.wk-head {
  grid-column: content;
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: var(--gap);
  align-items: center;
  margin-bottom: 8rem;
}

.wk-eyebrow {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  margin-bottom: 2.2rem;
  font-family: var(--font-sans);
  font-size: 1.2rem;
  font-weight: 500;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--gold-deep);
}
.wk-eyebrow i { width: 3.4rem; height: var(--hair); background: currentColor; opacity: .5; }

.wk-title {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 5.6rem;
  line-height: 1.1;
  letter-spacing: -.024em;
}
.wk-note {
  font-family: var(--font-sans);
  /* 500, a step up from the body weight it inherited. It sits at
     reduced contrast beside a 5.6rem claim, which is exactly where
     a 400 starts to look thin rather than quiet. */
  font-weight: 500;
  font-size: 1.65rem;
  line-height: 1.6;
  color: var(--on-light);
}

/* ── A group ─────────────────────────────────────────────
   The heading that names a loop, its controls, and its way out —
   ruled off from the row it introduces so the two loops read as two
   lists rather than one long one.

   NO COUNT. It said how many cards were in a row the visitor can
   already see, and stopped being true the moment the loop was
   filled from a database. */
.wk-group { grid-column: content; }
.wk-group + .wk-group { margin-top: 11rem; }

.wk-group-head {
  display: flex;
  align-items: center;
  gap: 1.6rem;
  padding-bottom: 2.4rem;
  margin-bottom: 4rem;
  border-bottom: var(--hair) solid var(--line-light);
}
.wk-group-title {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 2.8rem;
  letter-spacing: -.012em;
}

/* ── The controls ────────────────────────────────────────
   THE WAY OUT SITS WITH THE NAME; THE ARROWS SIT AT THE END.

   They are different kinds of control and the heading is where
   that gets said. The button belongs to the words — it is 'view all
   RESIDENTIAL', a continuation of the heading it follows — so it
   reads as part of the label. The arrows belong to the row of
   cards underneath, so they are ranged to the end of it, over the
   edge where the next card comes from.

   Grouped together at the end they were simply three controls in a
   corner, and which one moved the row and which one left the page
   was something you had to work out. */
.wk-nav { display: flex; gap: .8rem; margin-left: auto; }

.wk-arrow {
  width: 4.6rem;
  height: 4.6rem;
  display: grid;
  place-items: center;
  border: var(--hair) solid var(--line-light-lift);
  border-radius: 100vmax;
  background: none;
  color: var(--ink);
  cursor: pointer;
  transition:
    border-color .4s var(--ease),
    color .4s var(--ease),
    opacity .4s var(--ease);
}
.wk-arrow svg { width: 1.8rem; height: 1.8rem; display: block; }
.wk-arrow:hover { border-color: var(--gold); color: var(--gold-deep); }
/* No disabled state: the arrows wrap rather than stopping at the
   ends, because the row rotates on its own and an arrow greying out
   would contradict what the carousel is about to do a moment later.
   See nudge() in js/sections/work.js. */

.wk-all { --h: 4.6rem; padding: 0 2rem; font-size: 1.15rem; }

/* ── The track ───────────────────────────────────────────
   A REAL HORIZONTAL SCROLLER, not a transformed strip. Native
   scrolling means a touch swipe works with no code at all, the
   keyboard reaches every card, and the arrows only have to nudge a
   scroll position. A transform would have to reimplement momentum,
   snapping and bounds — and would still not be swipeable without
   writing all of that again. */
.wk-track {
  display: flex;
  gap: var(--gap);
  overflow-x: auto;
  overflow-y: visible;
  scroll-snap-type: x mandatory;
  /* ROOM FOR THE SHADOW TO FINISH IN. A scroller clips, and the
     card's hover shadow reached further below it than this padding
     allowed — so the soft falloff was cut off square and what
     showed was a hard-edged grey rectangle under the card. A
     shadow that is clipped does not read as a small shadow; it
     reads as a box.

     NO SIDE PADDING AT ALL, so the row is cut exactly on the
     section's grid line and the card after the last visible one
     does not peek into the margin. It briefly had 5rem of side
     padding to let the hover shadow finish — and that padding is
     scrollport, so a sliver of the next card showed in it.

     The shadow no longer needs it: it is drawn under the card
     rather than around it (see .wk-card::after), so its whole
     extent is within the card's own width and only the vertical
     room below is required. */
  /* ONE PIXEL EACH SIDE, and it is not decoration. A card's outer
     edge lands exactly on the scrollport's clip boundary — the row
     is sized to fill the track — so its 1px border straddles that
     boundary and a fractional scroll position cuts whichever side
     falls on the wrong half of a device pixel. Measured at 520px:
     a 446.14px card in a 446px scrollport. That is the border
     appearing and disappearing on one side and then the other.

     A pixel of clearance puts the whole border inside the clip. It
     cannot show the neighbouring card, which is a full gap away. */
  padding: 2rem 2px 5.5rem;
  margin: -2rem -2px -5.5rem;
  /* AND THE SNAPPORT HAS TO BE INSET TOO. Padding alone does not
     help: scroll-snap-align:start aligns the card's edge with the
     snapport's edge EXACTLY, and the snapport is the padding box —
     so the row simply rested one pixel further along and put the
     border back on the clip boundary. scroll-padding moves the line
     the snap aligns to, which is the only way to land the card
     inside its own scroller. */
  scroll-padding-inline: 2px;
  /* The scrollbar is furniture the arrows already replace. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.wk-track::-webkit-scrollbar { display: none; }

/* ── A card ──────────────────────────────────────────────
   A BOUNDED OBJECT. The words used to sit under the picture on open
   paper, so a card read as a photograph with some text loose
   beneath it rather than as one thing. The border draws the edge;
   the picture is flush inside it and the words are inset, which is
   what says the two belong to each other. */
.wk-card {
  position: relative;
  flex: 0 0 calc((100% - 2 * var(--gap)) / 3);
  scroll-snap-align: start;
  display: flex;
}

/* ── The shadow ──────────────────────────────────────────
   UNDER THE CARD, NOT AROUND IT, and it stays on the ground while
   the card lifts off it.

   A box-shadow spreads sideways as far as it spreads down, and the
   track is a scroller — so the sides were either clipped square or
   the padding that stopped them being clipped let the next card
   peek through. Drawn as a soft ellipse beneath the card, the whole
   shadow lives inside the card's own width and only needs room
   below.

   It also belongs to the LI rather than to the link, so the card's
   hover transform does not carry it: the card rises and its shadow
   stays put, which is what a lift looks like. Moving both together
   is what makes a hover read as the whole thing sliding. */
.wk-card::after {
  content: '';
  position: absolute;
  /* FULL WIDTH, AND FADING TO NOTHING AT ITS OWN EDGES.

     The first version inset the box 5% a side and stopped the
     gradient at 78% of its radius — so the shadow covered the
     middle third of the card and nothing else, which is exactly
     what a smudge under the centre looks like.

     The ellipse now spans the whole card: a horizontal radius of
     50% reaches the box's own left and right edges, and the last
     stop is at 100% so it arrives there already transparent. No
     inset, no cut — it simply runs out where the card does. */
  left: 0;
  right: 0;
  bottom: -1.8rem;
  height: 3.6rem;
  z-index: -1;
  background: radial-gradient(50% 100% at 50% 0%,
    rgba(19, 25, 38, .22) 0%,
    rgba(19, 25, 38, .13) 38%,
    rgba(19, 25, 38, .05) 66%,
    rgba(19, 25, 38, 0) 100%);
  opacity: 0;
  pointer-events: none;
  transition: opacity .6s var(--ease);
}

/* The visible card: the border, the ground it paints on, and the
   thing that moves. */
.wk-link {
  display: flex;
  flex-direction: column;
  width: 100%;
  color: inherit;
  border: var(--hair) solid var(--line-light);
  background: var(--paper);
  overflow: hidden;
  transition:
    border-color .55s var(--ease),
    transform .65s var(--ease-arch);
}
.wk-link:focus-visible { outline: 2px solid var(--gold); outline-offset: .4rem; }

/* ── The picture ─────────────────────────────────────────
   The clip is on the FRAME and the scale on the picture inside it,
   never both on one element: the frame has to stay a fixed shape
   while what fills it moves, which is the difference between a
   photograph behind a window and a photograph being stretched. */
.wk-media {
  position: relative;
  display: block;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: var(--ink);
}
.wk-media img {
  width: 100%;
  height: 100%;
  /* max-width:none — the reset caps every image at 100%, which is
     not what a cover-fitted picture in a resizing box wants. */
  max-width: none;
  object-fit: cover;
  display: block;
  transition: transform 1.1s var(--ease-arch);
}

/* The type of project, on the picture. Small, and the only thing
   allowed to sit over the photograph. */
.wk-tag {
  position: absolute;
  left: 1.6rem;
  bottom: 1.6rem;
  z-index: 1;
  padding: .6rem 1.2rem;
  background: rgba(19, 25, 38, .62);
  color: var(--paper);
  font-family: var(--font-sans);
  font-size: 1.1rem;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
}

/* ── The card's words ────────────────────────────────────── */
.wk-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  flex: 1;
  padding: 2.8rem 2.6rem 3rem;
}
.wk-name {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 2.4rem;
  line-height: 1.15;
  letter-spacing: -.014em;
  margin-bottom: .6rem;
}
.wk-place {
  display: block;
  margin-bottom: 1.4rem;
  font-family: var(--font-sans);
  font-size: 1.25rem;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--gold-deep);
}
.wk-desc {
  font-family: var(--font-sans);
  font-size: 1.5rem;
  line-height: 1.6;
  color: var(--on-light);
  margin-bottom: 2.4rem;
}

/* ── Read more ───────────────────────────────────────────
   Pushed to the foot of the card, so a row of them lines up
   whatever length each description runs to — they are real copy,
   not a fixed field.

   A rule that draws itself across on hover with the arrow riding
   the end of it. One transition on each of two properties, so a
   pointer swept quickly across a row reverses cleanly instead of
   leaving half-finished sequences behind it — the lesson the menu's
   per-letter roll taught. */
.wk-more {
  position: relative;
  margin-top: auto;
  display: inline-flex;
  align-items: center;
  gap: 1rem;
  padding-bottom: .9rem;
  font-family: var(--font-sans);
  font-size: 1.25rem;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink);
  transition: color .45s var(--ease);
}
.wk-more::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--hair);
  background: var(--gold);
  /* REVEALED BY A CLIP, NOT GROWN BY A SCALE.

     scaleX puts the rule on a composited layer for the length of
     the transition, and a one-pixel line on a composited layer is
     resampled — it paints soft while it travels and then crisp the
     instant the transform lands, which is why it appeared to gain
     weight at the end.

     A clip never touches the pixels. The line is drawn at its true
     height the whole time and simply uncovered, so what arrives is
     exactly what was already there. */
  clip-path: inset(0 100% 0 0);
  transition: clip-path .55s var(--ease-arch);
}
.wk-more svg {
  width: 1.5rem;
  height: 1.5rem;
  display: block;
  transition: transform .55s var(--ease-arch);
}

/* The card answers as a whole — the border warms, it lifts off the
   paper, the photograph pushes in behind its frame and the rule
   under the link draws. One gesture with four parts, all on the
   card's own hover so they cannot get out of step.

   THE ZOOM IS INSIDE A FIXED FRAME. .wk-media does not move or
   change size; only the picture within it does, and the frame's
   overflow crops the difference. That is what keeps the lift
   readable as a lift — the card's edges travel, its contents do
   not grow with them. */
@media (hover: hover) {
  .wk-card:hover .wk-link {
    border-color: rgba(217, 179, 120, .6);
    transform: translateY(-.6rem);
  }
  .wk-card:hover::after { opacity: 1; }
  .wk-card:hover .wk-media img { transform: scale(1.06); }
  .wk-card:hover .wk-more { color: var(--gold-deep); }
  .wk-card:hover .wk-more::after { clip-path: inset(0 0 0 0); }
  .wk-card:hover .wk-more svg { transform: translateX(.5rem); }
}

/* ── The pre-JS state ────────────────────────────────────
   Guarded twice over: by .has-gsap, so a browser without it gets a
   plain complete row rather than a blank one — and by :not(.is-in),
   so it stops applying the moment a card is handed to the script.

   THE SECOND GUARD IS NOT OPTIONAL. The entrance ends with
   clearProps, which removes GSAP's inline value — and without
   .is-in the card falls straight back onto THIS rule and disappears
   at the end of its own arrival. A resting state written here and a
   resting state written by the script are the same state; only one
   of them can be the one that survives. */
.has-gsap .wk-head:not(.is-in) .wk-eyebrow,
.has-gsap .wk-head:not(.is-in) .wk-title,
.has-gsap .wk-head:not(.is-in) .wk-note { opacity: 0; }
.has-gsap .wk-card:not(.is-in) { opacity: 0; }
.has-gsap .wk-card:not(.is-in) .wk-media img { clip-path: inset(100% 0 0 0); }

/* ── Responsive ──────────────────────────────────────────── */
@media (max-width: 1200px) {
  .wk-title { font-size: 4.6rem; }
  .wk-card { flex-basis: calc((100% - var(--gap)) / 2); }
}
@media (max-width: 991px) {
  .wk { padding: 8.5rem 0 9rem; }
  /* The claim and its note stack — side by side at this width
     neither is wide enough to hold a line.

     TIGHTER THAN THE WIDE LAYOUT'S SPACING, not the same numbers on
     a smaller canvas. Space that reads as composure across a
     sixteen-hundred-pixel row reads as a gap on a phone, because
     the eye takes in the whole column at once and the emptiness is
     the only thing between one line and the next. */
  .wk-head { grid-template-columns: 1fr; gap: 2rem; margin-bottom: 5rem; }
  .wk-eyebrow { margin-bottom: 1.6rem; }
  .wk-title { font-size: 4rem; }
  .wk-group + .wk-group { margin-top: 7rem; }
  /* WHOLE CARDS ONLY. This used to leave a sliver of the next one
     showing, as a hint that the row continued — but a card cut off
     mid-picture reads as a layout that has overflowed, not as an
     invitation. The arrows say the same thing without looking like
     a mistake. */
  .wk-card { flex-basis: calc((100% - var(--gap)) / 2); }
}
@media (max-width: 767px) {
  .wk { padding: 6rem 0 6.5rem; }
  .wk-head { gap: 1.6rem; margin-bottom: 3.6rem; }
  .wk-eyebrow { margin-bottom: 1.2rem; }
  .wk-title { font-size: 2.8rem; }
  .wk-note { font-size: 1.5rem; }

  /* THE HEADING WRAPS RATHER THAN CRUSHING. The title takes its own
     line and the controls sit under it — three things on one line
     at this width leaves the button too narrow to read. */
  .wk-group-head { flex-wrap: wrap; gap: 1.6rem; margin-bottom: 3rem; }
  .wk-group-title { font-size: 2.2rem; flex: 1 0 100%; }

  /* ONE AT A TIME, ENTIRE. At 86% the next card started inside the
     screen, so a phone showed one card and the left-hand slice of
     another — which looks like something has run over the edge
     rather than like there is more to come. */
  .wk-card { flex-basis: 100%; }
  .wk-body { padding: 2.2rem 2rem 2.4rem; }
  .wk-name { font-size: 2.1rem; }
  .wk-arrow { width: 4.2rem; height: 4.2rem; }
}
