/* Projects, round 3 (R3-B, jury round 1 F12). Linked after site.next.css; the lead may fold it into site.css at the switch.
   Pinned (desktop, main.js sets .is-pinned): the intro holds still in columns 1-4 while the cards travel in from the right
   and pass under a soft 40 px mist at column 5. main.js still moves .track and measures travel as track.scrollWidth - clientWidth;
   .proj-row is the still window around it that carries the mist mask (a mask on the moving track would travel with it).
   Every card opens on a window into the same painted world (assets/img/detail-*), with its line drawing laid over in fine ivory.
   Not pinned (phones, reduced motion, no JS, zoom): the intro sits above the cards; phones keep the swipe row, wider screens get two columns. */

.projects {
  --intro-w: max(calc((100% - 2 * var(--margin)) / 3), 300px); /* columns 1-4 of 12 */
  --mist-at: calc(var(--margin) + var(--intro-w));               /* column 5: where the cards go under the mist */
}

/* ---------- Pinned: a still intro, a moving row ---------- */
.motion .projects.is-pinned .pin { display: grid; grid-template: auto / 100%; align-content: safe center; }
.motion .projects.is-pinned .pin > * { grid-area: 1 / 1; align-self: start; min-width: 0; }
.motion .projects.is-pinned .proj-intro { z-index: 1; width: var(--intro-w); margin-left: var(--margin); padding-right: clamp(16px, 2.6vw, 44px); }
.motion .projects.is-pinned .proj-row {
  -webkit-mask-image: linear-gradient(90deg, transparent var(--mist-at), #000 calc(var(--mist-at) + 40px));
  mask-image: linear-gradient(90deg, transparent var(--mist-at), #000 calc(var(--mist-at) + 40px));
}
/* The mist itself: a soft column of the intro's own haze over the mask's edge, the height of the cards */
.motion .projects.is-pinned .proj-intro::after { content: ""; position: absolute; z-index: -1; pointer-events: none;
  top: -28px; left: calc(100% - 60px); width: 108px; height: calc(min(68svh, 620px) + 56px);
  /* zero at both sides and eased, so the haze has no edge of its own; peak over the mask's ramp (100% .. 100% + 40px) */
  background: linear-gradient(90deg, rgba(244, 240, 232, 0), rgba(244, 240, 232, .1) 20%, rgba(244, 240, 232, .3) 40%, rgba(244, 240, 232, .36) 52%, rgba(244, 240, 232, .22) 66%, rgba(244, 240, 232, .07) 82%, rgba(244, 240, 232, 0));
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 16%, #000 82%, transparent); mask-image: linear-gradient(180deg, transparent, #000 16%, #000 82%, transparent); }
/* The first card rests just clear of the mist; the row ends one margin from the right edge (the day ruler is hidden here) */
.motion .projects.is-pinned .track { padding-left: calc(var(--mist-at) + 40px + clamp(8px, 1vw, 16px)); padding-right: var(--margin); }

/* ---------- Not pinned: intro above the cards ---------- */
.projects:not(.is-pinned) .pin { display: block; }
.projects:not(.is-pinned) .proj-intro { width: auto; max-width: 36em; margin: 0 var(--margin) clamp(36px, 6svh, 56px); padding-right: 0; }
.projects:not(.is-pinned) .proj:first-child { scroll-snap-align: start; }
@media (min-width: 760px) {
  .projects:not(.is-pinned) .track { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: calc(1000px + 2 * var(--margin)); overflow: visible; scroll-snap-type: none; padding: 0 var(--margin); }
  .projects:not(.is-pinned) .proj { width: auto; min-height: 0; }
}
/* Phones: the cards stack one under the other (the owner does not want a sideways swipe) */
@media (max-width: 759px) {
  .projects:not(.is-pinned) .track { display: grid; gap: 20px; overflow: visible; scroll-snap-type: none; padding: 0 var(--margin); }
  .projects:not(.is-pinned) .proj { width: auto; min-height: 0; }
  .projects:not(.is-pinned) .proj-window { aspect-ratio: 16 / 9; } /* a full-width card would make the pane 40 px taller than in the row */
}

/* ---------- The card: solid enough under the text that no ridge line shows through (jury round 2). The mist mask makes
   .proj-row a backdrop root, so while pinned the blur has nothing to blur and the ivory alone must carry the text. ---------- */
.proj { background: rgba(244, 240, 232, .9); -webkit-backdrop-filter: blur(28px) saturate(1.2); backdrop-filter: blur(28px) saturate(1.2); }

/* Titles in Newsreader on every screen, as on the phone (jury 4, fix 8); the phone keeps its size */
.proj .proj-name { font: 400 clamp(24px, 2vw, 30px)/1.1 var(--serif); text-transform: none; letter-spacing: -.01em; }
@media (max-width: 759px) { .proj .proj-name { font-size: clamp(24px, 6.6vw, 30px); } }

/* ---------- The window: a painted detail, inset like a pane in the glass, with the drawing over it ---------- */
.proj-window { order: -2; position: relative; isolation: isolate; margin: -16px -16px 8px; aspect-ratio: 320 / 220; border-radius: 10px; overflow: hidden; background: #A9B5AD; }
.proj-window picture { position: absolute; inset: 0; display: block; }
.proj-window img { width: 100%; height: 100%; object-fit: cover; scale: 1.08; transform-origin: 50% 60%; }
/* A breath of evening at the foot of the pane, and a hairline edge, so the ivory lines read on light and dark paint alike */
.proj-window::after { content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: linear-gradient(180deg, rgba(20, 35, 33, .14), rgba(20, 35, 33, .06) 40%, rgba(20, 35, 33, .34));
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .28); }
.proj-window .proj-art { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; padding: 0; order: 0;
  stroke: var(--ivory); stroke-width: 1.5; filter: drop-shadow(0 1px 1.5px rgba(20, 35, 33, .42)); }
.proj-window .proj-art .hit { stroke: var(--lowsun); stroke-width: 2.2; }
.proj-window .proj-art .dot { fill: var(--ivory); stroke: none; }
.proj-window .proj-art .hit.dot { fill: var(--lowsun); }
.proj-window .proj-art .traveller { fill: var(--lowsun); }

/* Entry: the card wipes open (site.next.css), and the painting settles into its pane behind it.
   The closed wipe keeps a 1 px strip: a fully clipped card has an empty box, and Chrome then holds its lazy image back
   until the wipe opens (measured: cards 3 and 4 unloaded at scene start), so the paint would pop in late. */
.motion:not(.mat-paper) .proj[data-reveal] { clip-path: inset(0 0 calc(100% - 1px) 0 round var(--radius)); }
/* ...and that strip (the top border row) paints nothing until the card opens */
.motion:not(.mat-paper) .proj[data-reveal]:not(.is-in):not(:focus-within) { border-top-color: transparent; background-clip: padding-box; }
.motion:not(.mat-paper) .proj[data-reveal] .proj-window img { scale: 1.2; transition: scale 1.8s var(--ease-out) .12s; }
.motion:not(.mat-paper) .proj[data-reveal]:is(.is-in, :focus-within) .proj-window img { scale: 1.08; }
@media (hover: hover) and (pointer: fine) {
  .motion:not(.is-still):not(.mat-paper) .proj[data-reveal].is-in:hover .proj-window img { scale: 1.13; transition-duration: 1.4s; transition-delay: 0s; }
}

/* While the row travels, the paint in each pane drifts the other way, a little: a window onto a far landscape */
@supports (animation-timeline: view()) {
  .motion:not(.mat-paper) .projects.is-pinned { view-timeline: --proj-scene block; }
  .motion:not(.mat-paper) .projects.is-pinned .proj-window picture { animation: proj-drift 1s linear both; animation-timeline: --proj-scene; animation-range: contain 0% contain 100%; }
}
@keyframes proj-drift { from { translate: -3.2% 0; } to { translate: 3.2% 0; } }

@media (prefers-reduced-motion: reduce) {
  .motion .proj[data-reveal] .proj-window img, .proj-window img { scale: 1; transition: none; }
  .motion:not(.mat-paper) .proj[data-reveal] { clip-path: none; }
}
