/* ==========================================================================
   SERVICES — "THE FOLD"   (.fld, index.html, homepage 02)

   Replaces the pivot (css/pivot.css, unlinked, kept on disk).

   WHY THE PIVOT WAS REPLACED, MEASURED BEFORE ANYTHING WAS DRAWN. It
   staged the six services in real depth and the depth was honest — but
   it showed ONE PHOTOGRAPH AT A TIME. Five of the six plates were
   clipped to zero width at rest and only developed under the clock or
   the pointer, so at any single moment the section offered one picture,
   five names and one sentence, for 856px of scroll. A visitor who does
   not hover and does not wait never sees five sixths of it. The brief
   this time was "understand it without reading", and a section that
   hides five of its six subjects cannot meet that brief however well it
   is staged.

   THE CONCEPT: SIX PANELS OF ONE SCREEN.
   The claim under this section has never been "we sell six products".
   It is "there is one team, and this is its full range" — so the
   drawing is ONE CONTINUOUS SURFACE, hinged, that the scroll OPENS OUT.
   Closed it is a compact folded object; open it is a rank spanning the
   whole container, six photographs of the actual work standing edge to
   edge on one flat datum. Nothing is hidden at any point in that
   animation: all six pictures, all six numbers and all six names are on
   screen from the first frame to the last, and only the SENTENCE is
   held back as the reward for pointing at a panel.

   That is also why it is a fold rather than a carousel, a deck or a
   grid. A grid of six says "six separate things, pick one". A fold says
   "one thing, presented" — which is the actual sales argument, and it
   is made by the geometry rather than by a caption.

   THE HINGE ARITHMETIC IS EXACT, NOT EYEBALLED. Every panel is the same
   width W and rotates about its own centre by ±θ, so its right edge
   travels +(W/2)sin θ in z and its left edge -(W/2)sin θ. Alternate the
   sign and adjacent edges arrive at the same z — a real hinge. For them
   to meet in x as well, consecutive centres must be W·cos θ apart,
   which is one line of CSS:

       margin-inline: calc((cos(θ) - 1) * W / 2)

   θ is driven by --fld, so the rank's projected width falls out of the
   same number that draws the rotation and the two can never disagree.
   At θ = 21° (open) the rank projects to exactly 6·W·cos 21° = 100% of
   the container; at θ = 66° (closed) to 40.7% of it. W itself is
   100%/6/cos 21° = 17.851%, so the arithmetic closes at every width
   with no measurement in JS and no layout shift at any point — panel
   BOXES never move, only their projections.

   THE DATUM IS FLAT AND IS NOT PART OF THE FOLD. One unbroken hairline
   runs the full container width beneath the rank, a SIBLING of the
   stage inside the perspective element and never a descendant, so it
   stays in screen space while the object above it turns. A gold cursor
   one panel wide slides along it to sit under whichever panel is live.
   Folded object, flat ground: the contrast is what makes the depth
   legible, and the cursor is what ties a moving three-dimensional thing
   back to a single measured line.

   SEPARABLE FROM ITS NEIGHBOURS ON PURPOSE. 03 (forge) is an abstract
   wireframe solid on a canvas, pinned; 04 (reach) is two photographic
   panels at two depths with the camera swinging past. This is a rank of
   six hinged panels seen HEAD ON, opening toward the reader — a
   different object, a different camera and a different action, which is
   what stops three consecutive depth sections reading as one device
   applied three times.

   THE 3D DISCIPLINE (every one of these fails silently, with no error):
   no filter, opacity, mask, clip or overflow on .fld__scene or
   .fld__rank — each flattens preserve-3d. The photographic grading
   lives on .fld__plate and the shading on .fld__panel::after, both
   LEAVES with nothing 3D inside them. No .reveal anywhere in the scene:
   it sets opacity AND a transform, so it would flatten the context and
   fight the stage at the same time. `overflow-x: clip` sits on the
   SECTION, which is outside the perspective context, because a
   3D-projected box and a layout box are different things and the
   layout one can sit outside the container while the projection looks
   clean.

   REST STATE IS THE FINISHED SCENE (--fld initial-value 1) with panel
   01 live, so no JS, a dead CDN or reduced motion all render the open
   rank with a lit panel rather than a folded object nobody can read.
   ========================================================================== */

@property --fld { syntax: "<number>"; inherits: true; initial-value: 1; }
@property --lit { syntax: "<number>"; inherits: true; initial-value: 0; }
@property --fk  { syntax: "<number>"; inherits: true; initial-value: 0; }

.fld {
  /* ONE SOURCE OF TRUTH FOR THE GEOMETRY. The rank and the flat datum
     beneath it are siblings, not ancestor and descendant, so every
     number the two share is declared here and inherited by both — the
     cursor on the ground cannot drift out of agreement with the object
     above it because it is reading the same cosine. */
  --pn: 6;                             /* panels */
  --open: 21deg;                       /* hinge angle, open */
  --shut: 66deg;                       /* hinge angle, closed */
  --ang: calc(var(--shut) + (var(--open) - var(--shut)) * var(--fld));
  --c: cos(var(--ang));
  --cw: 0.93358;                       /* cos(21deg), the open cosine */
  --pw: calc(100% / var(--pn) / var(--cw));
  /* Unitless because the cursor on the datum has to do ARITHMETIC with
     them, not just apply them: it needs the magnification P/(P - z) to
     land under a panel that has stepped toward the reader. Declared
     once here and consumed by the perspective, the rank's recession,
     the live panel's step and the cursor alike, so the flat ground and
     the turning object can never be working from different numbers. */
  --p: 1500;                           /* perspective */
  --step: 58;                          /* the live panel's forward step */
  --recede: 200;                       /* how far back the closed rank sits */

  position: relative;
  padding-block: clamp(2.2rem, 1.8rem + 1.8vw, 3.6rem);
  /* The live panel turns flat and steps forward, which widens its
     PROJECTION past its layout box. Clip, not hidden, so the section can
     never become a scroll container; safe here because .fld is an
     ancestor of the perspective element, not a preserve-3d parent. */
  overflow-x: clip;
  /* The cursor on the datum slides rather than jumps: --fk is a
     registered number, so it interpolates and every descendant reading
     it gets the intermediate values. */
  transition: --fk 0.5s var(--ease-out);
}

/* ---------- head: the heading and the way out share one row ---------- */

.fld__head { margin-bottom: clamp(1.6rem, 1.2rem + 1.6vw, 2.75rem); }

.fld__headrow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--space-4);
  margin-top: var(--space-2);
}

.fld__h {
  font-size: var(--text-h2);
  line-height: 1.06;
  letter-spacing: -0.022em;
  font-weight: 300;
  margin: 0;
  text-wrap: balance;
}
.fld__h em { font-style: normal; color: var(--gold); }

.fld__on { padding-bottom: 0.35rem; white-space: nowrap; }

/* ---------- the scene ---------- */

.fld__scene {
  display: flex;
  flex-direction: column;
  perspective: calc(var(--p) * 1px);
  /* Horizontally centred: an off-centre origin drags the whole
     projected stage sideways. Only the vertical is biased, to put the
     eye slightly above the rank's middle. */
  perspective-origin: 50% 44%;
}

.fld__rank {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  transform-style: preserve-3d;
  /* Closed, the whole object also stands further off and tips slightly
     away — the scroll walks it forward and levels it. */
  transform:
    translateZ(calc(var(--recede) * -1px * (1 - var(--fld))))
    rotateX(calc(6deg * (1 - var(--fld))));
  /* Room for the live panel's forward step to scale up into. */
  padding-block: 10px;
}

.fld__panel {
  --s: -1;                             /* hinge sign, alternating */
  --gw: 0;                             /* give-way when a neighbour is live */
  position: relative;
  flex: 0 0 auto;
  width: var(--pw);
  aspect-ratio: 0.6;
  margin-inline: calc((var(--c) - 1) * var(--pw) / 2);

  /* NOT A CARD: no side borders, so the six read as one banded surface
     creased at the seams rather than as six bordered rectangles at six
     angles. Only the folded top and bottom edges are drawn. */
  border: 0;
  border-block: 1px solid var(--border-gold-strong);
  border-radius: 0;
  padding: 0;
  background: var(--obsidian-raised);
  overflow: hidden;                    /* safe: a leaf in the 3D tree */
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
  -webkit-appearance: none;
  appearance: none;

  transform: rotateY(calc(var(--s) * var(--ang) + var(--gw) * 8deg));
  transform-origin: 50% 50%;
  transition:
    transform 0.45s var(--ease-out),
    --lit 0.45s var(--ease-out);
}
.fld__panel:nth-child(even) { --s: 1; }

/* The peak creases — the hinges that come toward the reader — catch the
   light. Only the near ones: a highlight on every seam would read as a
   grid of outlines. */
.fld__panel:nth-child(odd) {
  box-shadow: inset -1px 0 0 rgba(184, 145, 88, 0.3);
}

.fld__plate {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
  /* filter here, never on the stage: this is a leaf. Dormant panels sit
     back in the light; the live one is fully developed. The whole rank
     is also darker while it is still folded. */
  /* The dormant floor is 0.62, not the 0.5 this started at: five
     photographs the reader cannot make out is the exact fault that took
     the pivot out, and a section whose argument is "all six, together"
     cannot dim five of them into the ground. Far enough down that the
     live panel is unmistakably the lit one; far enough up that the
     other five are still legible photographs. */
  filter:
    brightness(calc((0.62 + 0.38 * var(--lit)) * (0.72 + 0.28 * var(--fld))))
    saturate(calc(0.86 + 0.14 * var(--lit)));
  transition: filter 0.45s var(--ease-out);
}
.fld__plate img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
  display: block;
}

/* Directional shading, mirrored on the hinge sign — this is what makes
   the rank read as folded rather than as six tilted pictures. It is
   strongest while the object is closed and eases off as it opens. */
.fld__panel::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* The receding half of each panel is the shaded half: s = -1 turns
     the LEFT edge away, so the dark end of the gradient is on the left
     (90deg), and s = +1 mirrors it (270deg). The live panel is square
     to the reader and gets a neutral top vignette instead (180deg). */
  background: linear-gradient(
    calc(180deg + var(--s) * 90deg),
    rgba(0, 0, 0, 0.62),
    rgba(0, 0, 0, 0) 64%
  );
  opacity: calc(0.3 + 0.7 * (1 - var(--fld)));
  transition: opacity 0.45s var(--ease-out);
}

/* ---------- what stands on a panel ---------- */

.fld__n {
  position: absolute;
  z-index: 3;
  top: 0.85rem;
  left: 0.85rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  font-style: normal;
  font-variant-numeric: tabular-nums;
  color: var(--vein);
  transition: color 0.4s var(--ease-out);
}
.fld__n::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -6px;
  width: 14px;
  height: 1px;
  background: currentColor;
  transform: scaleX(calc(0.4 + 0.6 * var(--lit)));
  transform-origin: 0 50%;
  transition: transform 0.4s var(--ease-out);
}

.fld__face {
  position: absolute;
  z-index: 3;
  inset: auto 0 0 0;
  padding: 1.15rem 0.95rem 1.05rem;
  background: linear-gradient(
    to top,
    rgb(var(--scrim-rgb) / 0.92) 12%,
    rgb(var(--scrim-rgb) / 0.72) 52%,
    rgb(var(--scrim-rgb) / 0) 100%
  );
}

.fld__name {
  display: block;
  font-size: 0.95rem;
  line-height: 1.22;
  letter-spacing: -0.005em;
  font-weight: 400;
  color: var(--ink);
  text-wrap: balance;
  transition: color 0.4s var(--ease-out);
}

/* The one line of copy is the payoff for pointing, not the only way to
   reach the service — the picture, the number and the name are all
   permanently on screen. 0fr -> 1fr so the growth is animatable and the
   text is never clipped mid-word at rest. */
.fld__say {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  margin-top: 0;
  transition:
    grid-template-rows 0.45s var(--ease-out),
    opacity 0.3s var(--ease-out),
    margin-top 0.45s var(--ease-out);
}
.fld__say > span {
  overflow: hidden;
  min-height: 0;
  display: block;
  font-size: 0.78rem;
  line-height: 1.42;
  color: var(--vein);
}

/* ---------- live ---------- */

.fld__panel.is-live {
  --s: 0;                              /* square to the reader */
  --lit: 1;
  transform: rotateY(0deg) translateZ(calc(var(--step) * 1px));
  border-block-color: var(--gold);
  z-index: 4;
}
.fld__panel.is-live .fld__n { color: var(--gold); }
.fld__panel.is-live .fld__name { color: var(--white); }
.fld__panel.is-live .fld__say {
  grid-template-rows: 1fr;
  opacity: 1;
  margin-top: 0.5rem;
}

/* The fold gathers around whatever is being looked at: the two
   neighbours turn a little further away so the live panel has somewhere
   to open into. Graceful — where :has() is missing, --gw stays 0 and
   only the live panel moves. */
.fld__panel:has(+ .fld__panel.is-live) { --gw: -1; }
.fld__panel.is-live + .fld__panel { --gw: 1; }

/* Pointing is the same state as the clock's, so the two can never
   disagree about where a panel is standing. */
.fld__panel:focus-visible {
  outline: 1px solid var(--gold);
  outline-offset: -3px;
}

/* ---------- the datum: one flat line under a turning object ---------- */

.fld__datum {
  position: relative;
  height: 26px;
  margin-top: 1.35rem;
  flex: 0 0 auto;
}
.fld__datum::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent,
    var(--border-gold-strong) 6%,
    var(--border-gold-strong) 94%,
    transparent
  );
  transform: scaleX(calc(0.12 + 0.88 * var(--fld)));
}

/* One panel wide, and it tracks the fold as well as the clock: --spread
   is the same cosine that sets the rank's width, so the cursor stays
   under its panel while the object is still opening. */
.fld__tick {
  --spread: calc(var(--c) / var(--cw));
  /* THE CURSOR HAS TO BE CORRECTED FOR PERSPECTIVE OR IT MISSES. The
     live panel steps --step toward the reader, so its projection is
     magnified about the perspective origin and no longer sits over the
     footprint the flat arithmetic gives — measured 20px out at both
     ends of the rank, which on a flat ruled line reads as a bug rather
     than as depth. --k is that magnification, P / (P - z), with z the
     live panel's NET depth once the rank's own recession is counted;
     applied to the offset and the width alike it lands the mark under
     the panel at every value of --fld (119/119 at the left end,
     1151/1151 at the right, measured at 1280). */
  --k: calc(var(--p) / (var(--p) - var(--step) + var(--recede) * (1 - var(--fld))));
  position: absolute;
  top: -1px;
  height: 3px;
  width: calc(var(--pw) * var(--k));
  left: calc(50% + (var(--fk) - 2.5) / var(--pn) * 100% * var(--spread) * var(--k));
  transform: translateX(-50%);
  background: linear-gradient(90deg, transparent, var(--gold) 18%, var(--gold) 82%, transparent);
  opacity: var(--fld);
}

/* ==========================================================================
   NARROWER — the fold survives, the copy does not
   ========================================================================== */

@media (max-width: 1080px) {
  .fld__panel { aspect-ratio: 0.58; }
  .fld__name { font-size: 0.82rem; }
  .fld__face { padding: 0.9rem 0.7rem 0.85rem; }
  /* Under ~160px of projected width a three-line sentence is a block of
     grey noise on a photograph. The name and the picture carry it. */
  .fld__say { display: none; }
}

@media (max-width: 860px) {
  .fld__headrow { grid-template-columns: 1fr; align-items: start; }
  .fld__on { justify-self: start; }
}

/* ==========================================================================
   PHONE — the same object, turned through ninety degrees

   Six panels of a 340px-wide screen would be 50px each, which is not a
   photograph. The fold is not abandoned, it is REORIENTED: the hinges
   become horizontal, the panels become slats, and the rank runs down
   the page instead of across it. Same arithmetic, same opening action,
   same live state — but the margin is a fixed length rather than a
   percentage, because a percentage block margin resolves against the
   INLINE size in CSS and would collapse the wrong axis.
   ========================================================================== */

@media (max-width: 720px) {
  .fld {
    --recede: 120;
    --step: 30;
  }
  .fld__rank {
    --sh: 108px;
    display: block;
    padding-block: 6px;
    transform: translateZ(calc(var(--recede) * -1px * (1 - var(--fld))));
  }
  .fld__panel {
    display: block;
    width: 100%;
    height: var(--sh);
    aspect-ratio: auto;
    margin-inline: 0;
    margin-block: calc((var(--c) - 1) * var(--sh) / 2);
    border-block-width: 1px;
    box-shadow: none;
    transform: rotateX(calc(var(--s) * var(--ang) * -1 + var(--gw) * 6deg));
  }
  .fld__panel:nth-child(odd) { box-shadow: none; }
  .fld__panel.is-live { transform: rotateX(0deg) translateZ(calc(var(--step) * 1px)); }
  .fld__panel::after {
    /* Same rule one axis over: s = -1 tips the TOP edge away here, so
       the shade sits at the top (180deg) and s = +1 mirrors it (0deg). */
    background: linear-gradient(
      calc(90deg - var(--s) * 90deg),
      rgba(0, 0, 0, 0.6),
      rgba(0, 0, 0, 0) 70%
    );
  }
  .fld__plate img { object-position: 50% 38%; }
  .fld__n { top: 0.6rem; left: 0.7rem; }
  .fld__face {
    inset: 0 0 0 auto;
    width: 62%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 0.6rem 0.85rem;
    background: linear-gradient(
      to left,
      rgb(var(--scrim-rgb) / 0.9) 40%,
      rgb(var(--scrim-rgb) / 0) 100%
    );
    text-align: right;
  }
  .fld__name { font-size: 0.9rem; }
  .fld__say { display: grid; }
  .fld__say > span { font-size: 0.72rem; }
  .fld__datum { height: 18px; margin-top: 1rem; }
  .fld__tick {
    /* Along a vertical rank the cursor has nothing horizontal to track,
       so the datum becomes a plain closing rule. */
    display: none;
  }
}

/* ==========================================================================
   REDUCED MOTION — the object arrives open and stays open
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .fld,
  .fld__rank { --fld: 1; }
  .fld__panel,
  .fld__plate,
  .fld__say,
  .fld__n,
  .fld__n::after,
  .fld__name,
  .fld__panel::after { transition-duration: 0.01ms; }
}
