/* ==========================================================
   SHOWCASE LAYOUT — masthead across the top, open space, and a
   horizontal row of equal-size project cards along the bottom.

   All cards share one size and one baseline; hover scales a card up
   from its bottom edge so it grows into the empty space above rather
   than pushing the row around.
   ========================================================== */

body.showcase-page{
  /* colour comes from the palette in style.css — nothing is defined here */
  --bg: var(--white);
  --bg-soft: #f0f0f0;
  --bg-soft: color-mix(in srgb, var(--grey) 22%, var(--white));
  --fg: var(--black);
  --fg-dim: #6f6f6f;
  --fg-dim: color-mix(in srgb, var(--black) 72%, var(--white));
  --line: #d7d7d7;
  --line: color-mix(in srgb, var(--grey) 60%, var(--white));
  --hl: var(--pink);
  --pad-x: clamp(18px, 3vw, 44px);
  --card-w: clamp(148px, 14.5vw, 205px);
  /* How far a hovered card scales up. js/showcase-row.js reads this and
     derives each neighbour's push from it — half the extra width, since
     the card grows equally to both sides — so this is the single source
     of truth and the gaps can't drift out of sync. */
  --card-grow: 1.35;
  /* How far the frame's top edge travels when it scales. Frames are 4/5,
     so their height is 1.25x their width; growing from a bottom origin
     pushes the top up by that height times (grow - 1). Used to lift the
     label clear of the expanding frame and to size the row's headroom,
     both derived from --card-grow so nothing has to be kept in sync. */
  --card-lift: calc(var(--card-w) * 1.25 * (var(--card-grow) - 1));
  /* Easing for the scale and label lift. Barely any overshoot — the
     magnetic rebound now comes from the spring in showcase-row.js, and
     stacking a bouncy curve on top of it read as a double-hitch. */
  --card-ease: cubic-bezier(.19, 1.02, .28, 1);
  --card-push-duration: 1.15s;
  background: var(--bg);
  color: var(--fg);
}

/* this page brings its own three-column masthead, so the shared header
   isn't loaded at all — nothing to override */

.sc-page{
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* ---------- masthead ---------- */
.sc-masthead{
  display: grid;
  /* name column sizes to its own content now that it's a single nowrap
     line; the intro takes whatever is left */
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: clamp(20px, 4vw, 60px);
  padding: clamp(22px, 3.4vh, 34px) var(--pad-x) 0;
}

/* name on a single line in the top-left corner. Sized smaller than the
   stacked version had to be — 22 characters on one line simply can't run
   at 66px without overflowing the column. */
.sc-name{
  display: block;
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(17px, 2.5vw, 42px);
  line-height: 1;
  letter-spacing: -.03em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--fg);
  transition: color .35s ease;
}
.sc-name:hover{ color: var(--hl); }

/* ---------- the tag, held in the right-middle void ----------
   Now the 3D dot-cloud tag from the earlier homepage (js/hero-split.js)
   rather than a flat masked SVG: it idles, spins with the scroll wheel
   and scatters where you click it. The container is square because the
   WebGL canvas fills it and the renderer sizes itself from these
   dimensions — the tag is fitted inside that square by the scene. */
/* the flat mark is the phone's tag; above the breakpoint the dot cloud has it */
.sc-tag-flat{ display: none; }

.sc-tag{
  position: absolute;
  /* +100px pulls it in from the right edge */
  right: calc(clamp(18px, 4vw, 70px) + 100px);
  top: 42%;
  transform: translateY(-50%);
  width: clamp(240px, 30vw, 460px);
  aspect-ratio: 1;
  z-index: 2;
}
.sc-tag canvas{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* clickable so the scatter interaction still works */
  cursor: pointer;
}

.sc-intro{ max-width: 46ch; }
.sc-intro h1{
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .01em;
  margin-bottom: 3px;
}
.sc-intro p{
  font-size: 12px;
  line-height: 1.45;
  color: var(--fg-dim);
}

/* contact links sitting directly under the intro copy — the nav no longer
   carries a Contact item, since that section lives on the about page */
.sc-intro-social{
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin-top: 10px;
}
.sc-intro-social a{
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--fg);
  padding-bottom: 2px;
  border-bottom: 1px solid color-mix(in srgb, var(--black) 30%, transparent);
  transition: color .3s ease, border-color .3s ease;
}
.sc-intro-social a:hover{
  color: var(--hl);
  border-color: var(--hl);
}

.sc-nav{
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}
.sc-nav a{
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--fg-dim);
  transition: color .3s ease;
}
.sc-nav a:hover,
.sc-nav a[aria-current="page"]{ color: var(--fg); }

/* ---------- the row ----------
   Sits at the bottom of the page. overflow-x scrolls; padding-bottom
   leaves room for a hovered card to grow upward without being clipped
   by the scroll container. */
.sc-row-wrap{
  margin-top: auto;
  position: relative;
  /* above the tag canvas, so a card near the tag still takes the hover
     rather than the canvas swallowing the pointer */
  z-index: 6;
  padding-bottom: clamp(14px, 2.6vh, 26px);
}

/* soft fade at both ends so cards dissolve off the edges instead of
   being sliced by a hard boundary */
.sc-row-wrap::before,
.sc-row-wrap::after{
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  width: clamp(28px, 5vw, 80px);
  z-index: 5;
  pointer-events: none;
}
.sc-row-wrap::before{
  left: 0;
  background: linear-gradient(to right, var(--bg) 12%, color-mix(in srgb, var(--bg) 0%, transparent) 100%);
}
.sc-row-wrap::after{
  right: 0;
  background: linear-gradient(to left, var(--bg) 12%, color-mix(in srgb, var(--bg) 0%, transparent) 100%);
}

.sc-row{
  display: flex;
  align-items: flex-end;
  gap: clamp(10px, 1.1vw, 18px);
  padding: 0 var(--pad-x);
  overflow-x: auto;
  overflow-y: hidden;
  /* a hovered card scales up out of the row; without headroom inside the
     scrolling box the growth would be clipped at the top. Derived from
     --card-lift so raising --card-grow can't silently start clipping. */
  padding-top: calc(var(--card-lift) + 38px);
  scrollbar-width: none;
  -ms-overflow-style: none;
  cursor: grab;
  /* Was 'smooth'. js/showcase-row.js now runs its own flywheel, writing
     scrollLeft every frame, and smooth behaviour would start a fresh
     animation on each of those writes and fight the one already running.
     The easing is in the velocity now, not in the scroller. */
  scroll-behavior: auto;
}
/* Set by js/showcase-row.js once the row has been cloned into a loop. The
   side padding existed to keep the first and last card off the screen edge,
   but a loop has no first or last card: left in, it would open a gap the
   width of the padding every time the sequence came round again. */
.sc-row.is-looping{
  padding-left: 0;
  padding-right: 0;
}
.sc-row::-webkit-scrollbar{ display: none; }
.sc-row.is-dragging{
  cursor: grabbing;
  scroll-behavior: auto;
}

.sc-card{
  position: relative;
  flex: 0 0 var(--card-w);
  width: var(--card-w);
  text-decoration: none;
  color: inherit;
  /* No transition here on purpose. js/showcase-row.js writes this
     element's transform every frame from a spring simulation — a CSS
     transition on the same property would restart from scratch each time
     the target changed, which is exactly what made sweeping across the
     row stutter. */
  will-change: transform;
}

.sc-card-label{
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 8px;
  font-size: 10.5px;
  letter-spacing: .01em;
  color: var(--fg-dim);
  white-space: nowrap;
  /* rides up with the growing frame, which would otherwise expand over it */
  transition: color .35s ease,
              transform var(--card-push-duration) var(--card-ease);
}
.sc-card:hover .sc-card-label,
.sc-card:focus-visible .sc-card-label{
  transform: translateY(calc(var(--card-lift) * -1));
}
.sc-card-label .dot{
  width: 6px;
  height: 6px;
  border-radius: 50%;
  border: 1px solid currentColor;
  flex: none;
  transition: background .35s ease, border-color .35s ease;
}
.sc-card:hover .sc-card-label{ color: var(--fg); }
.sc-card:hover .sc-card-label .dot{
  background: var(--hl);
  border-color: var(--hl);
}

.sc-card-frame{
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: 10px;
  background: var(--bg-soft);
  /* grows up out of the row, so the baseline never moves */
  transform-origin: bottom center;
  transform: scale(1);
  transition: transform var(--card-push-duration) var(--card-ease),
              box-shadow var(--card-push-duration) var(--card-ease);
}
.sc-card:hover .sc-card-frame,
.sc-card:focus-visible .sc-card-frame{
  /* bottom-CENTRE origin (set above) means this grows equally to the left
     and right, which is what the neighbours' shift is calculated against */
  transform: scale(var(--card-grow));
  box-shadow: 0 30px 60px -22px color-mix(in srgb, var(--black) 45%, transparent);
}
/* lift the hovered card above its neighbours as it expands over them */
.sc-card:hover,
.sc-card:focus-visible{ z-index: 20; }

.sc-card-frame img{
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: saturate(.82) brightness(.97);
  transition: filter .5s var(--ease);
}
.sc-card:hover .sc-card-frame img{ filter: saturate(1) brightness(1); }

/* small corner badge, as in the reference */
.sc-card-badge{
  position: absolute;
  top: 0;
  right: 0;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  color: var(--fg);
  font-size: 11px;
  line-height: 1;
  opacity: 0;
  transform: translateY(-2px);
  transition: opacity .35s var(--ease), transform .35s var(--ease);
}
.sc-card:hover .sc-card-badge,
.sc-card:focus-visible .sc-card-badge{
  opacity: 1;
  transform: translateY(0);
}

/* ---------- responsive ---------- */
/* ==========================================================
   PHONE — follows the layout sketch: name and a round ABOUT button at
   the top, intro, large contact links, a swipeable row of big cards,
   and the tag beneath.
   ========================================================== */
@media (max-width: 860px){
  /* Two bands, and the card takes everything under the masthead: a slim
     signature row, then the work running off the bottom edge.

     The page is a locked single screen here, as on desktop. It stopped
     being one when the old layout stacked a tag, cards, a name and a link
     row taller than the viewport; with only a masthead above the card there
     is nothing left to scroll to, and locking it means a vertical drag on
     the card can't nudge the page instead of doing nothing. */
  .sc-page{
    /* svh, not vh: vh on a phone means the viewport with the browser chrome
       hidden, so a 100vh column overflows by the height of the address bar
       until you scroll. svh is the smallest state, which always fits. */
    height: 100vh;
    height: 100svh;
    min-height: 0;
    overflow: hidden;
    /* the card runs to the bottom edge, so nothing sits under it */
    padding-bottom: 0;
  }
  /* the page is exactly one screen again, so scrolling it does nothing */
  body.showcase-page,
  html:has(body.showcase-page){
    height: 100%;
    overflow: hidden;
  }
  /* Two bands: a thin masthead, then the card. The masthead is a three-part
     row — name at the left, the tag mark centred, the burger at the right —
     which is the same left/centre/right relationship the desktop header
     has, so the phone reads as the same site rather than a separate design.
     The tag is taken out of flow and centred absolutely, because it has to
     centre on the SCREEN rather than on the space left over beside the
     name. */
  /* ---------- one masthead axis ----------
     The name, the tag and the burger used to have three different tops and
     two different side gutters. These three values give them one of each:
     the band starts at --mast-top and is --mast-h tall, and all three centre
     on the line through the middle of it. --burger-top is read by menu.css,
     which cannot know the band's height on its own. */
  body.showcase-page{
    --mast-top: clamp(12px, 2.4vh, 20px);
    --mast-h: min(30vw, 120px);
    --burger-top: calc(var(--mast-top) + (var(--mast-h) - 46px) / 2);
  }

  .sc-page{ position: relative; }
  .sc-masthead { order: 1; flex: none; }
  /* takes every pixel the masthead doesn't, so the card reaches the bottom
     edge on any screen without the height being guessed at */
  .sc-row-wrap {
    order: 2;
    margin-top: 0;
    flex: 1;
    min-height: 0;
    display: flex;
    /* the same gutter the card keeps at its sides */
    padding-bottom: var(--card-inset);
  }
  .sc-row{ flex: 1; align-items: stretch; }
  .sc-card{ display: flex; flex-direction: column; }

  /* The band is exactly the tag's height and the name is centred in it, so
     the name's optical centre and the tag's land on the same line. The
     height also keeps the card clear of the tag, which is absolutely
     positioned and would otherwise drop pink dots onto the first preview. */
  .sc-masthead{
    display: flex;
    align-items: center;
    margin-top: var(--mast-top);
    padding-top: 0;
    padding-bottom: 0;
    min-height: var(--mast-h);
    box-sizing: border-box;
  }
  /* nothing left in it on a phone; without this it still claims flex width */
  .sc-intro{ display: none; }
  /* Stops short of where the centred tag begins. The tag box is 30vw
     centred, so it starts at 35vw; 25vw leaves a clear gap at every phone
     width down to 320px rather than the name running under the mark. */
  .sc-name{ max-width: 25vw; }
  /* Set small and bold as a corner logotype rather than a headline: the
     card is the loud thing on this screen. Still the full name though,
     not an abbreviation, because this is the page a stranger lands on and
     it is the only place his name appears. It wraps to two lines, which is
     what keeps it narrow enough to clear the centred tag. */
  .sc-name{
    white-space: normal;
    font-family: var(--display);
    font-weight: 700;
    font-size: clamp(13px, 3.7vw, 16px);
    line-height: 1.12;
    letter-spacing: -.01em;
  }
  .sc-intro{ max-width: none; }
  /* The role line comes back: a visitor who has just swiped the work should
     be told what it is in one line. The paragraph stays hidden, it is three
     sentences of positioning that nobody reads on a phone. Still in the
     markup, so it is indexed and read out, just not painted. */
  /* Neither the role line nor the paragraph is painted here. The name, the
     tag and the card already say what this is, and the about page carries
     the positioning properly. Both stay in the markup, so they are still
     indexed and read aloud, just not drawn. */
  .sc-intro h1,
  .sc-intro p{ display: none; }

  /* Contact moves into the burger, which already holds the navigation and
     its own copy of these three links. Two blocks of links on one screen,
     one of them duplicated inside the menu, was a large part of what made
     the page feel busy. */
  .sc-intro-social{ display: none; }

  /* nav collapses to the single round ABOUT button from the sketch —
     "Work" is the page you're already on, so it earns no space here */
  .sc-nav{ align-self: start; }
  .sc-nav a[aria-current="page"]{ display: none; }
  .sc-nav a{
    display: flex;
    align-items: center;
    justify-content: center;
    width: clamp(74px, 21vw, 100px);
    aspect-ratio: 1;
    border-radius: 50%;
    background: var(--hl);
    color: var(--black);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .12em;
  }

  /* The card is the page: one at a time, sitting on the same small gutter on
     the left, right and bottom, and running up under the masthead.

     The gutter is one value used three ways: the card's inset from each
     side, the gap to the next card, and the space beneath. Because the gap
     equals the inset, the next card's leading edge lands exactly on the
     right screen edge when this one is snapped centre, so there is no sliver
     of the neighbour showing. */
  body.showcase-page{
    --card-inset: clamp(10px, 3.4vw, 16px);
    --card-w: calc(100vw - var(--card-inset) * 2);
  }
  .sc-row{
    /* no hover here, so none of the scale headroom the desktop row needs */
    padding-top: clamp(10px, 2vh, 18px);
    gap: var(--card-inset);
    scroll-snap-type: x mandatory;
  }
  /* ---------- the mist, but only while it moves ----------
     The desktop row fades its ends into the page so cards dissolve at the
     edges rather than being sliced. On a phone the card is almost the full
     width, so a permanent fade sat as two white veils across the edges of
     the card you are looking at.

     But the dissolve is only ever ABOUT movement: it exists for cards
     crossing the boundary. So it fades in when the row starts moving and
     back out when it settles. At rest the card has the clean hard edges of
     the reference; the moment you swipe, the neighbours arrive and leave
     through the mist. Wider here than on desktop, because a phone card
     travels the whole screen and needs room to dissolve.

     .is-scrolling is set by js/showcase-row.js. */
  .sc-row-wrap::before,
  .sc-row-wrap::after{
    width: clamp(38px, 13vw, 76px);
    opacity: 0;
    transition: opacity .28s var(--ease);
  }
  .sc-row-wrap.is-scrolling::before,
  .sc-row-wrap.is-scrolling::after{ opacity: 1; }

  @media (prefers-reduced-motion: reduce){
    .sc-row-wrap::before,
    .sc-row-wrap::after{ transition: none; }
  }
  .sc-card{
    scroll-snap-align: center;
    position: relative;
  }
  .sc-card-frame{
    aspect-ratio: auto;
    /* fills the row's height rather than being sized from a ratio.
       min-height:0 is what lets it actually shrink to the flex line: without
       it the frame keeps its content's height and pushes past the row. */
    flex: 1;
    min-height: 0;
    height: auto;
    border-radius: 22px;
  }
  /* The image is pinned to the frame rather than sized at 100%. A percentage
     height resolves against the parent's height, and this parent's height
     comes from the flex line rather than a stated value, so the percentage
     had nothing to resolve against: the image fell back to its natural size
     and ran past the rounded corners. Pinning to the edges removes the
     dependency entirely. */
  .sc-card-frame img{
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* The title moves inside the frame. Above the card it was a second object
     with its own alignment; inside it, the card is one thing you tap. */
  .sc-card-label{
    position: absolute;
    left: 20px;
    bottom: 18px;
    z-index: 3;
    margin: 0;
    font-family: var(--display);
    font-weight: 700;
    font-size: clamp(21px, 6.2vw, 30px);
    letter-spacing: -.02em;
    color: var(--white);
  }
  /* the ring marker belongs to the desktop row's hover language */
  .sc-card-label .dot{ display: none; }

  /* White type has to survive whatever preview sits behind it, and these
     range from a dark stage to a near-white zine page. A scrim guarantees
     it rather than hoping: black at 70% fading out over the bottom third,
     which is invisible on the dark previews and does the work on the light
     ones. */
  .sc-card-frame::after{
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 42%;
    background: linear-gradient(to top,
      color-mix(in srgb, var(--black) 70%, transparent),
      transparent);
    pointer-events: none;
  }

  /* the corner badge is redundant once the whole card is one tap target */
  .sc-card-badge{ display: none; }

  /* no hover on touch — show the work at full strength and keep the
     badge visible instead of gating it behind a state that can't happen */
  .sc-card-frame img{ filter: saturate(1) brightness(1); }
  .sc-card-badge{ opacity: 1; transform: none; }
  .sc-card:hover .sc-card-frame{ transform: scale(1); box-shadow: none; }
  .sc-card:hover .sc-card-label{ transform: none; }
  /* neutralise any inline push JS may have written before a resize
     crossed this breakpoint */
  .sc-card{ transform: none !important; }

  /* The dot cloud, same as desktop, centred on the SCREEN rather than in
     whatever space is left beside the name, so it is taken out of the flow.

     Square, because the WebGL renderer sizes itself from this box's computed
     width and height: with height:auto it would measure zero and render
     nothing. The mark only fills the middle of that square, which is why the
     box can sit this close to the name without them touching.

     pointer-events stay off here. On desktop the canvas is clickable to
     scatter the dots, but on a phone the box overlaps the top of the card,
     and swallowing a swipe that was meant for the row would cost far more
     than the tap is worth. */
  .sc-tag{
    position: absolute;
    top: var(--mast-top);
    left: 50%;
    transform: translateX(-50%);
    width: var(--mast-h);
    aspect-ratio: 1;
    height: auto;
    margin: 0;
    z-index: 5;
    pointer-events: none;
  }
  /* the flat mark is the desktop-free fallback only; the dot cloud is back */
  .sc-tag-flat{ display: none; }
}
