/*
 * THE OBJECTS.
 *
 * Envelope, wax seal and sheet of paper. Each is independently animatable: the
 * render loop writes custom properties and nothing here reaches across to
 * anything else.
 *
 * Two rules hold the whole thing together physically:
 *
 *   1. One light. Key from the upper left, so every highlight faces up-left and
 *      every shadow falls down-right, matching the photograph exactly.
 *   2. Contact shadows are never blurred per-frame. Each shadow is painted once
 *      with a fixed blur and thereafter only translated, scaled and faded —
 *      which is also, conveniently, how a real shadow behaves as its object
 *      rises off a surface.
 */

/* ══════════════════════════════════════════════════════════════ envelope ══ */

.envelope {
  position: absolute;
  left: 50%;
  top: 50%;
  /* --env-w is set per breakpoint by viewport.js; see the note there on why the
     objects recompose rather than scale. */
  width: calc(var(--stage) * var(--env-w, 0.455));
  aspect-ratio: 1.362;
  z-index: 6;
  opacity: var(--env-opacity, 0);
  transform: translate(-50%, -50%)
    translate3d(calc(var(--stage) * var(--env-x, 0)), calc(var(--stage) * var(--env-y, 0)), 0)
    rotate(var(--env-rot, -8.5deg)) scale(var(--env-scale, 1));
  /*
   * The perspective lives here rather than being inherited from the camera.
   * Anything that groups — opacity below 1, a filter, a mask, and notably
   * `will-change: opacity` — flattens preserve-3d in every descendant, and this
   * element animates its opacity. Owning the perspective makes the flap's 3D
   * independent of what any ancestor happens to be doing.
   */
  perspective: calc(var(--stage) * 1.8);
  /* Nothing here is interactive; the story is driven entirely by scroll. */
  pointer-events: none;
}

/*
 * CONTACT SHADOW.
 *
 * A sibling of the envelope, anchored to the spot on the table where the
 * envelope is going to land — not to the envelope itself.
 *
 * Everything about it is driven by one value, `lift`, which is how far off the
 * surface the envelope is: high up it is large, soft, faint and displaced down
 * and to the right of the landing point; on contact it is small, tight, dark and
 * exactly underneath. That single relationship is most of what makes the fall
 * read as weight rather than as an element sliding down the page.
 *
 * The blur is fixed and the element is scaled, so the softness scales with the
 * size for free and nothing has to be re-blurred per frame.
 */
.envelope-shadow {
  position: absolute;
  left: 50%;
  top: 50%;
  width: calc(var(--stage) * var(--env-w, 0.455));
  aspect-ratio: 1.362;
  z-index: 5;
  border-radius: calc(var(--stage) * 0.01);
  /*
   * A filled soft blob, not a box-shadow ring. box-shadow paints outside the
   * (transparent) box only, so mid-fall the shadow rendered as the straight
   * rim of the envelope's FUTURE footprint hanging on the empty table — a
   * ghost outline before its caster arrived. The radial fill makes it a mass
   * of shade that happens to sharpen into a footprint exactly as the envelope
   * lands on top of it.
   */
  background: radial-gradient(
    92% 88% at 50% 50%,
    hsl(var(--shadow-hue) / 0.5) 0%,
    hsl(var(--shadow-hue) / 0.4) 58%,
    hsl(var(--shadow-hue) / 0.16) 82%,
    hsl(var(--shadow-hue) / 0) 100%
  );
  box-shadow: 0 0 calc(var(--stage) * 0.035) calc(var(--stage) * 0.002)
    hsl(var(--shadow-hue) / 0.28);
  transform: translate(-50%, -50%)
    translate3d(
      calc(var(--stage) * var(--env-sh-x, 0)),
      calc(var(--stage) * var(--env-sh-y, 0)),
      0
    )
    rotate(var(--env-sh-rot, -8.5deg)) scale(var(--env-sh-scale, 1));
  opacity: var(--env-sh-opacity, 0);
  pointer-events: none;
}

.envelope__3d {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transform: rotateX(var(--env-tilt-x, 0deg)) rotateY(var(--env-tilt-y, 0deg));
}

/* Interior, seen through the mouth once the flap swings back. Only the top V is
   ever visible (the front panels cover the rest), so the shading concentrates
   there: darkest under the fold at the top edge — the deepest part of the
   pocket — easing as it comes forward to where the sheet stands. */
.envelope__inner {
  position: absolute;
  inset: 0;
  border-radius: calc(var(--stage) * 0.005);
  background:
    linear-gradient(
      to bottom,
      hsl(16 42% 24% / 0.36) 0%,
      hsl(16 40% 27% / 0.16) 22%,
      transparent 52%
    ),
    linear-gradient(var(--light-angle), #a8705c 0%, #bd8674 42%, #cd9884 100%);
}

/* The folded sheet inside the envelope, seen through the mouth and sliding up
   out of it. The element sits between the interior wall and the body panel in
   DOM order, so the occlusion order is physical for free: interior behind it,
   front panels over its sides, the whole of it hidden while the flap is shut
   because the flap's front face covers exactly the mouth's V.

   Two details are load-bearing:

   - `translate`, the 2D property, NOT translate3d. This element lives inside a
     preserve-3d subtree (the flap needs real 3D), and inside preserve-3d a
     sibling carrying a 3D transform is depth-sorted rather than painted in DOM
     order — translate3d put the sheet in front of the envelope's front panel,
     where it hung over the body as a ghost rectangle instead of standing
     inside it. (Same trap as the seal — see the notes there.)

   - At rest its edge sits exactly at the envelope's top edge: a sealed
     envelope shows no paper. The mouth does the revealing — as the flap swings
     back you see the sheet standing inside, already there, before it moves. */
.envelope__paper-peek {
  position: absolute;
  left: 5.5%;
  top: 3.8%;
  width: 89%;
  /* Sized so the sheet stays INSIDE the envelope's silhouette at rest — WITH
     its lean, computed rather than eyeballed this time: the corners sit half
     the sheet's width (60.6% of the envelope's height, the envelope being
     1.362:1) from the centreline, so a 3.2deg lean swings them 60.6% x
     sin(3.2deg) = 3.38% of the envelope's height past the box on each side.
     3.8% top + 92% height leaves ~0.4% true margin at both edges. The slide's
     travel (44% of this height) still keeps the sheet's foot below the mouth's
     apex, so the mouth never shows empty behind it. */
  height: 92%;
  /* A slight counter-lean against the envelope's cant, as in the reference:
     paper pulled from a pocket never sits perfectly square in it. */
  rotate: -3.2deg;
  transform-origin: 50% 90%;
  translate: 0 calc(var(--peek-y, 0) * -1%);
  opacity: var(--peek-opacity, 0);
}

/* The sheet's shadow into the pocket and onto whatever is behind it. A separate
   element because the paper's deckle is a clip-path and a clip takes box-shadow
   with it; this sits behind, unclipped, its top tucked below the deckle line so
   its own straight edge can never show. */
.peek-shadow {
  position: absolute;
  /* Inset far enough that the blurred, offset shadow can never reach past the
     envelope's silhouette — it was printing a dark seam just beyond the sealed
     bottom edge. */
  inset: 4.5% 3% 4.5% 4%;
  border-radius: calc(var(--stage) * 0.003);
  box-shadow:
    calc(var(--stage) * 0.004) calc(var(--stage) * 0.006) calc(var(--stage) * 0.014)
      hsl(var(--shadow-hue) / 0.42);
}

/* The paper itself: cotton rag with a deckled head. The ragged top is a
   clip-path polygon GENERATED in main.js from a seeded random walk — torn
   fibre needs roughness at more than one scale (a slow undulation, fine
   tears riding it, the odd deeper nick), and every hand-authored list of
   points settled into a rhythm the eye read as scalloped trim. Seeded, so it
   is the same tear on every load. The bright fringe gradient under the edge
   stands in for the torn fibres catching the key light. */
.peek-sheet {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(
      to bottom,
      hsl(42 80% 99% / 0.95) 0%,
      hsl(42 70% 97% / 0.5) 0.9%,
      hsl(42 60% 96% / 0) 2.1%
    ),
    url('../../assets/grain.png'),
    linear-gradient(
      to bottom,
      hsl(24 30% 40% / 0) 84%,
      hsl(24 30% 36% / 0.1) 100%
    ),
    linear-gradient(var(--light-angle), #fffdf7 0%, #faf3e6 46%, #f0e6d4 100%);
  background-size: 100% 100%, 84px, 100% 100%, 100% 100%;
  background-blend-mode: normal, soft-light, normal, normal;
  /* clip-path set inline by main.js (see buildDeckle) */
}

/* No drop-shadow filter here, for the same reason as the seal: a filter inside
   a rescaling subtree is recomputed every frame. The contact shadow element
   below the envelope does this job, and does it more accurately. */
.envelope__body {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* Paper tooth. The reference envelope is a soft, slightly mottled stock — flat
   vector gradients alone read as plastic. */
.envelope__grain {
  position: absolute;
  inset: 0;
  border-radius: calc(var(--stage) * 0.005);
  background-image: url('../../assets/grain.png');
  background-size: 96px;
  mix-blend-mode: multiply;
  opacity: 0.09;
  /*
   * Clipped to the envelope's FRONT (the same shape the body SVG paints, with
   * the mouth's V left open). This layer used to cover the whole box, which
   * meant it also multiplied over the sheet standing in the mouth — and since
   * the risen part of the sheet escapes the box while the mouth part does not,
   * the box's edge printed a hard tone boundary straight across the paper.
   * The sheet carries its own grain; each surface owns its own skin.
   */
  clip-path: polygon(
    0% 0.82%, 1% 0.82%, 50% 54.77%, 99% 0.82%, 100% 0.82%,
    100% 100%, 0% 100%
  );
}

/*
 * The room's leaf shadow, falling across the envelope.
 *
 * This layer was cut once for performance and a critic caught it immediately:
 * with the dapple only beneath the objects, the shadow pattern ran across the
 * table and stopped dead at the envelope's silhouette, so the objects and the
 * photograph were lit by two different lights. It is back — but as a small
 * overlay on the object itself rather than the full-stage blend layer it used to
 * be, which is a few hundred square pixels instead of two million.
 */
.envelope__dapple,
.paper__dapple {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url('../../assets/dapple.webp');
  background-size: 210% 210%;
  background-position: 22% 18%;
  mix-blend-mode: multiply;
  /* --sheet-skin is written on the paper element only, so the envelope's copy
     of this rule resolves it to 1 and is untouched; the sheet's leaf shadow is
     laid out against the sheet's box, which changes size at the takeover's
     match cut, so it crosses the cut absent like the rest of the skin. */
  opacity: calc(var(--dapple-opacity) * 1.5 * var(--dapple-amount, 1) * var(--sheet-skin, 1));
  animation: dapple-drift 54s cubic-bezier(0.45, 0, 0.55, 1) infinite alternate;
}

/* The envelope's leaf shadow falls on the envelope's FACE, not into its mouth
   or across the sheet standing in it — same clip and same reasoning as the
   grain above. */
.envelope__dapple {
  clip-path: polygon(
    0% 0.82%, 1% 0.82%, 50% 54.77%, 99% 0.82%, 100% 0.82%,
    100% 100%, 0% 100%
  );
}

/* ── flap ─────────────────────────────────────────────────────────────────── */

.envelope__flap {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 55.04%; /* 404 / 734, matching the body SVG's geometry */
  transform-origin: 50% 0%;
  transform: rotateX(var(--flap-rot, 0deg));
  transform-style: preserve-3d;
}

.envelope__flap-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

.envelope__flap-face svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* The flap's shadow on the envelope below it is drawn inside the SVG, not with
   a filter — see the note on .envelope__body. */

/*
 * The reverse of the flap.
 *
 * Mirrored on Y, not on X — and that distinction is the whole difference between
 * an envelope and a shape.
 *
 * `rotateX(180deg)` is the usual way to build the back of a two-sided card, and
 * it works because a card is a rectangle, symmetric about its own centre. This
 * face is a TRIANGLE. Flipping it vertically moves its apex to the opposite end,
 * and then the flap's own rotation about the top edge flips it back again — so
 * the open flap rendered apex-down, hinged at its point, reading as a detached
 * triangle sitting on a rectangle rather than a flap folded back.
 *
 * rotateY leaves the silhouette alone (the triangle is symmetric left-to-right)
 * and still turns the face away from the viewer, which is all it needs to do.
 */
.envelope__flap-face--back {
  transform: rotateY(180deg);
}

/* ── the line written on the envelope ─────────────────────────────────────── */

.envelope__note {
  position: absolute;
  left: 50%;
  top: 27%;
  width: 60%;
  margin: 0;
  translate: -46% 0;
  rotate: -3.2deg;
  font-family: var(--font-fine);
  font-size: var(--type-envelope-note);
  line-height: 1.5;
  letter-spacing: 0.045em;
  /* Warm grey-brown, not near-black: in the reference this is a low-contrast
     hairline. Legible, but it should not compete with the wax. */
  color: hsl(14 26% 38% / 0.82);
  text-align: center;
  white-space: pre-line;
  /* Ink absorbed into the stock rather than printed on top of it. */
  mix-blend-mode: multiply;
}

/* ══════════════════════════════════════════════════════════════ wax seal ══ */

.seal {
  position: absolute;
  left: 50.5%;
  top: 55.5%;
  /* Sized against the envelope, not the stage, so it keeps its proportion to the
     thing it is holding shut at every breakpoint. */
  /* 21.5% of the envelope's width. Measured against frame2, where the wax is
     19.5%; the earlier 27% made it a saucer. */
  width: calc(var(--stage) * var(--env-w, 0.455) * 0.215);
  aspect-ratio: 1;
  translate: -50% -50%;
  /*
   * Deliberately no translateZ.
   *
   * Inside a preserve-3d parent the browser paints children in Z order, not DOM
   * order. A few thousandths of a stage of lift here put the envelope's piece of
   * wax in front of the light overlay while the flap's piece — nested one level
   * deeper, at z=0 — stayed behind it. The result was one piece of the same seal
   * being shaded and the other not, which read as a dark wedge cut out of it.
   */
  opacity: var(--seal-opacity, 1);
}

.seal svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/*
 * The flap's piece of the seal.
 *
 * It sits inside .envelope__flap and is positioned so its centre lands exactly
 * where the body piece's centre is: the seal sits 55.5% down the envelope, and
 * the flap is the top 55.04% of it, so in the flap's own coordinates that is
 * 100.84% — just past its point.
 *
 * backface-visibility is doing real work. As the flap swings past vertical this
 * piece turns away with it and stops being drawn, which is exactly right — once
 * the flap is folded back its wax is on the far side, and what remains on the
 * envelope is the piece that stayed behind.
 */
.seal--flap {
  top: 100.84%;
  /*
   * Its visibility is driven explicitly from the flap's angle rather than by
   * backface-visibility.
   *
   * The declarative version simply did not paint this element — it has no 3D
   * transform of its own, only an inherited one, and Chrome culled it as
   * back-facing while the flap was barely tilted. The symptom was subtle and
   * misleading: the wax looked fine until the fracture opened, because the
   * unbroken layer underneath was covering the gap, and then a dark wedge
   * appeared that looked like a shading bug rather than a missing object.
   */
  opacity: calc(var(--seal-opacity, 1) * var(--seal-flap-visible, 1));
  /* Depth compensation, computed per frame — see the note in main.js. */
  transform: translateZ(var(--seal-flap-z, 0px));
}

/* The intact seal, revealed beneath the two pieces until the fracture opens. */
.seal__whole {
  opacity: calc(1 - var(--seal-split, 0));
}

.seal__piece {
  transform-box: fill-box;
  transform-origin: center;
}

/* The fracture's depth appears with the crack, not before it. */
.seal__edge {
  opacity: var(--seal-crack, 0);
}

/*
 * The pieces barely move.
 *
 * Wax stuck to paper does not fly apart — it cracks, the two sides shift a
 * millimetre, and a dark fissure opens between them. An earlier version pushed
 * them seven percent apart and it read as a seal that had been cut in half and
 * pulled, with a black chasm down the middle. Restraint is the whole effect
 * here: the crack should be something you notice, not something you watch.
 *
 * They part in the direction the paper is about to move: the flap's piece lifts,
 * the envelope's settles.
 */
.seal__piece--flap {
  transform: translate(calc(var(--seal-split, 0) * -0.6%), calc(var(--seal-split, 0) * -2.8%))
    rotate(calc(var(--seal-split, 0) * -2.6deg));
}

.seal__piece--body {
  transform: translate(calc(var(--seal-split, 0) * 0.5%), calc(var(--seal-split, 0) * 1.4%))
    rotate(calc(var(--seal-split, 0) * 1.8deg));
}

/*
 * The fissure: a dark line that appears before anything moves. That pause
 * between "it has cracked" and "it comes apart" is the whole beat.
 *
 * It fades out as the halves separate, because by then the break is a real gap
 * with real shadow in it and a drawn line lying across that gap is a drawing.
 */
.seal__crack {
  opacity: calc(var(--seal-crack, 0) * (1 - var(--seal-split, 0)));
}

/*
 * The monogram, pressed into the wax.
 *
 * An impression is read almost entirely from its lit edge: a dark trough with a
 * bright rim on the side facing the light. Two offset copies, and both of them
 * have to be committed — the first version was too faint to read at all, which
 * is a strange thing to do to the one piece of personalisation in the piece.
 */
.seal__mono {
  font-family: var(--font-fine);
  font-size: 32px;
  /* Interlocked, like a real cypher, rather than two initials standing apart. */
  letter-spacing: -0.09em;
}

/* Pressed into dark wax: a deep trough, and a lit edge on the side the light
   comes from. The lit copy does most of the reading. */
.seal__mono--shadow {
  fill: #34120a;
  fill-opacity: 0.85;
}

.seal__mono--light {
  fill: #f0a98a;
  fill-opacity: 0.9;
}

.seal__shard {
  transform-box: fill-box;
  transform-origin: center;
  opacity: calc(var(--seal-shards, 0) * 0.95);
  transform: translate(
      calc(var(--seal-shards, 0) * var(--sx) * 1%),
      calc(var(--seal-shards, 0) * var(--sy) * 1%)
    )
    rotate(calc(var(--seal-shards, 0) * var(--sr) * 1deg));
}

/* ═══════════════════════════════════════════════════════════════════ paper ══ */

.paper {
  position: absolute;
  left: 50%;
  top: 50%;
  /* Sized so the unfolded sheet still clears the viewport once the camera has
     pushed in on it, and so the writing is legible at the size the device
     actually is. A letter that runs off the bottom of the frame — or that lands
     at nine pixels on a phone — is not a letter you can read. */
  width: calc(var(--stage) * var(--paper-w, 0.4));
  /*
   * The sheet is as long as the letter needs.
   *
   * Its height is measured from the laid-out text (see letter.js) rather than
   * fixed by a ratio, which is the only honest way round for a letter whose
   * length the author is invited to change. The fallback keeps the CSS sensible
   * before script has run.
   */
  height: var(--paper-h, calc(var(--stage) * var(--paper-w, 0.4) / var(--paper-aspect, 0.34)));
  z-index: 7;
  opacity: var(--paper-opacity, 0);
  transform: translate(-50%, -50%)
    translate3d(calc(var(--stage) * var(--paper-x, 0)), calc(var(--stage) * var(--paper-y, 0)), 0)
    rotate(var(--paper-rot, -8.5deg)) scale(var(--paper-scale, 1));
  perspective: calc(var(--stage) * 1.8); /* see the note on .envelope */
  pointer-events: none;
}

.paper__shadow {
  position: absolute;
  inset: 3% 2%;
  border-radius: calc(var(--stage) * 0.004);
  box-shadow: 0 calc(var(--stage) * 0.01) calc(var(--stage) * 0.026)
    calc(var(--stage) * -0.005) hsl(var(--shadow-hue) / 0.5);
  transform: translate3d(var(--paper-sh-x, 0px), var(--paper-sh-y, 0px), 0)
    scale(var(--paper-sh-scale, 1));
  opacity: var(--paper-sh-opacity, 0.85);
}

.paper__sheet {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
}

/* Three panels, folded in thirds like a real letter. The middle one never moves;
   the other two rotate about the creases they share with it. */
.paper__panel {
  position: absolute;
  left: 0;
  width: 100%;
  height: 33.34%;
  background: linear-gradient(var(--light-angle), #fdf8f0 0%, var(--c-paper) 44%, #eee3d5 100%);
}

.paper__panel--top {
  top: 0;
  transform-origin: 50% 100%;
  transform: rotateX(calc(var(--fold-top, 0) * 172deg));
}

.paper__panel--mid {
  top: 33.33%;
  /* Creases inside; outside, the edge of the folded stack — while the other two
     panels are still folded over this one, this is the only thing telling you
     there is more paper than you can see. */
  box-shadow:
    inset 0 1px 0 hsl(var(--shadow-hue) / 0.09),
    inset 0 -1px 0 hsl(var(--shadow-hue) / 0.09),
    0 calc(var(--stage) * 0.0015) calc(var(--stage) * 0.004) hsl(var(--shadow-hue) / calc(0.3 * max(var(--fold-top, 0), var(--fold-bot, 0))));
}

.paper__panel--bot {
  top: 66.66%;
  transform-origin: 50% 0%;
  transform: rotateX(calc(var(--fold-bot, 0) * -172deg));
}

/* Shading that deepens as a panel turns away from the light. */
.paper__shade {
  position: absolute;
  inset: 0;
  background: hsl(var(--shadow-hue) / 0.5);
  opacity: var(--shade, 0);
}

.paper__panel--top .paper__shade {
  --shade: calc(var(--fold-top, 0) * 0.55);
}

.paper__panel--bot .paper__shade {
  --shade: calc(var(--fold-bot, 0) * 0.55);
}

/*
 * Light drawn OVER the writing. This is the detail that decides whether the
 * message looks written on the paper or floated above it: the raking light and
 * the residual crease shadows fall across the ink, not behind it.
 */
.paper__light {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background-image:
    /* residual creases, where the sheet was folded in three */
    linear-gradient(
      to bottom,
      transparent 32.2%,
      hsl(var(--shadow-hue) / 0.1) 33.3%,
      transparent 34.4%,
      transparent 65.6%,
      hsl(var(--shadow-hue) / 0.1) 66.6%,
      transparent 67.7%
    ),
    /* the key light raking across the sheet, upper-left to lower-right */
      linear-gradient(
        var(--light-angle),
        hsl(40 80% 96% / 0.55) 0%,
        transparent 38%,
        hsl(var(--shadow-hue) / 0.13) 100%
      );
  background-size: 100% 100%;
  mix-blend-mode: multiply;
  /* Only once the sheet is actually flat. These overlays span the whole sheet,
     so drawing them during the fold paints a full rectangle of paper over
     panels that are still edge-on — the sheet looks unfolded before it is.
     The --sheet-skin term dims (never removes) the raking light across the
     takeover's match cut: the gradient spans the whole sheet, and the sheet's
     height changes at the cut, so the light's landing would shift visibly at
     full strength. A floor of 0.3 keeps the paper lit while it happens. */
  opacity: calc(
    (1 - max(var(--fold-top, 0), var(--fold-bot, 0))) * (0.3 + 0.7 * var(--sheet-skin, 1))
  );
}

/*
 * Paper tooth.
 *
 * Kept as its own layer rather than folded into .paper__light, because the grain
 * tile is mid-grey and multiplying by mid-grey halves the brightness of
 * everything under it — which turned a warm cream sheet into grey television
 * static. Overlay leaves the value alone and only perturbs it.
 */
.paper__tooth {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  background-image: url('../../assets/grain.png');
  background-size: 82px;
  mix-blend-mode: overlay;
  /*
   * --sheet-skin breathes the grain out during the takeover push and back in
   * once the letter is the page. The tile is a fixed 82 CSS px, so a camera
   * pushing from 1.02x to page scale magnifies it two-and-a-half times and the
   * match cut would snap it back — a texture density jump at the exact moment
   * nothing else changes. Absent across the cut, it has nothing to betray.
   */
  opacity: calc((1 - max(var(--fold-top, 0), var(--fold-bot, 0))) * 0.16 * var(--sheet-skin, 1));
}
