/* ══════════════════════════════════════════════════════════════════════
   ZYRN — THE PROOF BAND

   Its own file rather than a block inside case.css: this is the only piece
   of the case-study work that the LANDING page needs, and case.css carries
   two pinned instruments, five vitrine variants and a dependency on
   Lumina's two typefaces. Loading all of that on index.html to style one
   card would be a real cost for no benefit.
   ══════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════
   THE PROOF BAND  (index.html, between SYS.03 and SYS.04)

   The landing page carried no evidence at all until 2026-08-19 — the four
   lines made a claim and nothing on the page answered "show me". This is
   the answer, and it is one card rather than a grid because there is one
   case: a row of three would advertise two that do not exist.

   The whole card is a single <a>. Its children are spans, not divs, so the
   markup stays valid inside an anchor. One `data-reveal` on the anchor and
   none inside it — a nested reveal never fires (CLAUDE.md).
   ══════════════════════════════════════════════════════════════════════ */

/* FOUR cards, two by two. v1 was one card laid out horizontally (shot
   beside body); at a third of the width that arrangement gives the copy
   about a dozen characters a line, so the cards stack their own contents
   instead. v2 was a three-column row.

   Four across does not work at this container width — a quarter of 1180px
   is 270px, which is narrower than the third that was already too narrow to
   lay a card out sideways in. So the band is a 2x2 block: each card keeps
   roughly the width three had, and the row simply goes down instead of
   along.

   Still an explicit column count, never `auto-fit`: auto-fit drops a track
   somewhere around 1000px on its own schedule and orphans whatever is left
   over at half width with a hole beside it. Below 1040 these go to a single
   column and lay out horizontally — see the media queries. */
.proof__row{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(16px,2.4vw,28px);
  margin-top:clamp(26px,4vh,44px);
}

/* The card is an <article> as of 2026-08-30, not an <a>. `.proof__go` is the
   real link and stretches itself over the whole card, so the card is still
   one hit target — and the visit tab can be a second, genuine link inside it,
   which a nested anchor could never have been. */
.proof__card{
  position:relative;
  display:flex;flex-direction:column;
  padding:clamp(14px,1.6vw,20px);
  border:1px solid var(--hair);
  background:rgba(14,15,18,0.6);
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);
  transition:border-color 320ms var(--ease-reveal,cubic-bezier(.16,1,.3,1)),
             transform 420ms var(--ease-reveal,cubic-bezier(.16,1,.3,1)),
             background 320ms ease;
}
@media (hover:hover) and (pointer:fine){
  .proof__card:hover{
    border-color:var(--hair-strong);
    background:rgba(14,15,18,0.72);
    transform:translateY(-3px);
  }
  .proof__card:hover .proof__vis img,
  .proof__card:hover .proof__vid{transform:scale(1.04)}
  .proof__card:hover .proof__go .arrow{transform:translateX(5px)}
  .proof__card:hover .proof__go{color:var(--vapor)}
  /* Duk's rings spin up on hover, and the question it is holding surfaces.
     One custom property does both — the mark reads `--spin` in its own
     animation durations, so the card never has to know how the mark works. */
  .proof__card--duk:hover .dukmark{--spin:2.6}
  .proof__card--duk:hover .dukfield__q{opacity:0.9;transform:none;letter-spacing:0.42em}
  .proof__card--duk:hover .dukfield::after{opacity:0.5;transform:scale(1)}

  /* Axes answers the same way, through the same one property — except
     for the entity itself, which is a canvas and cannot be driven by a
     custom property: axent.js takes the pointer off the card directly
     and doubles its own clock. */
  .proof__card--axes:hover .axfield{--spin:3}
  .proof__card--axes:hover .axfield__q{opacity:0.9;transform:none;letter-spacing:0.42em}
}
/* the outline follows the link, and is drawn on the card it covers */
.proof__card:focus-within{outline:2px solid var(--vapor);outline-offset:3px}
.proof__go:focus-visible{outline:none}
/* ...except for the visit tab, which is its own control and keeps its own */
.proof__card:focus-within:has(.visit:focus-visible){outline:none}

.proof__vis{
  position:relative;
  display:block;overflow:hidden;border-radius:4px;
  border:1px solid var(--hair);background:#05070B;
  margin-bottom:clamp(14px,1.8vh,20px);
}
.proof__vis img,
.proof__vid{
  display:block;width:100%;height:auto;aspect-ratio:16/10;object-fit:cover;
  transition:transform 620ms var(--ease-reveal,cubic-bezier(.16,1,.3,1));
}
/* The card is one hit target and a <video> is not part of it: without this a
   right-click lands on the media element and offers to save a film of someone
   else's website, and on a phone a long press does the same. The card's own
   href is what should be reachable, so the video is scenery. */
.proof__vid{pointer-events:none;background:#05070B}
/* the rewind after a hover: out, seek to the top, back in (ui.js) */
.proof__vid{transition:transform 620ms var(--ease-reveal,cubic-bezier(.16,1,.3,1)),opacity 240ms ease}
.proof__card.is-rewind .proof__vid{opacity:0}

/* ── the scroll-through (Lumina, THEHUB) ─────────────────────────────
   Each of the two client cards carries one take of that client's live
   home page scrolled top to bottom. With a mouse it plays while the card
   is hovered or focused (ui.js), so the frame needs to say two things a
   poster cannot: that there is more page below, and where in it you are.

   The rail is a scrollbar because that is what it stands for — the thumb
   is the take's playhead. Vapor ink; the light around it is the client's
   own cast (amber for Lumina, cyan for THEHUB), the same rule as their
   logo halos: another firm's colour appears as light, never as paint. */
.proof__rail{
  position:absolute;right:8px;top:19%;bottom:12%;z-index:2;
  width:2px;border-radius:2px;
  background:rgba(242,243,245,0.10);
  pointer-events:none;
  opacity:0.55;transition:opacity 420ms ease;
}
.proof__rail i{
  display:block;width:100%;height:22%;border-radius:2px;
  background:var(--vapor);
  box-shadow:0 0 10px var(--cast,rgba(242,243,245,0.5));
  will-change:transform;
}
.proof__card{--cast:rgba(255,178,90,0.75)}
.proof__card--hub{--cast:rgba(74,196,255,0.75)}
.proof__card.is-playing .proof__rail{opacity:1}

/* The cue exists only where the scroll waits for a hover. Without one the
   take plays on approach and a cue asking for a gesture the device cannot
   make would be a lie. */
.proof__cue{display:none}
@media (hover:hover) and (pointer:fine) and (prefers-reduced-motion:no-preference){
  .proof__card.has-scroll .proof__cue{
    position:absolute;left:10px;bottom:10px;z-index:2;
    display:inline-flex;align-items:center;gap:8px;
    height:26px;padding:0 10px;
    font-size:9px;letter-spacing:0.22em;text-transform:uppercase;
    color:rgba(242,243,245,0.8);
    background:rgba(8,9,12,0.8);
    border:1px solid rgba(242,243,245,0.18);
    -webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);
    pointer-events:none;
    transition:opacity 360ms ease,transform 460ms var(--ease-reveal,cubic-bezier(.16,1,.3,1));
  }
  /* a mouse wheel, drawn: the dot inside rolls down and fades, on repeat */
  .proof__card.has-scroll .proof__cue::before{
    content:'';flex:0 0 auto;width:9px;height:13px;border-radius:5px;
    border:1px solid rgba(242,243,245,0.6);box-sizing:border-box;
  }
  .proof__card.has-scroll .proof__cue::after{
    content:'';position:absolute;left:14px;top:calc(50% - 4px);
    width:1px;height:3px;border-radius:1px;background:var(--vapor);
    animation:pf-wheel 1.8s cubic-bezier(.45,0,.2,1) infinite;
  }
  .proof__card.has-scroll:hover .proof__cue,
  .proof__card.has-scroll.is-playing .proof__cue{opacity:0;transform:translateY(6px)}
}
@keyframes pf-wheel{
  0%{transform:translateY(0);opacity:1}
  70%,100%{transform:translateY(4px);opacity:0}
}
/* ── Duk's panel ────────────────────────────────────────────────────
   Duk has no screenshot and must not be given a fake one — it is not
   built. So its panel is the mark, live, on a ruled ground: the thing
   itself rather than a mock-up of a product that does not exist yet. */
.proof__vis--duk{background:#0B0C0F;border-color:var(--hair)}
.dukfield{
  position:relative;display:flex;align-items:center;justify-content:center;
  aspect-ratio:16/10;overflow:hidden;
  background-image:
    linear-gradient(rgba(242,243,245,0.028) 1px, transparent 1px),
    linear-gradient(90deg, rgba(242,243,245,0.028) 1px, transparent 1px);
  background-size:34px 34px;
}
/* The mark carries this panel, so it is sized to fill it rather than to sit
   politely in the middle of it — at 86px in an 825px box it read as a
   loading spinner someone forgot to remove. */
.dukfield .dukmark{--dm:clamp(96px,15vw,196px);position:relative;z-index:2}

/* Two rings behind it. The inner one is the listening ripple that answers
   the hover; the outer is a dashed horizon that turns slowly whatever you
   do, so the panel is alive before you touch it. */
.dukfield::after{
  content:'';position:absolute;left:50%;top:50%;
  width:58%;aspect-ratio:1/1;margin:-29% 0 0 -29%;
  border:1px solid rgba(242,243,245,0.3);border-radius:50%;
  opacity:0.22;transform:scale(0.74);
  transition:opacity 520ms ease,transform 620ms cubic-bezier(.16,1,.3,1);
  pointer-events:none;
}
.dukfield::before{
  content:'';position:absolute;left:50%;top:50%;
  width:88%;aspect-ratio:1/1;margin:-44% 0 0 -44%;
  border:1px dashed rgba(242,243,245,0.14);border-radius:50%;
  opacity:0.7;
  animation:duk-horizon 44s linear infinite;
  pointer-events:none;
}
@keyframes duk-horizon{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.dukfield::before{animation:none}}
.dukfield__q{
  position:absolute;left:0;right:0;bottom:11%;
  text-align:center;font-size:9.5px;letter-spacing:0.3em;
  color:rgba(242,243,245,0.42);
  opacity:0;transform:translateY(6px);
  transition:opacity 420ms ease,transform 520ms cubic-bezier(.16,1,.3,1),
             letter-spacing 520ms cubic-bezier(.16,1,.3,1);
}

/* ── the body ───────────────────────────────────────────────────────── */

.proof__body{display:flex;flex-direction:column;align-items:flex-start;gap:clamp(9px,1.3vh,13px);flex:1 1 auto}
.proof__head{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%}
.proof__mark{display:block;line-height:0}
.proof__mark img{
  display:block;height:17px;width:auto;
  filter:drop-shadow(0 2px 9px rgba(0,0,0,0.55))
         drop-shadow(0 0 20px rgba(255,178,90,0.32));
}
/* Their lockup is a wordmark beside a mark rather than a wordmark alone, so
   it carries more width per unit of height and needs a little less of it.
   The amber halo is Lumina's — this one gets a cyan cast of its own, because
   the vitrine rule shows another firm's brand and never repaints it. */
.proof__mark--hub img{
  height:21px;
  filter:drop-shadow(0 2px 9px rgba(0,0,0,0.55))
         drop-shadow(0 0 20px rgba(74,196,255,0.28));
}
/* Duk is ours, so it is set in our own type rather than given a logotype */
.proof__wm{
  font-size:19px;font-weight:500;letter-spacing:-0.035em;color:var(--vapor);
  line-height:1;
}

.proof__st{
  display:inline-flex;align-items:center;gap:7px;flex:0 0 auto;
  padding:3px 9px;border:1px solid var(--hair);border-radius:2px;
  font-size:9px;color:rgba(242,243,245,0.72);
}
.proof__st::before{content:'';width:5px;height:5px;border-radius:50%;background:var(--vapor)}
/* Three states, three dots, and the difference between them is the point
   of the band: shipped holds steady, in service breathes because it is
   working right now, in development blinks because it is not finished. */
.proof__st--live::before{opacity:0.9}
.proof__st--svc::before{animation:pf-svc 3.4s ease-in-out infinite}
.proof__st--dev::before{animation:pf-dev 2.2s ease-in-out infinite}
@keyframes pf-svc{0%,100%{opacity:0.5}50%{opacity:1}}
@keyframes pf-dev{0%,100%{opacity:0.28}50%{opacity:1}}
@media (prefers-reduced-motion:reduce){
  .proof__st--dev::before,.proof__st--svc::before{animation:none;opacity:0.7}
}

.proof__t{
  display:block;font-size:clamp(16px,1.7vw,20px);line-height:1.24;
  letter-spacing:-0.02em;color:var(--vapor);
}
.proof__b{
  display:block;font-size:13.5px;line-height:1.6;
  color:rgba(242,243,245,0.64);
}
.proof__nums{
  display:flex;flex-wrap:wrap;gap:6px clamp(12px,1.6vw,20px);
  color:var(--steel);padding-top:2px;margin-top:auto;
}
/* Each stat is one unit. Left to wrap internally, "00 build steps" broke
   after "build" and left "steps" alone on a second line — a number and
   its unit are not a sentence and must not be hyphenated apart. */
.proof__nums > span{white-space:nowrap}
.proof__nums b{
  font-weight:400;color:var(--vapor);
  font-variant-numeric:tabular-nums;margin-right:5px;
}
.proof__go{
  display:inline-flex;align-items:center;gap:9px;
  color:rgba(242,243,245,0.8);
  border-bottom:1px solid var(--hair-strong);padding-bottom:3px;margin-top:4px;
  transition:color 260ms ease;
}
.proof__go .arrow{transition:transform 320ms var(--ease-reveal,cubic-bezier(.16,1,.3,1))}
/* This is what makes the card clickable. z-index 1 keeps it under the visit
   tab, which sits at 3 — otherwise the stretched link would swallow the tap
   and every card would go to its case study instead of the live site. */
.proof__go::after{
  content:'';position:absolute;inset:0;z-index:1;
}

/* One column below 1040. A full-width card with a 16:10 visual stacked on
   top of it is a 600px-tall image on a laptop, so from here down to phone
   width the card lays itself out horizontally instead — visual left, copy
   right. That IS the v1 arrangement the note at the top of this file
   rejected, and it is right again here for the reason it was wrong there:
   the card is full width now, so the copy column is not twenty characters. */
@media (max-width:1040px){
  .proof__row{grid-template-columns:1fr;gap:18px}
  .proof__card{flex-direction:row;gap:clamp(16px,2.6vw,28px);align-items:stretch}
  .proof__vis{flex:0 0 clamp(230px,38%,430px);margin-bottom:0;align-self:flex-start}
  .proof__body{flex:1 1 auto}
}
/* and back to stacked once that copy column would drop under ~30 chars */
@media (max-width:640px){
  .proof__card{flex-direction:column}
  /* `align-self` has to be reset with the rest of it. Once the card is a
     COLUMN, flex-start is a cross-axis (horizontal) alignment, so the
     panel shrank to its content width — which is nothing for the two
     panels that are drawn rather than photographed, and 160px of hexagon
     on a 390px screen. Lumina's card hid it, because an <img> carries an
     intrinsic width and filled the box anyway. */
  .proof__vis{flex:none;align-self:stretch;margin-bottom:clamp(14px,1.8vh,20px)}
}
