/* ============================================================
   About Jess — approved alpha-polaroid collage, turbulent washes,
   and a restrained glass veil behind the editorial copy.
   ============================================================ */

.about {
  min-height: 880px;
  overflow: clip;
  /* No background and no isolation. The .content-sheet paper runs unbroken
     through this section, and the wash/grain layers blend against that paper.
     An isolated stacking context with nothing painted in it gives every
     mix-blend-mode child a transparent backdrop to blend with, which turns the
     soft-light grain into a flat grey veil. .content-sheet already forms a
     stacking context (position:relative + z-index), so the blending stays
     contained to the narrative sheet either way. */
  padding-block: clamp(86px, 9vw, 126px);

  /* ---------- The static field: prismatic studio light ----------
     Fifth of the six chapters, and the most materially tactile one, because it
     is the section that represents Jess. THE COLLAGE IS MADE OF PAPER; THE
     ENVIRONMENT IS NOT. Everything below is digital light in a quiet room, at
     roughly half the hero's intensity, and the collage stays crisp on top of it.

     Why this is a section background and not a sixth .about-wash. The five
     existing .about-wash--* elements are collage ART, not a surface tone: they
     live inside .about-collage, which is an aspect-ratio 790/920 box in the left
     grid column with `isolation: isolate`, and their translucent source alpha
     is composited locally,
     and each is displaced by one of the three #about-wash-a/b/c turbulence
     filters so it reads as wet pigment under the polaroids. Nothing in that set
     can become a section-wide surface without pulling it out of the collage and
     out of its isolation, which would be a rebuild rather than a polish, and
     stretching a multiply layer over the copy panel is exactly the kind of thing
     that eats the contrast this pass has to protect. So they are left alone --
     they stay LOCAL, which is the point of them -- and the surface tone is
     painted here instead, in the same coral-rose hue family they already carry,
     so it reads as the collage's own light spilling onto the page.

     This is the element's own background, below every descendant, so it does not
     go anywhere near .about-grain's soft-light pass. If anything it makes that
     safer: the grain now has one more opaque thing painted beneath it.

     THE SEAM LIP RULE. Each chapter join is a bare full-width lip of veiled
     paper (--seam-lip, 16px at 390 rising to 26px at 2552) with no ragged edge
     left to hide a step, so every layer here reaches ZERO alpha before the
     section's own top and bottom edges. The cream lift does it with an explicit
     0%/100% stop; the three radials do it by having their falloff complete well
     inside the box (worked out against About's 880px minimum height: the lilac
     field is zero above 6% and below 74%, a 299px ramp, and the two collage
     reflections are zero for 190px+ before either edge). Zero stops are spelled
     rgba(r,g,b,0) and never `transparent`, which is rgba(0,0,0,0) and darkens an
     unpremultiplied fade on its way out. tools/about-atmosphere.mjs measures
     both edges directly rather than trusting this arithmetic.

     CONTRAST. --about-lilac is the one colour here that is not a shared --atmos
     token, and the reason is measured. It is the field the body copy sits on,
     and .about-pull-line ran p5 4.52 against a 4.5:1 bar BEFORE this pass: two
     hundredths of headroom. Relative luminance is 71.5% green, so a saturated
     violet over cream costs contrast fast (plain --atmos-violet-2 measures -6.5
     luma on this backdrop). --about-lilac instead holds R and G AT the veiled
     paper's own levels (247 / 231) and lifts only B, so it ADDS BLUE rather than
     SUBTRACTING YELLOW: measured +1.3 luma, i.e. it pays for itself. It still
     reads as a cool blush-violet next to the warm cream around it, which is the
     field the editorial glass is here to refract. */
  --about-lilac: rgba(247, 231, 255, 0.5);
  --about-lilac-0: rgba(247, 231, 255, 0);

  background-image:
    /* Blush-violet field behind the editorial panel. Its core sits at the
       panel's upper right and it falls away across the panel, so the glass gets
       a diagonal hue RAMP to refract instead of a second flat colour. */
    radial-gradient(58% 46% at 88% 40%, var(--about-lilac), var(--about-lilac-0) 74%),
    /* Blush and coral reflecting off the collage. Deliberately local to the left
       column: the blooms are local, the light is global. */
    radial-gradient(42% 30% at 22% 56%, var(--atmos-coral-2), var(--atmos-coral-0) 72%),
    radial-gradient(30% 20% at 34% 34%, var(--atmos-pink-1), var(--atmos-pink-0) 70%),
    /* The luminous cream field everything else sits in. */
    linear-gradient(180deg,
      var(--atmos-cream-0) 0%,
      var(--atmos-cream-2) 22%,
      var(--atmos-cream-2) 74%,
      var(--atmos-cream-0) 100%);
}

/* ============================================================
   The one ambient behaviour: LIGHT MOVING, not pigment drifting
   ============================================================
   Approach gathers, Ways holds surface tension, Who floats. Here the colour
   stays where it is and the light over it changes, the way a studio changes
   through an afternoon. That distinction is the whole reason this section does
   not get drifting blobs: other chapters already own that, and About is about
   recognition and trust, so the movement has to be barely detectable.

   Both layers are compositor-only (transform + opacity), on the prime 19/23/29s
   --dur-ambient* periods so the loops never re-align and pulse. */

/* The vertical mask is the seam-lip rule made structural rather than arithmetic.
   These shafts MOVE, so their own falloff cannot be trusted to land on zero at
   the section edges at every breakpoint; a fixed 300px envelope can. 300px is 11
   to 18 times the lip, and About is never shorter than its 880px min-height, so
   the two ramps never meet. Spelled rgba(0,0,0,0) rather than `transparent` to
   match the house rule, though for an alpha mask only the alpha is read. */
.about-light {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0) 0, #000 300px, #000 calc(100% - 300px), rgba(0, 0, 0, 0) 100%);
  mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0) 0, #000 300px, #000 calc(100% - 300px), rgba(0, 0, 0, 0) 100%);
}

.about-light__warm,
.about-light__cool {
  position: absolute;
  inset: 0;
  display: block;
  will-change: transform, opacity;
}

/* Soft amber daylight entering ONE upper corner. A room has one window. It is
   the left corner because that is the side the collage is on, so the warm light
   is the same light the coral and blush reflections below it are bouncing. */
.about-light__warm {
  background:
    radial-gradient(64% 56% at 2% -8%, var(--atmos-amber-3), var(--atmos-amber-1) 38%, var(--atmos-amber-0) 72%),
    radial-gradient(46% 38% at 14% 4%, var(--atmos-cream-3), var(--atmos-cream-0) 70%);
  /* Base state = the MIDPOINT of the loop, not an endpoint. base.css collapses
     animation-duration to 0.01ms with iteration-count 1 under reduced motion, and
     with fill-mode none what survives is this declaration. */
  transform: translate3d(0, 0, 0) scale(1.022);
  opacity: 0.92;
  animation: about-sun var(--dur-ambient-slow) var(--ease-ambient) infinite;
}

/* The cooler light on the opposite flank: the faint violet shadow behind the
   copy side. Its core is off-canvas at 106%, so only the falloff crosses the
   panel. That is deliberate -- an on-canvas core would give the panel a second
   flat colour, and what makes glass read as glass is the RAMP.

   It is parked HIGH, at 30%, and reaches zero by 63% of the section, and that
   is a contrast decision rather than a compositional one. --atmos-violet-2 is
   real pigment and costs about -6.5 luma at full alpha; at 58% its tail sat
   across the body column and dragged .about-pull-line from p5 4.52 to 4.29,
   under the 4.5:1 bar (measured, tools/ramp-contrast.mjs). At 30% the violet
   falls behind the HEADING instead -- 50px serif, so a 3:1 bar with room to
   spare -- and the body and pull line below it are lit by the luminance-neutral
   --about-lilac alone. The panel loses nothing by this: violet at the top and
   lilac at the bottom is MORE hue structure across its footprint, not less. */
.about-light__cool {
  background: radial-gradient(44% 46% at 106% 30%, var(--atmos-violet-2), var(--atmos-violet-0) 72%);
  transform: translate3d(0, 0, 0) scale(1.02);
  opacity: 0.92;
  animation: about-counter-light var(--dur-ambient) var(--ease-ambient) infinite;
}

/* Travel stays inside --atmos-drift (18px peak-to-peak), scale inside
   --atmos-scale (1.045) and the opacity swing inside --atmos-fade (0.12). The
   two run on different periods and in opposite directions, so the warm and cool
   sides of the room never brighten together. */
@keyframes about-sun {
  0%, 100% { transform: translate3d(-8px, -5px, 0) scale(1); opacity: 0.86; }
  50%      { transform: translate3d(8px, 6px, 0) scale(1.045); opacity: 0.98; }
}

@keyframes about-counter-light {
  0%, 100% { transform: translate3d(6px, 5px, 0) scale(1.04); opacity: 0.98; }
  50%      { transform: translate3d(-6px, -4px, 0) scale(1); opacity: 0.86; }
}

/* ---------- The signature move: very subtle caustics ----------
   Sunlight through coloured glass, or through a jar of cloudy paint water. One
   restrained pool, not a texture over the whole section.

   It is a data-URI SVG and not an element filter on purpose. An SVG filter on a
   full-section element re-runs whenever that element is transformed; a data URI
   is rasterised once by the image pipeline and cached, so the loop below stays
   compositor-only. (Trap 4 in ENGINEERING-NOTES -- a relative url() inside a
   custom property resolving against the stylesheet that USES it -- does not
   apply to a data URI, but it is still spelled out at its call site rather than
   tokenised, for the same reason every other url() on this project is.)

   The recipe: fractalNoise -> a luminance-to-alpha feColorMatrix -> a ridged
   feFuncA table, where the 0 / 0.9 / 0 / 0.55 crossings are what turn smooth
   noise into a filament NETWORK rather than clouds -> a 5px blur -> flooded warm
   cream and composited into that alpha. Three alternatives were rendered and
   compared over the real field colour before this one was picked; the sheet is
   at output/audit/caustic/tuned.png.

   The radial mask keeps it a POOL of light instead of wallpaper, and doubles as
   this layer's seam-lip envelope: it reaches zero at 9.9% and 82.1% of the
   section height, so at the 880px minimum there are 87px and 158px of clear
   paper before either lip and the drift moves it by at most 5px.

   Round 3 quietened this pool from 0.2 to 0.15 opacity to make room for a
   caustic line mesh layered on top of it. Round 4 removed that mesh outright
   -- Daniel rejected it on sight -- and replaced it with a WebGL2 metaball
   field that sat BELOW this pool (z-index 1, see its own comment) rather than
   above it, so the two never competed for the same pixels. The ambient
   transplant pass that followed kept that same tier assignment when it
   replaced the metaball field with .about-streams, now a broad ink-ribbon
   shader (see that layer's own comment). LEFT AT 0.15 rather than raised back to
   0.2: whichever layer sits underneath is now the section's own low-frequency
   hue structure, and tools/about-atmosphere.mjs's addsStructure metric (this
   pool's contribution alone) is unaffected by changing a layer underneath it,
   so there is no measurement pushing this number to move again. */
.about-caustic {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1000' height='700'%3E%3Cfilter id='c' x='0' y='0' width='100%' height='100%' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.0026 0.0031' numOctaves='3' seed='17' result='n'/%3E%3CfeColorMatrix in='n' type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.9 0.7 0.4 0 -0.7' result='m'/%3E%3CfeComponentTransfer in='m' result='r'%3E%3CfeFuncA type='table' tableValues='0 0 0.9 0 0.55 0 0'/%3E%3C/feComponentTransfer%3E%3CfeGaussianBlur in='r' stdDeviation='5' result='s'/%3E%3CfeFlood flood-color='%23fff2cc' result='t'/%3E%3CfeComposite in='t' in2='s' operator='in'/%3E%3C/filter%3E%3Crect width='1000' height='700' filter='url(%23c)'/%3E%3C/svg%3E") 8% 30% / clamp(860px, 78vw, 1560px) auto no-repeat;
  opacity: 0.15;
  -webkit-mask-image: radial-gradient(72% 42% at 40% 46%, #000 0%, #000 26%, rgba(0, 0, 0, 0) 86%);
  mask-image: radial-gradient(72% 42% at 40% 46%, #000 0%, #000 26%, rgba(0, 0, 0, 0) 86%);
  transform: translate3d(0, 0, 0) scale(1.02);
  will-change: transform;
  animation: about-caustic-drift var(--dur-ambient-fast) var(--ease-ambient) infinite;
}

@keyframes about-caustic-drift {
  0%, 100% { transform: translate3d(-5px, 3px, 0) scale(1); }
  50%      { transform: translate3d(5px, -3px, 0) scale(1.04); }
}

/* ---------- Flowing ink ribbons: the ambient field ----------
   A low-resolution WebGL1 canvas paints five broad, broken brush currents in
   About's violet/blush/coral/amber/teal palette. It replaced the costly Canvas
   2D curl-noise particle field after Firefox profiling isolated that renderer
   as the section's bottleneck. The old renderer remains only as a graceful
   WebGL fallback and an explicit ?aboutstream=particles rollback hook.

   Same tier as the layer it replaces: z-index 1, the same as .about-light and
   below .about-caustic (2), because this is the base wash the room's more
   literal light and pool sit on top of, not a foreground detail competing
   with them. The interactive dot field that used to sit above this at z-index
   3 is removed as part of the same pass -- see the comment where it used to
   be, just below this rule -- because it duplicated this field rather than
   earning a place next to it.

   Not blended: the canvas paints its own alpha directly (source-over /
   destination-out only), and a mix-blend-mode on a full-section layer is the
   exact trap that has already turned three earlier layers into a flat grey
   veil (trap 1, ENGINEERING-NOTES).

   The renderer pauses outside the viewport, restores after WebGL context loss,
   and draws one still for reduced motion. The section's CSS background-image
   gradients above are already a complete room if neither renderer is usable. */
.about-streams {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* ---------- The interactive dot field: removed ----------
   .about-dots used to sit here: a sparse, low-contrast scatter that gave way
   to the pointer and settled back, with no ambient loop of its own because
   About's only autonomous motion was the shifting light above it. The ink
   ribbon field (.about-streams) is now a second ambient identity in the same
   footprint and the same accent colours, and a side-by-side screenshot at
   2560x1440 (output/about-streams/dots-on.png vs dots-off.png) showed the
   dots reading as disconnected polka dots over the new field rather than
   earning a place next to it. Removed outright -- canvas, JS and probe, not
   tuned again -- as part of the same transplant that replaced the round 4
   metaball gradient. Git has the history. */

.about-filter-defs {
  position: absolute;
  pointer-events: none;
}

.about-grain {
  position: absolute;
  inset: 0;
  z-index: 30;
  pointer-events: none;
  background: url('../../assets/img/grain.png') center / 180px repeat;
  mix-blend-mode: soft-light;
  /* Was a hardcoded 0.4, the token's value when this was written. On
     2026-07-27 --grain-opacity went to 0.30 to match the Cell Drift lab, and
     this copy did not follow, so About and the hero were left grainier than
     the four sections that read the token. Grain is the SHARED sitewide
     finish; a local literal here was a stale duplicate, not a decision. */
  opacity: var(--grain-opacity);
}

.about-layout {
  position: relative;
  z-index: 5;
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(380px, 0.92fr);
  gap: clamp(48px, 6vw, 92px);
  align-items: center;
}

/* Ultrawide breathing room. Daniel 2026-07-29: "about section could probably
   space a bit more", said in the same breath as Who being too wide, so the two
   sections were converging from opposite extremes. Measured at 2552 before the
   change: About occupied 1240px, 48.6% of the viewport, with 51.4% of it bare
   paper - collage 609px, panel 431px, 122px between them - while Who ran the
   full 2552. The composition was not cramped so much as marooned.

   This raises the cap for THIS SECTION ONLY, from the shared 1240 to 1440, and
   opens the column gap with it. At 2552 that takes the collage to 726 and the
   panel to 514, i.e. both grow by about 19%, and the gap 92 -> 112.

   WHY 1440 AND NOT MORE. About's copy panel is the last long-form reading on
   the page and its measure is already near the top of what is comfortable; the
   extra width goes mostly to the collage, which is pictures and can take it.
   Wider than this and the panel's line length starts working against the
   reading, which is a worse trade than a little bare paper.

   Starts at 1600px, the same breakpoint Who's cap uses, so the two sections
   change together rather than at different widths on the way up. */
@media (min-width: 1600px) {
  .about-layout {
    max-width: 1440px;
    gap: clamp(92px, 5vw, 112px);
  }
}

/* ---------- Collage ---------- */

/* The collage box is a COORDINATE SYSTEM, not a frame: every card is absolutely
   positioned in percentages of it, and cards have always been allowed to bleed
   past its edges (materials sat at left -4%, studio's bottom reached 105.5%).

   The aspect ratio moved from 790/720 to 790/920 on 2026-07-27 (round 7) to
   absorb --hands going from 56% to 84% width. That extra 28 points of width is
   28 points of HEIGHT too, and the old box could not hold it: at 56% the hands
   card was 61.6% of the box's height, at 84% it is 75.5%, which left the other
   two cards nowhere to go but off the bottom.

   IT COSTS NOTHING AT DESKTOP, which is why this was the right lever rather
   than shrinking the other two cards further. The two grid columns are
   align-items: center and the row's height is whichever is taller: at 2560 the
   copy panel is 765px against the collage's 555, and at 1440 it is 760 against
   558. Growing the collage to 709 / 713 still leaves the panel taller, so the
   section's own height does not move at either width -- measured after the
   change, not assumed: 1018 and 1012 before and after.
   Below 820 the layout stacks and the collage's height IS its own, so the ratio
   was kept as small as the composition would take: it adds about 95px at 390,
   not the ~230 a box tall enough to hold every card comfortably would have. */
.about-collage {
  position: relative;
  min-width: 0;
  aspect-ratio: 790 / 920;
  isolation: isolate;
}

.about-decor,
.about-wash,
.about-polaroid {
  position: absolute;
}

.about-decor {
  z-index: 1;
  pointer-events: none;
}

/* Re-placed with the cards on 2026-07-27. It used to sit centred behind the top
   of the collage, which worked while --hands was 56% wide and sat off to the
   right. At 84% the hands card lands square on top of it, and because that
   card's photograph is TILTED INSIDE ITS OWN SQUARE BOX the box's four corners
   are transparent -- so the butterfly's two wings showed through the top-left
   and top-right corner triangles as a matched pair of shapes poking out either
   side of the card. It read as a mistake rather than as a paper offcut. Moved
   left and down so one wing sits in open paper beside the cards and the rest
   goes behind them, which is the asymmetry the original had by luck. */
.about-decor--butterfly {
  top: 9%;
  left: -16%;
  width: 72%;
  opacity: 0.42;
}

.about-decor--bulb {
  top: 2%;
  left: 0;
  width: 21%;
  opacity: 0.8;
  transform: rotate(-7deg);
}

.about-wash {
  z-index: 2;
  pointer-events: none;
  /* The collage is deliberately isolated but deliberately transparent so its
     paper and ambient light remain visible. `multiply` therefore had no
     opaque local backdrop across most of each wash and fell back to the source
     colour there. Normal alpha compositing is the stable equivalent for that
     real backdrop, and keeps the approved translucent SVG-filtered pigments
     without opening the isolated group or painting an opaque rectangle behind
     the collage. tools/visual-guard-parity.mjs measures the rendered change at
     mean 0.54-0.60 RGB levels and p95 <= 3.33 from 390 through 2552px. */
  mix-blend-mode: normal;
  transform: translateZ(0);
}

.about-wash--coral {
  left: -14%;
  top: 16%;
  width: 92%;
  height: 73%;
  filter: url(#about-wash-a);
  background:
    radial-gradient(58% 52% at 46% 44%, rgba(244, 164, 110, 0.5), rgba(244, 164, 110, 0.22) 48%, transparent 70%),
    radial-gradient(40% 36% at 63% 28%, rgba(233, 130, 145, 0.32), transparent 65%),
    radial-gradient(62% 56% at 44% 46%, transparent 55%, rgba(226, 124, 84, 0.22) 67%, transparent 75%);
}

.about-wash--violet {
  left: -4%;
  top: 67%;
  width: 72%;
  height: 42%;
  filter: url(#about-wash-b);
  background:
    radial-gradient(56% 50% at 42% 52%, rgba(164, 138, 220, 0.4), rgba(164, 138, 220, 0.18) 46%, transparent 68%),
    radial-gradient(60% 54% at 42% 52%, transparent 52%, rgba(150, 120, 210, 0.18) 65%, transparent 73%);
}

.about-wash--pink {
  left: 48%;
  top: 78%;
  width: 68%;
  height: 30%;
  filter: url(#about-wash-c);
  background: radial-gradient(62% 54% at 48% 55%, rgba(231, 111, 148, 0.32), transparent 70%);
}

.about-wash--rose {
  left: 78%;
  top: 71%;
  width: 62%;
  height: 38%;
  filter: url(#about-wash-a);
  background:
    radial-gradient(58% 52% at 52% 58%, rgba(228, 96, 134, 0.38), rgba(228, 96, 134, 0.16) 48%, transparent 70%),
    radial-gradient(62% 56% at 52% 58%, transparent 54%, rgba(214, 80, 120, 0.2) 66%, transparent 74%);
}

.about-wash--blush {
  left: -12%;
  top: -9%;
  width: 45%;
  height: 36%;
  filter: url(#about-wash-b);
  background: radial-gradient(64% 34% at 40% 44%, rgba(236, 140, 168, 0.3), transparent 68%);
}

/* ---------- Low-tier wash drift: baked displacement, no animated noise ----------
   The three wash filters now use fixed turbulence on every browser. Their old
   SMIL baseFrequency loops were the remaining Firefox bottleneck after the ink
   ribbons replaced Current Streams, so the <animate> nodes were removed rather
   than paused after first paint. On reduced tiers only, transform + opacity on
   the already-rasterised wash shapes preserves a little parallax at compositor
   cost. Full-tier desktop leaves these collage washes still; the ink ribbons
   provide the section's authored motion.

   VERIFIED, NOT ASSUMED: this is the crux of the whole fix, because in some
   engines transforming a filtered element re-triggers its filter every frame,
   which would leave this exactly where it started while looking fixed. Measured
   with the SAME CPU-throttled probe: fps recovers with this animation running,
   which is only possible if Chrome is compositing the once-rasterised static
   filter result rather than re-running feTurbulence/feDisplacementMap on every
   transform tick. If a future engine regresses this, the fix is to move the
   transform to a WRAPPER outside the filtered element (route 3 in the brief),
   not to remove the drift.

   Three different periods and directions -- about-wash-drift-a/b/c, reusing
   this file's own --dur-ambient tokens and the shared --atmos-drift/
   --atmos-scale/--atmos-fade envelope so this reads as the SAME ambient system
   as .about-light and .about-caustic above, not a new one -- so the five washes
   drift across and through each other rather than moving as one rigid block.
   Grouped by loose visual proximity rather than by which #about-wash-X filter
   each one happens to share (the filter reuse across variants is a rasterising
   convenience from the original CSS, not a motion grouping). */
html:not([data-tier="full"]) .about-wash {
  will-change: transform, opacity;
}

html:not([data-tier="full"]) .about-wash--coral,
html:not([data-tier="full"]) .about-wash--rose {
  animation: about-wash-drift-a var(--dur-ambient-slow) var(--ease-ambient) infinite;
}

html:not([data-tier="full"]) .about-wash--violet,
html:not([data-tier="full"]) .about-wash--blush {
  animation: about-wash-drift-b var(--dur-ambient) var(--ease-ambient) infinite reverse;
}

html:not([data-tier="full"]) .about-wash--pink {
  animation: about-wash-drift-c var(--dur-ambient-fast) var(--ease-ambient) infinite;
}

/* The unfolded foldable trace is tile-memory-bound, not shader-bound: keeping
   full-section composited surfaces alive costs tiles before the compositor can
   schedule their raster work. On a reduced/minimal coarse-pointer device, the
   ribbons remain About's single moving identity; these broad supporting layers
   keep their declared resting appearance but no longer demand permanent
   transform/opacity promotion. Full-tier desktop stays byte-for-byte on its
   existing animated path. */
@media (pointer: coarse) and (hover: none) {
  html:not([data-tier="full"]) .about-light__warm,
  html:not([data-tier="full"]) .about-light__cool,
  html:not([data-tier="full"]) .about-caustic,
  html:not([data-tier="full"]) .about-wash {
    animation: none;
    will-change: auto;
  }

  /* `translate3d(0,0,0)` / `translateZ(0)` are visual no-ops but explicit
     compositor-promotion hints. Keep each layer's authored resting scale while
     dropping that third dimension; the washes have no resting scale, so none
     is their exact static appearance. */
  html:not([data-tier="full"]) .about-light__warm { transform: scale(1.022); }
  html:not([data-tier="full"]) .about-light__cool { transform: scale(1.02); }
  html:not([data-tier="full"]) .about-caustic { transform: scale(1.02); }
  html:not([data-tier="full"]) .about-wash { transform: none; }
}

@keyframes about-wash-drift-a {
  0%, 100% { transform: translate3d(calc(var(--atmos-drift) * -1), calc(var(--atmos-drift) * 0.6), 0) scale(1); opacity: 0.9; }
  50%      { transform: translate3d(var(--atmos-drift), calc(var(--atmos-drift) * -0.6), 0) scale(var(--atmos-scale)); opacity: 1; }
}

@keyframes about-wash-drift-b {
  0%, 100% { transform: translate3d(calc(var(--atmos-drift) * 0.7), var(--atmos-drift), 0) scale(var(--atmos-scale)); opacity: 0.92; }
  50%      { transform: translate3d(calc(var(--atmos-drift) * -0.7), calc(var(--atmos-drift) * -1), 0) scale(1); opacity: 1; }
}

@keyframes about-wash-drift-c {
  0%, 100% { transform: translate3d(0, calc(var(--atmos-drift) * -1), 0) scale(1); opacity: 0.88; }
  50%      { transform: translate3d(0, var(--atmos-drift), 0) scale(var(--atmos-scale)); opacity: 1; }
}

.about-polaroid {
  z-index: 8;
  margin: 0;
}

/* will-change moved off the <figure> onto the <img> on 2026-07-29, following
   data-about-wiggle. The image is what about.js now re-transforms every 2000ms
   for the posterised drift, so it is the element that benefits from a standing
   layer; the figure only transforms during its one-off entrance tween, and GSAP
   manages will-change for the duration of that itself. Moved rather than added
   to both: three extra permanent layers is not free on a section that has
   already been fill-rate bound once (CLAUDE.md gotcha 9). */
.about-polaroid img {
  width: 100%;
  height: auto;
  filter: drop-shadow(0 22px 22px rgba(42, 34, 48, 0.16));
  will-change: transform;
}

/* ---------- Card placement, re-cut 2026-07-27 (round 7) ----------
   Daniel: "Make the main top card of Jess working with client the biggest card
   about 50% bigger than it is now." --hands goes 56% -> 84% and becomes the
   anchor; the other two are re-placed around it rather than left where they
   were, because 84% simply does not fit the old arrangement.

   THE TILT IS BAKED INTO THE SOURCE IMAGES' OWN PIXELS (about 4.4deg, see the
   note above the --hands figure in 04-about.html). There is no CSS rotation on
   any of these and none was added: the scattered-on-a-desk reading is the
   photographs' own content, and rotating the boxes as well would double it.

   STACK ORDER, and this is a correction rather than a preference. Daniel,
   mid-pass: "i would also say move jess image up a touch and on top of the
   others." The shipped order was materials 9 / hands 10 / studio 11, which put
   the studio shot over her; a first cut of this pass made it worse by dropping
   her to the back entirely, on the reasoning that a big photo with two smaller
   ones laid over its bottom edge is what a real pile looks like. It is, but she
   is the subject of the section, so she goes on top: the three are renumbered
   9 / 10 / 11 as materials / studio / HANDS. They stay inside the 9-11 band on
   purpose -- .about-wash is 2, .about-polaroid's own base is 8 and
   .about-content is 12, so the whole collage still sits above the pigment and
   below the copy panel, and nothing else in this file or in about.js reads
   these values (grepped).

   THAT CHANGE IS WHAT DRIVES THE REST OF THE NUMBERS. With her on top and at
   84%, wherever she overlaps she now HIDES rather than being hidden, so the
   other two had to move down and out or they would have been half-covered
   photographs of nothing. materials keeps only its top-right corner under her
   and studio only its top third, which is the "scattered on a desk" reading
   intact rather than a neat stack:

     - every card still overlaps at least one other, in both directions. This
       is a collage, not a three-up grid, and the point of the change is that
       SHE is the one on top, not that the cards stop touching;
     - --hands is lifted to top: -2%, about 21px higher at 2560 than the first
       cut and higher still relative to the other two, which moved down to 44%
       and 58%. "A touch" was his word, so it is a nudge, not a relocation;
     - --hands is the widest by a clear margin: 84% against materials' 61% and
       studio's 45%. At 2560 that is 512px against 372 and 274, where it used to
       be 342 against 407 and 283 -- it was the SECOND biggest before and is now
       half again the size of anything else;
     - materials bleeds further left (-4% -> -15%) instead of shrinking to make
       room. The section is overflow: clip and the container is centred with
       660px of bare paper either side at 2560 and 100px at 1440, so a left
       bleed is free there; taking the width out of the card instead would have
       made the whole collage read smaller, which is not what was asked for. It
       is pulled back in below 1060 -- see the media block for the measurement.

   RESOLUTION IS NOT A PROBLEM HERE AND IT WAS CHECKED, NOT ASSUMED. The source
   asset is 1348x1348 and at 84% it paints 512 CSS px at 2560, i.e. 0.76 device
   pixels per image pixel on a 2x display -- still a downscale, with the
   sharpest sampling of the three cards. tools/image-resolution-audit.mjs with
   DPR=2 is the check. */
.about-polaroid--materials {
  left: -15%;
  top: 44%;
  width: 61%;
  z-index: 9;
}

.about-polaroid--studio {
  left: 44%;
  top: 58%;
  width: 45%;
  z-index: 10;
}

.about-polaroid--hands {
  left: 15%;
  top: -2%;
  width: 84%;
  z-index: 11;
}

/* ---------- Editorial copy ---------- */

/* ---------- The restrained editorial glass ----------
   This is the "editorial glass" family, NOT the rich refractive treatment on the
   Ways cards, and it must not become that. A light veil over an atmosphere.

   What was wrong with it, measured with tools/about-atmosphere.mjs at 1440
   before this pass. Two things, and only one of them was the panel's fault:

     field   rgb 247,231,215   chroma 32.2   sdLumaLF 3.76   sdWarmLF 2.40
     panel   rgb 251,235,224   chroma 27.1   milk +4.4   chromaGain 0.84

   sdWarmLF is hue structure surviving a low-pass, i.e. the only thing a 20px
   blur can actually refract, and there was none: the backdrop was one flat
   beige, so blurring it returned the colour it started from. That is problem
   one, and the ambient field above is the fix for it. Problem two is here: the
   top background layer was white at 0.50 falling to 0.24, which LAUNDERS the
   backdrop (chromaGain 0.84 -- the panel held 84% of the colour behind it) while
   adding 4.4 luma of milk. White paint plus a flat backdrop is exactly the
   recipe for a frosted slab.

   So the white paint mostly goes and its job is handed to backdrop-filter
   brightness(), which lifts luminance by SCALING every channel: chroma survives
   the lift instead of being diluted by it. saturate() then pushes the field's
   own hue ramp back up. The panel now reads as a veil that INTENSIFIES what is
   behind it, which is what glass does, and the luminance the copy needs is
   still there -- .about-pull-line had 0.02 of headroom (p5 4.52 against 4.5), so
   this could not be paid for with a darker panel.

   Gotcha 8 applies to every number here: .ways-card shipped blur(23px) in CSS
   and `none` in the browser for months. These were probed with getComputedStyle
   in real headed Chrome, and there is no JS or glass-ready path on this panel,
   so the declaration and the computed value do agree. Re-check that before
   tuning, do not assume it.

   ============================================================================
   ROUND 7, 2026-07-27: THE BALLOONS' MATERIAL, WITHOUT THE LIP
   ============================================================================
   Daniel: "turn that glass into the similar texture as the balloons, but don't
   have that thick banded edge on it. keep the edges you have just make it read
   more like that glass." He is describing a MATERIAL, not a component, and this
   shipped as one change with the removal of about.js's panelGate() -- the
   particle field now runs fully behind this panel, so the glass has to earn the
   legibility the exclusion used to buy.

   The target is .who-card__surface in who.css, the glass lab's round-2 V6.
   WHAT WAS CARRIED, and what each thing is actually doing:

     1. THE FROST, not the paint. The balloons are clear-faced but heavily
        blurred: .who-card::before is blur(14px x --who-content-scale), about
        34px at desktop, over a warm 14% milk. This panel was at 13px over cool
        white at 0.17. Blur goes to that same 34 and the flat white becomes the
        same warm milk tone. Blur is what makes a backdrop read as being seen
        THROUGH something; white paint just makes it read as being covered.
     2. WARM, NOT COOL. Every fill in the V6 material is a warm off-white
        (255,253,248 / 255,248,238 / 255,251,244). The 135deg sheen here was
        pure #fff, which reads as plastic against this section's cream paper.
        Same alphas, warm hue.
     3. saturate 1.55 -> 1.16, near the balloons' own 1.12 / 1.18. The old 1.55
        was chosen when the backdrop was flat beige and needed help; with a live
        particle field behind the panel it AMPLIFIES that field's pigment right
        under the body copy, which is the opposite of what this round needs.
     4. THE CONTACT SHADOWS. V6's own two-tier warm pair (a tight contact shadow
        plus a long soft one) replaces the single cool violet drop this had. This
        is most of what makes a balloon read as an object resting on paper rather
        than a rectangle floating over it, and it costs nothing in contrast
        because it paints OUTSIDE the panel.
     5. THE WALL, as a hairline pair only -- see ::after below.

   WHAT WAS DELIBERATELY NOT CARRIED: the V6 lip. .who-card__surface::before is
   a 9px padding-ring at 2.5px inset and ::after is a four-tier bevel whose
   widest contour is 9px. Those numbers are sized for a 218-268px balloon; on a
   431px panel they read as exactly the "thick banded edge" Daniel had already
   said no to. The panel's existing border, radius and inner ::before hairline
   are untouched -- "keep the edges you have".

   THE BACKDROP-FILTER LIVES ON THIS ELEMENT AND THAT IS CORRECT HERE, even
   though `backdrop-filter: none` on .who-card__surface is load-bearing. The
   reason it is load-bearing there is that a NESTED pseudo-element carries a
   filter of its own, and a parent with any non-none backdrop-filter becomes a
   backdrop root that the child can no longer see past. Nothing nested inside
   .about-content has a backdrop-filter -- ::before is a plain 1px hairline and
   ::after is plain box-shadow -- so there is no root to be opaque to. If anyone
   ever adds a filtered pseudo here, this declaration has to move first.

   AND IT WAS SWEPT, NOT ASSUMED. `will-change: opacity` on any ancestor would
   make this element's backdrop-filter silently do nothing while
   getComputedStyle still reported it perfectly (ENGINEERING-NOTES, "Two ways
   backdrop-filter lies"). .about-content carries data-reveal="rise", so GSAP is
   writing to it. The only test that catches this is sweeping the blur hard and
   confirming the measurement moves: tools/about-copy-legibility.mjs SWEEP=1
   reports the panel's own high-frequency backdrop detail at blur(0) against
   blur(96px). Measured 4.02 against 1.77 at 2560, 4.24 / 2.02 at 1440 and
   3.86 / 1.80 at 390 -- a factor of 2.1 to 2.3x, and the two frames it writes
   to output/cohesion/r7-about/blur-sweep/ show the difference plainly. The
   filter is real. The residue at blur(96) is the panel's own border, hairlines
   and gradient dithering, none of which a backdrop-filter can touch. */
.about-content {
  position: relative;
  z-index: 12;
  max-width: 500px;
  padding: clamp(30px, 3vw, 46px);
  border: 1px solid rgba(255, 255, 255, 0.5);
  border-radius: 38px 38px 38px 16px;
  background:
    /* Warm-white light pooling at the panel's lower edge. This one is not
       decoration, it is load-bearing, and the reason is worth writing down
       because it is invisible in the declaration.

       .about-wash--rose is collage art: rgba(228,96,134,.38) on multiply, and
       its radial core sits at about 110% of .about-collage's own box, i.e.
       OUTSIDE the collage and underneath the copy panel's lower left. That is
       where .about-pull-line and .about-credentials sit. The old panel hid it by
       accident, under white paint at 0.50 alpha; take the paint away to stop the
       panel reading as a slab and the rose comes through the glass and takes the
       pull line from p5 4.52 to 4.29, under the 4.5:1 bar.

       Isolated with output/audit/pullline-source.mjs, which switches each
       candidate layer off in turn: with the two collage washes hidden the same
       patch measures 250,240,220 instead of 251,204,204, and NOTHING else moves
       it -- not the ambient light, not the caustic, not the section field, not
       blur, not saturate. So it is paid for here rather than by trimming the
       collage (the bloom is meant to be there) or by putting the milk back
       (which is the thing this pass exists to remove). A bottom-weighted lift is
       also the honest glass idiom: light pools at the lower edge of a pane.

       0.32 fading to zero at 50% was picked off a measured sweep, not by eye,
       and the sweep had to run over TIME as well as over alpha: the ambient
       light above moves across this patch, so a single frame is not an answer.
       output/audit/pullline-sweep.mjs samples the ratio once a second for 30s,
       which covers a full cycle of all three 19/23/29s periods, and reports the
       MINIMUM. At 0.24 that minimum is 4.508 -- it clears 4.5 by eight
       thousandths, which is not a margin. At 0.32 it is 4.631, and 0.40 bought
       another 0.07 for milk that showed.

       ROUND 7 TOOK THAT LAST 0.07, deliberately, and the thing that changed is
       what it costs. 0.32 was chosen against a 13px blur, where an extra eight
       hundredths of warm white read as a visible band at the panel's foot. At
       34px the whole face is frost and the extra alpha lands as a soft pool
       rather than an edge, which is the honest glass idiom anyway. It is spent
       on the two lines that sit in it: with the particle field now running
       under the panel, .about-credentials fell from 3.699 to 3.685 worst-frame
       at 1440 and this puts it back to 3.722, i.e. slightly BETTER than the
       gated build it replaces, and .about-pull-line goes 5.31 -> 5.47. That is
       the only reason it moved; it is not a taste change. Warm white rather
       than pure white, so the lift does not cool the panel. */
    linear-gradient(0deg, rgba(255, 252, 248, 0.4), rgba(255, 252, 248, 0) 50%),
    /* The sheen. Same alphas as before, but the pure #fff became the balloons'
       own warm off-white (255,251,244, .who-card's --who-frost-milk): cool white
       over cream paper is the single thing that made this read as plastic. */
    linear-gradient(135deg, rgba(255, 251, 244, 0.17), rgba(255, 251, 244, 0.04) 58%, rgba(255, 251, 244, 0.11)),
    linear-gradient(115deg, rgba(143, 131, 224, 0.05), rgba(236, 95, 156, 0.05) 50%, rgba(247, 178, 95, 0.06));
  /* blur 13 -> 34, saturate 1.55 -> 1.16, brightness 1.045 -> 1.075.

     34px IS THE BALLOONS' OWN NUMBER, not a round figure: .who-card's
     --who-frost-blur is calc(14px x --who-content-scale) and measures about 34
     at desktop, tuned there against transmitted light rather than by eye. A
     fixed value is right here where it is not there, because this panel is
     roughly 431px wide at 2560 and 1440 alike and 350 at 390, so there is no
     card-scale term to track.

     The other two were then picked off a measured sweep of six recipes at 2560
     (tmp scratch harness driving tools/about-copy-legibility.mjs's own maths;
     blur 30/36/42/44 x brightness 1.045/1.075/1.09 x saturate 1.16/1.30 x the
     lift gradient at 0.32/0.40), against two numbers at once:

       - worst-frame contrast for all five copy targets. Everything that
         cleared AA under the old panel exclusion still clears it at the worst
         frame of a full cycle at 2560, 1440 and 390 with the field running
         fully behind the panel, but it is NOT free and the honest numbers are
         in the report: most lines are 0.1 to 0.8 down on the gated build, the
         tightest being .about-eyebrow at 1440 (5.35 -> 4.80 against a 4.5 bar),
         .about-pull-line is up at two of three widths, and
         .about-credentials -- which already failed before this round and is
         Daniel's colour decision, not this pass's -- is 0.09 to 0.22 worse
         (3.72/3.70/3.77 -> 3.63/3.61/3.54);
       - how much of the field's own luminance delta still survives INSIDE the
         panel's rect, which is the other failure mode of this round and the one
         a contrast probe cannot see on its own. Cranking the glass until the
         animation is invisible would satisfy the letter of "run the field
         behind the panel" and destroy the point of it. At the shipped recipe
         the field moves the panel's own mean luminance by 5-6 levels, i.e. it
         is plainly transmitting, not being erased.

     brightness carries most of the lift rather than more white paint, for the
     reason the block above already gives: it scales every channel, so chroma
     survives the lift instead of being diluted by it. */
  -webkit-backdrop-filter: blur(34px) saturate(1.16) brightness(1.09);
  backdrop-filter: blur(34px) saturate(1.16) brightness(1.09);
  /* External contact shadows only now; the bright inset hairlines moved to
     ::after, which is V6's paint-order rule (point 2 in who.css's material
     block). The pair is the balloons' own -- a tight contact shadow plus a long
     soft one, both warm brown rather than the cool violet this used to carry --
     scaled up for a panel roughly 1.8x a balloon's size. */
  box-shadow:
    4px 6px 15px -5px rgba(108, 78, 66, 0.20),
    24px 44px 72px -34px rgba(138, 96, 88, 0.22);
}

/* The inner hairline. UNCHANGED -- this is half of "keep the edges you have",
   and it is the one thing on this panel that is deliberately not the balloons'
   (they have no inner line). */
.about-content::before {
  content: '';
  position: absolute;
  inset: 10px;
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: 28px 28px 28px 11px;
  pointer-events: none;
}

/* THE WALL, as a hairline pair and nothing else.

   V6 builds a bevelled wall out of four tiers of zero-blur inset box-shadow,
   the widest a 9px contour. That contour is what does the visual fattening, and
   on a card this size it is precisely the "thick banded edge" Daniel ruled out.
   So only the hairline tier is carried: the two bright lines this panel already
   had, moved here verbatim, plus the DARK counter-line from V6 that they never
   had. That single addition is what turns a bright top-left highlight (a sheet
   with a shine on it) into a lit edge and a shaded one (a wall with thickness),
   which is most of the "glass rather than plastic" reading at a fraction of the
   width. Kept at 1px and .13 against V6's 2px and .27 because this panel is
   about 1.8x a balloon and a wall reads as a proportion of what it edges.

   ON ::after, NOT ON THE PARENT'S OWN box-shadow, which is V6's point 2. Here
   it is precaution rather than a live fix -- nothing nested carries a filter
   today -- but the lab traced a real bug to the other order (::before's
   displacement map sampling and warping an already-painted hairline), and this
   is the arrangement that stays correct if a filtered pseudo is ever added.
   inset 0 with `border-radius: inherit` so the lines follow the panel's own
   38/38/38/16 corner, including the small one; pointer-events: none so it can
   never intercept a link. It sits at inset 0-1px against a 30-46px padding, so
   no glyph is ever underneath it. */
.about-content::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.72),
    inset 1px 0 0 rgba(255, 255, 255, 0.32),
    inset -1px -1px 0 0 rgba(90, 70, 60, 0.13);
}

.about-eyebrow {
  color: var(--pink-link);
}

.about-heading {
  margin-top: 20px;
  font-size: clamp(2.55rem, 4.2vw, 3.125rem);
  line-height: 1.12;
  text-wrap: pretty;
}

.about-heading .grad-text {
  white-space: nowrap;
}

.about-copy {
  display: grid;
  gap: 18px;
  margin-top: 28px;
  font-size: clamp(1rem, 1.3vw, 1.09375rem);
  line-height: 1.62;
  color: var(--ink-600);
}

.about-pull-line {
  margin-top: 22px;
  font-weight: 600;
  color: var(--pink-link);
}

.about-credentials {
  margin-top: 42px;
  font-family: var(--font-mono);
  font-size: 0.65625rem;
  line-height: 1.7;
  letter-spacing: 0.1em;
  /* 0.58 -> 0.68, 2026-07-29. This was the only text on the page failing AA:
     3.629 / 3.617 / 3.572 at 2560 / 1440 / 390 against a 4.5 bar, measured as
     the MINIMUM frame of a 30-sample cycle by tools/about-copy-legibility.mjs.
     It sits low in the panel where .about-wash--rose comes through from behind
     the collage, so its backdrop is about 0.80 relative luminance against the
     0.89 the body copy enjoys -- that gap, not the alpha alone, is why this one
     line failed while everything above it passed. 0.68 was chosen over the 0.66
     that just clears the bar because the worst width swings 0.24 between frames.
     Composited it lands at about rgb(104,96,105), still LIGHTER than --ink-600
     body copy, so the line stays quieter than the copy above it. */
  color: rgba(42, 34, 48, 0.68);
}

.about-credentials span {
  display: inline-block;
  margin-inline: 0.55em;
  color: var(--pink-link);
}

/* Follow row under the credentials. The shared component is .ii-social in
   base.css; only the size and the gap above it are About's business.

   34px rather than the footer's 38: these sit at the bottom of a glass panel
   whose whole lower half is deliberately quiet, directly under the smallest
   text on the page. At 38 they became the heaviest thing in the panel and
   pulled the eye off the pull line above them. 18px of clearance keeps them
   read as a separate beat rather than as punctuation on the credentials. */
.about-social {
  margin-top: 18px;
}

.about-social .ii-social__link {
  --ii-social-size: 34px;
}

/* ---------- Responsive composition ---------- */

@media (max-width: 1060px) {
  .about {
    min-height: 0;
  }

  .about-layout {
    grid-template-columns: minmax(0, 1.05fr) minmax(340px, 0.95fr);
    gap: 30px;
  }

  .about-content {
    padding: 30px;
  }

  /* THE LEFT BLEED IS ONLY FREE WHILE THERE IS BARE PAPER TO BLEED INTO.
     At 2560 the container is centred with 660px of empty sheet either side and
     at 1440 with 100px, so materials at left: -15% costs nothing. Below this
     breakpoint the container's own margin collapses and then the collage goes
     full width and centres, and the same -15% put the card past the viewport
     edge where .about's `overflow: clip` cuts it: measured 14px off-screen at
     900, 57px at 768 and 51px at 390 (tools/overflow-gate.mjs cannot see this
     -- clip means it is not a scroll overflow, just a card with its corner
     sliced off). Pulled in for every width below 1060; the rest of the
     arrangement is unchanged, so the collage still reads the same. */
  .about-polaroid--materials { left: -6%; }
  .about-polaroid--hands { left: 13%; }
  .about-polaroid--studio { left: 48%; }
}

@media (max-width: 820px) {
  .about {
    padding-block: 84px 80px;
    /* The collage drops BELOW the copy at this breakpoint, so the whole room
       re-lights: the blush-violet field the glass refracts rises to sit behind
       the panel in the upper half, and the collage's coral reflection follows
       the collage down. The vertical envelope is unchanged in kind -- every
       layer still reaches zero before both section edges, which is what the two
       seam lips either side of this chapter care about. */
    background-image:
      radial-gradient(80% 26% at 52% 26%, var(--about-lilac), var(--about-lilac-0) 74%),
      radial-gradient(78% 22% at 46% 72%, var(--atmos-coral-2), var(--atmos-coral-0) 72%),
      radial-gradient(56% 14% at 62% 58%, var(--atmos-pink-1), var(--atmos-pink-0) 70%),
      linear-gradient(180deg,
        var(--atmos-cream-0) 0%,
        var(--atmos-cream-2) 18%,
        var(--atmos-cream-2) 80%,
        var(--atmos-cream-0) 100%);
  }

  /* One window, still, but it moves to the top of the stack now that the copy
     is what sits in the upper half. The cool flank stays on the right. */
  .about-light__warm {
    background:
      radial-gradient(86% 34% at 6% -4%, var(--atmos-amber-3), var(--atmos-amber-1) 38%, var(--atmos-amber-0) 72%),
      radial-gradient(64% 22% at 18% 4%, var(--atmos-cream-3), var(--atmos-cream-0) 70%);
  }

  .about-light__cool {
    background: radial-gradient(64% 40% at 108% 62%, var(--atmos-violet-2), var(--atmos-violet-0) 72%);
  }

  .about-caustic {
    background-position: 24% 62%;
    -webkit-mask-image: radial-gradient(86% 30% at 50% 62%, #000 0%, #000 26%, rgba(0, 0, 0, 0) 86%);
    mask-image: radial-gradient(86% 30% at 50% 62%, #000 0%, #000 26%, rgba(0, 0, 0, 0) 86%);
  }

  .about-layout {
    grid-template-columns: 1fr;
    gap: 30px;
  }

  .about-content {
    grid-row: 1;
    max-width: 640px;
    justify-self: center;
  }

  .about-collage {
    grid-row: 2;
    width: min(100%, 680px);
    justify-self: center;
  }
}

@media (max-width: 520px) {
  .about {
    /* Brought up from 70/68 to the pass's ~80px mobile target. */
    padding-block: 80px 78px;
  }

  /* Narrowed 2026-07-30 (Daniel, on the deployed site: "the card sizes across
     the board could be smaller to give space to see more of the background
     animation on mobile. Especially the width, we need to see the hard work we
     put in the background animations"). Measured before this change
     (tools/about-card-width.mjs): .about-content already rendered at 85.9% of
     a 390px viewport with next to no visible margin either side -- the panel
     read as edge-to-edge even though the number was technically inside the
     82-86% range asked for. Landed at 82%, the low end, plus tighter padding,
     specifically because the ambient wash behind it is a SUBTLE effect and
     needs real margin to read at all, not just a measured percentage.

     `vw`, NOT a plain `%`. This grid item's containing block is `.container`'s
     content box (already narrower than the viewport by --gutter on both
     sides), so a plain percentage compounds two paddings: the first attempt at
     `width: 82%` measured 70.4% of the ACTUAL viewport (tools/about-card-width.mjs),
     8-12 points short of what was asked for the third time in a row it was
     checked. `vw` reads off the viewport directly, which is the unit Daniel's
     own ask was phrased in.

     Checked against .about-heading wrapping and .about-copy's line length at
     390 in real Chrome before shipping; nothing here is text-legibility-
     critical enough to trade for it (priority 1 in the shared brief), and if a
     future pass needs more room it should come back up before it comes down. */
  .about-content {
    width: 82vw;
    padding: 20px 17px 22px;
    border-radius: 28px 28px 28px 12px;
  }

  .about-content::before {
    inset: 8px;
    border-radius: 21px 21px 21px 8px;
  }

  .about-heading {
    font-size: clamp(2.15rem, 10vw, 2.75rem);
  }

  .about-heading .grad-text {
    white-space: normal;
  }

  .about-credentials {
    margin-top: 30px;
  }

  /* Narrowed 2026-07-30, same ask and same measurement pass as .about-content
     above. This used to BLEED wider than the viewport (calc(100% + 24px), a
     -12px margin) to give the materials card room; measured before this
     change at 92.1% of a 390px viewport (tools/about-card-width.mjs), the
     widest thing in the section. 84% opens real paper on both sides for the
     ambient light/caustic/streams layers to show through. Every polaroid,
     wash and decor position inside .about-collage is a PERCENTAGE OF THIS
     BOX (see the file-level comment above .about-collage), so shrinking the
     box uniformly scales the whole composition down without touching a single
     one of those tuned placements -- it is the one change here that could not
     silently break the "every card still overlaps at least one other" rule
     that round 7 tuned by hand.

     `vw`, not `%`, same reasoning as .about-content above: this grid item's
     containing block is already narrower than the viewport, so a plain
     percentage compounds the container's own gutter on top of the number
     asked for. */
  .about-collage {
    width: 84vw;
  }

  /* The -3% override this used to need existed only to counteract the 12px
     bleed above, which is gone -- falls back to the general <=1060 value
     (-6%) instead, and that is now SAFER than before it was added, not riskier:
     the same percentage against a smaller box is fewer real pixels of bleed,
     and the box itself sits further from the viewport edge to begin with.
     Checked in real Chrome at 390 for the overflow:clip corner-slice this
     section is prone to (see the <=1060 comment on .about-polaroid--materials);
     nothing clips. If a future change reintroduces bleed here, re-add the
     override rather than assuming this comment still applies. */
}

@media (prefers-reduced-motion: reduce) {
  /* THE SELECTOR HAS TO MATCH WHERE THE HINT ACTUALLY IS. On 2026-07-29
     `will-change: transform` moved off the <figure> onto the inner <img>,
     following data-about-wiggle (see the note above `.about-polaroid img`), and
     this cleanup was left naming the figure. Measured in real headed Chrome
     with prefers-reduced-motion forced on: the figure computed `auto` (it no
     longer declares it at all) and all three images computed `transform`, so
     the rule was undoing nothing and a reduced-motion reader kept three
     standing compositor layers for images about.js never transforms -- its
     updatePhotos() returns early when `reduced`. tools/bottom-reduced-motion.mjs
     reads both elements' COMPUTED value and is the regression check. */
  .about-polaroid,
  .about-polaroid img {
    will-change: auto;
  }

  .about-wash {
    filter: none;
  }

  /* The mobile drift replacement above (html:not([data-tier="full"]) .about-wash)
     is at least as specific as this bare .about-wash selector and would
     otherwise keep animating under reduced motion. Matching the compound
     selector exactly, and placed after it in source order, so this wins the
     cascade regardless of which tier a reduced-motion reader happens to be on. */
  html:not([data-tier="full"]) .about-wash {
    animation: none;
    will-change: auto;
  }

  /* HELD STILL, NOT HIDDEN. Every ambient layer keeps its resting appearance,
     which each rule above declares as the MIDPOINT of its loop rather than an
     endpoint, so nothing is stranded at a start state and the room still reads
     as lit. base.css would already collapse these to 0.01ms; saying it out loud
     here means the resting look does not depend on fill-mode semantics. */
  .about-light__warm,
  .about-light__cool,
  .about-caustic {
    animation: none;
    will-change: auto;
  }
}

@media (prefers-reduced-transparency: reduce) {
  .about-content {
    background: rgba(255, 250, 246, 0.96);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* Decoration only, and the forced palette is the point of this mode. */
@media (forced-colors: active) {
  .about { background-image: none; }

  .about-light,
  .about-caustic,
  .about-streams { display: none; }
}
