/* ═══════════════════════════════════════════════════════════════════════════
   drowningdot · section layer
   The process and contact bands, lifted from the home page so /process/ and
   /contact/ can stand on their own URLs. The home page keeps its inline copy;
   a change to either band has to land in both places.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── contact ────────────────────────────────────────────────────────────── */
.contact{border-top:1px solid var(--rule);padding-top:var(--band)}
/* the closing statement. The whole point of the section is this line, so it gets
   the largest type on the page and the action sits directly under it. */
.cta-say{font-family:'Instrument Serif',serif;font-weight:400;
  font-size:clamp(38px,7.4vw,118px);line-height:.96;letter-spacing:-.026em;
  margin-bottom:clamp(30px,5vh,64px);max-width:20ch}
.cta-say em{font-style:normal;color:var(--graphite)}
.cta{display:block;text-decoration:none;color:var(--sheet);
  border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.cta-face{display:flex;align-items:baseline;justify-content:space-between;gap:24px;
  padding:clamp(30px,5.2vh,60px) clamp(12px,1.8vw,26px);
  font-family:'Instrument Serif',serif;
  font-size:clamp(30px,5.2vw,78px);line-height:1.02;letter-spacing:-.024em}
/* letter-spacing must be reset, not inherited: the display face's -.024em at
   13px crushed the "i" and the label rendered as "Emal" */
.cta-arrow{font-family:'Instrument Sans',sans-serif;font-size:var(--chrome);
  letter-spacing:normal;white-space:nowrap;align-self:center}
@media (hover:none){ .cta:active{color:var(--redline-text)} }

/* ── contact: the form the CTA opens ─────────────────────────────────────────
   The band is a closing statement, so the form is not allowed to be the first
   thing you see in it. It stays collapsed until the CTA is pressed, and the
   composition of the section is unchanged for anyone who does not press it.

   The collapse is grid-template-rows 0fr → 1fr rather than max-height, because
   max-height animation needs a magic number larger than the content and then
   eases against a height nothing actually has — the last 200ms of a max-height
   reveal is empty time. 0fr → 1fr animates the real measured height.

   visibility, not just overflow: a collapsed form that is merely clipped is
   still in the accessibility tree, so a screen reader finds three fields that
   sighted readers cannot see and Tab lands inside a closed box.

   minmax(0,Nfr) rather than a bare Nfr: an fr track takes an automatic minimum
   of min-content, so a bare 0fr floors at the tallest thing inside it that
   cannot be clipped. The closing rule used to live on the form, and a border is
   exactly that — it survives the form's own overflow:hidden, held the collapsed
   track at 1px, and stacked under the CTA's own rule as a 2px line. The rule
   belongs to the open state, so it is on the wrapper. */
.cf-wrap{display:grid;grid-template-rows:minmax(0,0fr);
  transition:grid-template-rows .55s var(--ease)}
.cf-wrap.is-open{grid-template-rows:minmax(0,1fr);
  border-bottom:1px solid var(--rule)}
.cf-wrap > *{min-height:0;overflow:hidden;visibility:hidden;
  transition:visibility 0s linear .55s}
.cf-wrap.is-open > *{visibility:visible;transition-delay:0s}
/* the same ruled ledger the work index uses, so the form reads as part of the
   page's furniture rather than a widget dropped into it */
.cf-row{display:grid;grid-template-columns:minmax(0,190px) 1fr;
  gap:clamp(10px,1.6vw,24px);align-items:baseline;
  padding:clamp(15px,2vh,20px) clamp(12px,1.8vw,26px);
  border-bottom:1px solid var(--rule);position:relative}
.cf-row label{font-size:var(--chrome);color:var(--graphite);letter-spacing:normal;
  transition:color .25s ease}
/* appearance:none, or Chrome paints its own field box inside the borderless
   row and the textarea reads as a sunken input on a page that has none. */
.cform input,.cform textarea{width:100%;background:transparent;border:0;padding:0;
  appearance:none;-webkit-appearance:none;
  color:var(--sheet);font-family:'Instrument Sans',sans-serif;
  font-size:clamp(16px,1.4vw,19px);line-height:1.5;letter-spacing:normal;resize:none}
.cform ::placeholder{color:#55534D;opacity:1}
/* The focus ring is NOT overridden here. :focus-visible in styles.css is the
   site's one focus primitive, measured against 2.4.11, and a form is the last
   place to start exempting things from it. The row treatment below is extra —
   it marks the active row for a pointer user, who gets no ring at all. */
.cf-row:focus-within{background:rgba(234,232,225,.035)}
.cf-row:focus-within label{color:var(--sheet)}
.cf-row::before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;
  background:var(--redline);transform:scaleY(0);transform-origin:top;
  transition:transform .28s var(--ease)}
.cf-row:focus-within::before{transform:scaleY(1)}
/* a field the server rejected keeps the edge without needing focus */
.cf-row.is-bad::before{transform:scaleY(1)}
.cf-row.is-bad label{color:var(--redline-text)}

.cf-pot{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

/* the call qualifier. Chrome type in graphite, the same register the pool cue
   and the work-index meta use, because it is a footnote to the CTA and not a
   second one — the moment it competes with "Start a project" the band has two
   actions and no statement. */
.cta-note{font-size:var(--chrome);color:var(--graphite);
  padding:15px clamp(12px,1.8vw,26px) 0;max-width:52ch}

.cf-foot{display:flex;align-items:center;justify-content:space-between;
  gap:20px;flex-wrap:wrap;padding:clamp(14px,2vh,18px) clamp(12px,1.8vw,26px)}
.cf-status{font-size:var(--chrome);color:var(--graphite);margin:0;min-height:1.2em}
.cf-status[data-state="bad"]{color:var(--redline-text)}
.cf-status[data-state="ok"]{color:var(--sheet)}
.cf-send{font-family:'Instrument Serif',serif;font-size:clamp(21px,2.2vw,30px);
  letter-spacing:-.018em;color:var(--sheet);background:transparent;
  border:1px solid var(--rule);padding:9px 20px;cursor:pointer;
  transition:background .3s ease,color .3s ease,border-color .3s ease}
.cf-send:hover,.cf-send:focus-visible{background:var(--sheet);color:var(--proof);
  border-color:var(--sheet)}
.cf-send[disabled]{opacity:.5;cursor:default;background:transparent;color:var(--sheet)}
@media (max-width:640px){
  /* 190px of label column against a 130px field is a form nobody can type in */
  .cf-row{grid-template-columns:1fr;gap:7px}
}
@media (prefers-reduced-motion:reduce){
  .cf-wrap{transition:none}
  .cf-row::before{transition:none}
}

/* ─── process ────────────────────────────────────────────────────────────── */
.process{padding:var(--band) 0 0}
.steps{position:relative;padding-bottom:var(--band)}
.step{position:sticky;top:60px;background:var(--proof);
  border-top:1px solid var(--rule);
  /* the numeral column widens with the numeral. at --label-col it was a 150px
     box holding 76px of type, so the figure read as a margin note beside the
     heading rather than as the thing you are counting through. */
  display:grid;grid-template-columns:clamp(104px,15vw,250px) minmax(0,1fr);
  gap:clamp(22px,3.4vw,56px);
  align-content:start;
  padding:clamp(32px,4.6vh,58px) 0 clamp(44px,7vh,86px);
  /* the height is the scroll distance each panel gets to stay pinned, but at
     46vh the content filled barely half of it and every panel read empty.
     Splitting heading and note into two columns halved the content height
     again, so this comes down rather than up — the pin still has room to read
     as a pin, without a third of every panel being floor. */
  min-height:clamp(238px,31vh,330px)}
/* later panels have to paint over earlier ones as they pin */
.step:nth-child(1){z-index:1} .step:nth-child(2){z-index:2}
.step:nth-child(3){z-index:3} .step:nth-child(4){z-index:4}
.step:nth-child(5){z-index:5}
/* 2px, not 1px: this rule is the only thing that reports which step you are on,
   and a hairline drawing itself across 1440px was easy to miss entirely */
.step::before{content:"";position:absolute;left:0;right:0;top:-1px;height:2px;
  background:var(--redline);transform:scaleX(0);transform-origin:left;
  transition:transform .55s var(--ease)}
.step.is-on::before{transform:scaleX(1)}
.step .n{font-family:'Instrument Serif',serif;
  font-size:clamp(58px,10.5vw,164px);line-height:.78;letter-spacing:-.045em;
  color:var(--graphite);font-variant-numeric:tabular-nums;
  transition:color .45s ease}
.step.is-on .n{color:var(--redline-text)}

/* ── the pinned step leads, the rest step back ──────────────────────────────
   Opacity sits on the content wrapper, never on .step itself: the panels are
   opaque plates that paint over each other as they pin, and a translucent one
   would show the step beneath showing through it.

   The note swaps to sheet while dimmed. This is the whole reason I left this
   effect out the first time — graphite at .55 composites to 2.17:1 against
   proof, which is precisely the trap rule 1 at the top of styles.css exists to
   describe. Sheet at the same opacity measures 5.2:1, so the note stays
   readable and still reads as clearly behind the live step. Measured, not
   guessed: 5.24:1 dimmed, against 5.4:1 for graphite at rest. */
.step > div{transition:opacity .5s var(--ease)}
.step:not(.is-on) > div{opacity:.55}
.step:not(.is-on) p{color:var(--sheet)}

/* ── red halftone on the live step ─────────────────────────────────────────
   The screen drifts by exactly one tile and loops, so it reads as a moving
   dot field with no seam. transform, not background-size — that was the whole
   lesson from the version of this that made scrolling heavy: translating a
   layer is composited on the GPU, resizing a gradient repaints the tile.
   Inset past the edges so the travel can never expose a corner, and z-index -1
   so it sits over the panel's own proof ground but under its type. */
.step::after{content:"";position:absolute;inset:-8px;z-index:-1;pointer-events:none;
  background-image:radial-gradient(var(--redline) 30%,transparent 32%);
  background-size:5px 5px;opacity:0;transition:opacity .55s var(--ease)}
.step.is-on::after{opacity:.14;animation:dot-drift-5 3.4s linear infinite}
/* the dot field bleeds 8px above the panel's own top edge, straight into its
   static border-top — border-box keeps the 1px reserved so nothing shifts,
   only its colour drops out, and just while the halftone is the thing playing */
.step.is-on{border-top-color:transparent}
@keyframes dot-drift-5{to{transform:translate3d(5px,5px,0)}}
/* heading and note sit side by side rather than stacked. Stacked, both were
   bounded by their own measure and the right third of every panel stayed
   empty — five times over, on a section that is already 2000px tall. Beside
   each other they use the full width and the panel reads as a spec line. */
.step > div{display:grid;grid-template-columns:minmax(0,1.06fr) minmax(0,.94fr);
  gap:clamp(20px,3.2vw,58px);align-items:start}
.step h3{font-family:'Instrument Serif',serif;font-weight:400;
  font-size:clamp(27px,3.9vw,56px);line-height:1.02;letter-spacing:-.024em;max-width:20ch}
/* 52ch, not 62: the heading grew, and a measure that outran it made the panel
   read as a paragraph with a title rather than a step with a note under it.
   Colour stays graphite at full opacity — dimming inactive steps was the
   obvious move and is exactly what rule 1 at the top of styles.css forbids,
   since graphite under any resting opacity drops through 4.5:1. */
.step p{font-size:clamp(15px,1.24vw,18px);color:var(--graphite);
  max-width:46ch;margin-top:.5em}
@media (max-width:1000px){
  .step{position:static;min-height:0;grid-template-columns:1fr;gap:10px;
    padding:26px 0 30px}
  .step > div{grid-template-columns:1fr;gap:0}
  .step p{margin-top:12px}
  .step .n{font-size:clamp(30px,9vw,46px)}
  .step::before{transform:scaleX(1)}
  .step:not(.is-on)::before{background:var(--rule)}
}
/* standalone pages: the band is the page, so it carries no top rule */
.page-band{border-top:0;padding-top:0}
