/* ==========================================================================
   STONEGRAPH — Living Art Engine
   Ported intact from the previous Stonegraph build so no motion work is lost.
   Every hand-drawn SVG (peacock, jharokha, archway, mandala, artisan, tools,
   carving) shares this vocabulary: strokes draw themselves on, ornaments
   bloom, and the finished form keeps breathing.
   ========================================================================== */

/* ---------- DRAW-ON — a stroke cuts itself into the stone ---------- */
.draw-on {
  stroke-dasharray: var(--len, 1000);
  stroke-dashoffset: var(--len, 1000);
  animation: draw-stroke 2.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
  animation-delay: var(--delay, 0s);
}
@keyframes draw-stroke {
  from { stroke-dashoffset: var(--len, 1000); }
  to   { stroke-dashoffset: 0; }
}

/* ---------- BLOOM — ornament opens ---------- */
.bloom-petal {
  opacity: 0;
  transform-origin: var(--origin, center);
  transform: scale(0) rotate(-20deg);
  animation: bloom-petal 1.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
  animation-delay: var(--delay, 1.5s);
}
.bloom-center {
  opacity: 0;
  transform-origin: center;
  animation: fade-in 1s ease-out forwards;
  animation-delay: var(--delay, 2s);
}
/* Blooms in and stays. The endless per-petal pulse that used to follow ran on
   twenty petals per mandala, on two pages, for a movement no one can see at
   watermark opacity — the plate as a whole already breathes. */
.bloom-then-breathe {
  opacity: 0;
  transform: scale(0) rotate(-20deg);
  animation: bloom-petal 1.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards var(--delay, 1.5s);
}
/* Bounded, like everything else in here — an endless second animation on an
   SVG child repaints the whole drawing for as long as it runs. Nothing in the
   current markup uses these two, but they are left correct rather than left
   as a trap for the next drawing that reaches for them. */
.bloom-then-twinkle {
  opacity: 0;
  transform: scale(0);
  animation:
    bloom-petal 1.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards var(--delay, 2s),
    iridescent-twinkle 3s ease-in-out 5 both var(--twinkle-delay, 4s);
}
.bloom-then-shimmer {
  opacity: 0;
  transform: scale(0);
  animation:
    bloom-petal 1.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards var(--delay, 2s),
    iridescent-shimmer 3s ease-in-out 5 both var(--shimmer-delay, 4s);
}
@keyframes bloom-petal {
  from { opacity: 0; transform: scale(0) rotate(-30deg); }
  to   { opacity: 1; transform: scale(1) rotate(0); }
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes petal-breathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.06); }
}
@keyframes iridescent-twinkle {
  0%, 100% { opacity: 0.85; transform: scale(1); }
  50%      { opacity: 1;    transform: scale(1.08); }
}
@keyframes iridescent-shimmer {
  0%, 100% { opacity: 0.85; transform: scale(1); }
  50%      { opacity: 1;    transform: scale(1.06); }
}

/* ---------- PEACOCK — the motion set worth keeping ----------
   Kept, but bounded. Each of these is a transform on a group inside the
   drawing, so any one of them running repaints the entire peacock; Story
   carried thirty-five of them at once. Three or four cycles is long enough
   for the bird to settle in front of the reader, and every cycle after that
   was being paid for on a page nobody was looking at any more. */
.tail-sway  { animation: tail-sway 8s ease-in-out 3 both; }
.tail-drift { animation: tail-drift 9s ease-in-out 3 both; }
.head-bob   { animation: head-bob 6s ease-in-out 4 both; }

.feather-sway-0 { animation: feather-sway 5s   ease-in-out 4 both; }
.feather-sway-1 { animation: feather-sway 6s   ease-in-out 4 both -1.5s; }
.feather-sway-2 { animation: feather-sway 7s   ease-in-out 3 both -3s; }
.feather-sway-3 { animation: feather-sway 5.5s ease-in-out 4 both -2s; }

.crest-sway   { animation: crest-sway 4s   ease-in-out 5 both; }
.crest-sway-0 { animation: crest-sway 4s   ease-in-out 5 both; }
.crest-sway-1 { animation: crest-sway 5s   ease-in-out 4 both -1s; }
.crest-sway-2 { animation: crest-sway 4.5s ease-in-out 5 both -2s; }

@keyframes tail-sway {
  0%, 100% { transform: rotate(-1.5deg) scale(1); }
  50%      { transform: rotate(1.5deg) scale(1.02); }
}
@keyframes tail-drift {
  0%, 100% { transform: rotate(-0.8deg) scale(1); }
  50%      { transform: rotate(0.8deg) scale(1.015); }
}
@keyframes feather-sway {
  0%, 100% { transform: rotate(-1.2deg); }
  50%      { transform: rotate(1.2deg); }
}
@keyframes crest-sway {
  0%, 100% { transform: rotate(-3deg); }
  50%      { transform: rotate(3deg); }
}
@keyframes head-bob {
  0%, 100% { transform: rotate(-1.5deg) translateY(0); }
  50%      { transform: rotate(1deg) translateY(-2px); }
}

/* ---------- JALI — light moving through perforation ---------- */
.jali-dot {
  opacity: 0;
  animation: fade-in 1.2s ease-out forwards;
  animation-delay: var(--delay, 1.5s);
}
/* One shimmer, not four hundred.
   Every perforation in a jali used to carry its own endless opacity loop —
   299 of them in the archway and 123 in the jharokha. Animating that many
   children forces the entire drawing to re-rasterise on every single frame,
   and that is what made Collections stutter on scroll. At watermark opacity
   nobody could tell the holes apart anyway.

   The perforations now fade in once and hold. The light moving across them is
   a single animation on the drawing as a whole, below. */
.jali-shimmer {
  opacity: 0;
  animation: fade-in 1.2s ease-out forwards var(--delay, 1.6s);
}

/* The one animation that replaces them: the whole plate breathes, the way
   light does moving across a carved screen. Eight of these on the site
   instead of several hundred. */
@keyframes plate-breathe {
  0%, 100% { opacity: 0.9; }
  50%      { opacity: 1; }
}
.art svg, .phead__art svg {
  animation: plate-breathe 7s ease-in-out infinite;
}

/* ---------- LAMP ---------- */
.lamp-glow {
  opacity: 0;
  animation:
    fade-in 1s ease-out forwards var(--delay, 2.7s),
    lamp-flicker 2.5s ease-in-out 8 both var(--delay, 4s);
}
.lamp-flicker { animation: lamp-flame 1.5s ease-in-out 12 both; transform-origin: center bottom; }
@keyframes lamp-flicker {
  0%, 100% { opacity: 0.85; transform: scale(1); }
  50%      { opacity: 1;    transform: scale(1.08); }
}
@keyframes lamp-flame {
  0%, 100% { opacity: 0.7; transform: scale(1) rotate(-2deg); }
  25%      { opacity: 0.9; transform: scale(1.05) rotate(1deg); }
  50%      { opacity: 0.8; transform: scale(0.95) rotate(-1deg); }
  75%      { opacity: 1;   transform: scale(1.03) rotate(2deg); }
}

/* ---------- JHAROKHA — bells and leaves ---------- */
.bell-sway-0 { animation: bell-sway 3s   ease-in-out 6 both; }
.bell-sway-1 { animation: bell-sway 3.5s ease-in-out 5 both -1s; }
.bell-sway-2 { animation: bell-sway 4s   ease-in-out 5 both -2s; }
.leaf-sway-0 { animation: leaf-sway 4s   ease-in-out 5 both; }
.leaf-sway-1 { animation: leaf-sway 5s   ease-in-out 4 both -1.5s; }
.leaf-sway-2 { animation: leaf-sway 4.5s ease-in-out 5 both -2.5s; }
@keyframes bell-sway {
  0%, 100% { transform: rotate(-4deg); }
  50%      { transform: rotate(4deg); }
}
@keyframes leaf-sway {
  0%, 100% { transform: rotate(-3deg); }
  50%      { transform: rotate(3deg); }
}

/* ---------- ARTISAN — the hand still working ---------- */
/* The carver works for a while and then rests his arm — twelve strokes rather
   than an endless one. The chips fly with him. */
.arm-carve  { animation: arm-carve 1.5s ease-in-out 12 both; }
.chip-fly-0 { animation: chip-fly 1.5s ease-out 12 both; }
.chip-fly-1 { animation: chip-fly 1.8s ease-out 10 both -0.5s; }
.chip-fly-2 { animation: chip-fly 1.6s ease-out 11 both -0.9s; }
@keyframes arm-carve {
  0%, 100% { transform: rotate(0deg); }
  50%      { transform: rotate(-8deg); }
}
@keyframes chip-fly {
  0%   { opacity: 0;   transform: translate(0, 0) scale(0.6); }
  25%  { opacity: 0.9; }
  100% { opacity: 0;   transform: translate(18px, -22px) scale(1.1); }
}

/* ==========================================================================
   AMBIENT — the plates wake, and then they settle
   Everything below used to loop forever. That is the single largest running
   cost on the site, and it is not a close contest.

   A transform or an opacity on an ordinary element is handed to the
   compositor and costs nothing to keep animating. A transform or an opacity on
   a node INSIDE an <svg> cannot be composited by any engine — it repaints the
   whole drawing. Measured on Collections, the two plates are 440x440 and
   352x383, which is a little over half a million device pixels; each of them
   carries a four-stage colour filter, and the archway holds 649 nodes. One
   turning ring or one blinking star anywhere inside is enough to force that
   entire surface to be re-rendered and re-filtered, sixty times a second, for
   as long as the plate is on screen. Thirty-one megapixels a second of
   permanent background work, on every page — which is why the site felt heavy
   everywhere at once rather than at any one moment.

   So the ambient motion is now bounded. A plate draws itself on when you reach
   it, breathes and glimmers for a stretch, and then holds — a finished
   drawing rather than a running one. After that it is a static image and costs
   nothing at all, which is what leaves the frame budget free for the thing the
   reader is actually doing: scrolling, or moving the collections strip.

   The one loop kept endless is `plate-breathe`, further down. It sits on the
   <svg> element itself rather than inside it, so the compositor owns it and it
   is genuinely free.
   ========================================================================== */
.twinkle {
  /* One run that arrives, glimmers, and rests lit — instead of an endless
     4s cycle. Eight of these per plate were repainting the whole drawing. */
  opacity: .8;
  transform-origin: center;
  animation: twinkle-settle 17s ease-in-out both;
  animation-delay: var(--delay, 0s);
}
@keyframes twinkle-settle {
  0%   { opacity: 0;   transform: scale(.7); }
  10%  { opacity: 1;   transform: scale(1.1); }
  22%  { opacity: .35; transform: scale(.82); }
  35%  { opacity: 1;   transform: scale(1.08); }
  48%  { opacity: .4;  transform: scale(.86); }
  61%  { opacity: 1;   transform: scale(1.06); }
  76%  { opacity: .5;  transform: scale(.9); }
  100% { opacity: .8;  transform: scale(1); }
}
/* Twenty-four of these per drawing, each an endless loop. They fade up once
   now; the sparkle that reads is .twinkle, of which there are a handful. */
.twinkle-dot {
  opacity: 0;
  animation: fade-in 1.4s ease-out forwards;
  animation-delay: var(--delay, 2s);
}
/* Three cycles, then hold the closing keyframe. Long enough to read as living
   while the plate is being looked at; finite, so it stops repainting. */
.breathe-glow  { animation: breathe-glow 8s ease-in-out 3 both; transform-origin: center; }
.relief-breathe{ animation: relief-breathe 20s ease-in-out 2 both; }
.breathe-center{ animation: breathe-center 6s ease-in-out 3 both; }
@keyframes twinkle-star {
  0%, 100% { opacity: 0.2; transform: scale(0.7); }
  50%      { opacity: 1;   transform: scale(1.1); }
}
@keyframes twinkle-dot {
  0%, 100% { opacity: 0.2; }
  50%      { opacity: 0.9; }
}
@keyframes breathe-glow {
  0%, 100% { opacity: 0.6; transform: scale(1); }
  50%      { opacity: 1;   transform: scale(1.08); }
}
@keyframes relief-breathe {
  0%, 100% { transform: scale(1);    opacity: 0.12; }
  50%      { transform: scale(1.05); opacity: 0.16; }
}
@keyframes breathe-center {
  0%, 100% { transform: scale(1);    opacity: 0.9; }
  50%      { transform: scale(1.04); opacity: 1; }
}

/* ---------- ROTATION — mandala rings ----------
   One revolution, not an endless one. A full turn ends exactly where it began,
   so the ring comes to rest without a seam and the plate goes quiet.
   Five of these were turning inside the mandala at once, and a rotating <g>
   repaints the whole drawing on every frame it moves. */
.rotate-ccw-45 { animation: rotate-ccw 45s linear 1 both; }
.rotate-ccw-50 { animation: rotate-ccw 50s linear 1 both; }
.rotate-ccw-75 { animation: rotate-ccw 75s linear 1 both; }
.rotate-cw-60  { animation: rotate-cw  60s linear 1 both; }
.rotate-cw-90  { animation: rotate-cw  90s linear 1 both; }
@keyframes rotate-cw  { from { transform: rotate(0deg); }   to { transform: rotate(360deg); } }
@keyframes rotate-ccw { from { transform: rotate(0deg); }   to { transform: rotate(-360deg); } }

/* The rings turn on a phone too.
   They were switched off below 900px in an earlier pass, on the grounds that a
   handheld GPU repainting a filtered drawing every frame is expensive. That
   was solving the right problem in the wrong place: the expense came from the
   loops being ENDLESS, and they are not any more — a ring makes one
   revolution and stops. A phone plate is also a quarter the area of a desktop
   one, so the repaint it does perform is a quarter the cost.
   Stripping the motion out of the artwork on the platform most of this
   audience actually uses was the wrong trade, and it is reversed. */

/* ---------- GATE ---------- */
/* Art holds still until it scrolls into view, then the whole sequence plays.
   This is what lets the same engine feel new in each new position. */
.art-gate .draw-on,
.art-gate .bloom-petal,
.art-gate .bloom-center,
.art-gate .bloom-then-breathe,
.art-gate .bloom-then-twinkle,
.art-gate .bloom-then-shimmer,
.art-gate .jali-dot,
.art-gate .jali-shimmer,
.art-gate .lamp-glow,
.art-gate .twinkle,
.art-gate .twinkle-dot {
  animation-play-state: paused;
}
.art-gate.is-in .draw-on,
.art-gate.is-in .bloom-petal,
.art-gate.is-in .bloom-center,
.art-gate.is-in .bloom-then-breathe,
.art-gate.is-in .bloom-then-twinkle,
.art-gate.is-in .bloom-then-shimmer,
.art-gate.is-in .jali-dot,
.art-gate.is-in .jali-shimmer,
.art-gate.is-in .twinkle-dot {
  /* One-shots. Once the plate is in view these run to the end and hold, and
     nothing may pause them — pausing a half-drawn stroke is what used to
     strand these drawings mid-line. Flat `running`, deliberately. */
  animation-play-state: running;
}
.art-gate.is-in .lamp-glow,
.art-gate.is-in .twinkle {
  /* These used to read `var(--play, running)` so a scroll could pause them.
     Nothing pauses anything now — every loop below is bounded — so this is
     simply `running`. */
  animation-play-state: running;
}

/* The drawings keep their motion.
   This block used to snap every stroke to its finished state and every petal
   to opacity 1 whenever the browser reported prefers-reduced-motion. On
   Android that preference is set by Battery Saver, so a very large share of
   phones were shown the plates already drawn, already bloomed, already still —
   the engine that draws these was doing nothing at all on the platform most of
   this audience arrives on.

   These animations are the drawing drawing itself: a stroke advancing along
   its own path, a petal opening, a feather swaying a degree or two. None of it
   travels across the screen, which is what the preference exists to prevent.
   The site-wide rules in site.css still hold back the page-level movement. */

/* ==========================================================================
   REST WHEN OUT OF VIEW — but only the endless loops
   Ambient motion left running offscreen costs real frames on every scroll, so
   it stops until the drawing comes back.

   What must NOT stop is a one-shot: .draw-on and the bloom classes play for a
   couple of seconds and then hold their finished state forever. Pausing those
   is what left drawings stranded half-drawn — the reader scrolls while the
   intro is still playing, the intro pauses, the gate goes is-out before it
   ever resumes, and the piece is frozen mid-stroke for the rest of the visit.
   A one-shot now always runs to completion, on screen or off; it costs two
   seconds of work once and nothing after that.
   ========================================================================== */
/* The long selector list that used to live here read `animation-play-state:
   var(--play, running)` on every looping class, so that a scroll — and later,
   a plate leaving the viewport — could pause the lot. Nothing pauses anything
   any more: every loop above is bounded and stops on its own, and the drawings
   are held still before they are reached by `.art-gate`. Writing `--play`
   measured 3.6ms because it inherits into the whole drawing, and it now has
   nothing left to switch off, so both the property and the rule are gone.

   GPU-isolate each artwork so its motion never triggers page-wide repaint */
.art, .hero__art, .phead__art { transform: translateZ(0); backface-visibility: hidden; }
/* meet, never slice — the whole drawing is always visible */
.art svg, .phead__art svg { overflow: visible; }
.art svg, .hero__art svg, .phead__art svg { will-change: opacity; }

/* ==========================================================================
   THE PLATES ON A PHONE
   Every drawing is placed with an inline `width:min(NNvw, NNNpx)`, tuned on a
   desktop. Read those same rules at 375px and the artwork all but disappears:
   the peacock came out 86px wide, the jharokha 105px, the mandala 135px. At
   that size a hand-drawn plate is not a background, it is a smudge — and the
   whole point of the engine is that these are drawn.

   Worse, `peacock-grove` is placed at `max(100%, 920px)` inside a section that
   clips: on a 375px screen that is 920px of drawing with 545px of it — nearly
   sixty percent — cut off outside the box.

   Inline styles can only be overridden with `!important`, which is why it is
   used here; it is confined to this block and to phone widths.
   ========================================================================== */
@media (max-width: 760px) {
  .art, .phead__art, .hero__art {
    /* Read as artwork, not as a watermark: never smaller than 250px, and
       allowed to run past the text column so it feels placed rather than
       squeezed in beside it. Bumped from clamp(250px, 74vw, 460px) to
       clamp(280px, 82vw, 520px) so a phone gets the same presence a laptop
       does. The earlier 460px cap was why a phone's hero mandala came out
       half the size of the desktop one and read as decoration rather than
       a drawn plate. */
    /* Proportion is what carries across screens, not pixels.
       This read clamp(280px, 82vw, 520px), which on a 390px phone made every
       plate 320px — 82% of the viewport. The same plate is 34% of a laptop
       viewport. So a drawing designed to sit beside the text became a drawing
       sitting on top of it, and the collections mandala in particular arrived
       oversized and hanging off the right edge.
       At 48vw a plate holds roughly the share of the screen it holds on a
       laptop, and stays a drawn plate rather than a smudge. */
    width: clamp(180px, 56vw, 330px) !important;
  }

  /* Whole drawings, placed on thirds.
     The previous attempt pushed each plate past the edge (right: -13%,
     left: -11%) on the theory that bleeding off-screen is how they are
     composed. Measured, that cropped the homepage mandala by 136px — 73% of
     the drawing gone — and took 43px off the collections archway. A plate the
     visitor only sees a third of is not a plate.

     So nothing is anchored outside the viewport any more. A right-hand plate
     sits with its centre near the two-thirds line and a left-hand one near the
     one-third line, both entirely on screen, which is the composition the
     desktop placements are reaching for on a canvas wide enough not to have to
     think about it. */
  .art[style*="right:"]  { right: 4% !important; left: auto !important; }
  .art[style*="left:"]   { left: 4% !important; right: auto !important; }
  /* The wide panorama plates are sized off the viewport instead, so they
     bleed edge to edge rather than being clipped down the middle. */
  .art[data-art="peacock-grove"] {
    width: 132vw !important;
    left: 50% !important;
    transform: translate(-50%, -50%) !important;
  }
  /* Corner-anchored plates lose their desktop inset, which on a narrow screen
     pushed most of the drawing off the edge. Only a slight bleed: these are
     masked to fade towards the LEFT, so their right-hand edge is the solid
     one and anything hanging past the section is cut cleanly rather than
     faded out. */
  .phead__art { right: -4% !important; }
}

/* ==========================================================================
   WHAT IS STILL ENDLESS, AND WHY IT IS ALLOWED TO BE
   Exactly one animation per plate: `plate-breathe`, on the <svg> element.
   That element is an ordinary box with `will-change: opacity`, so the
   compositor owns the animation and the drawing underneath is never
   re-rendered for it. It is the one piece of perpetual motion the page can
   afford, and it is the one that reads — the whole plate breathing, rather
   than any individual ornament twitching inside it.

   The rule for anything added here later: motion on the <svg> is free, motion
   on anything inside the <svg> repaints the entire drawing. Put it on the box.
   ========================================================================== */

/* ==========================================================================
   THE DRAWINGS GO BEHIND THE WORDS
   --------------------------------------------------------------------------
   `.art` is absolutely positioned with `z-index: 0`, and the copy it sits
   beside is not positioned at all. In CSS a positioned element paints above
   non-positioned content whatever the source order, so every plate on the site
   was painting ON TOP of the text — it simply never showed on a laptop, where
   each plate is anchored out to one side (right: -34%, and so on) and lands in
   empty margin.

   On a phone there is no margin to land in. The mobile rules above deliberately
   enlarge each plate to 82vw "so a phone gets the same presence a laptop does",
   which centres a 320px drawing directly over the column — and at the opacities
   these carry on desktop (up to 0.94, and 1.0 for the grove) the result was an
   opaque jharokha printed across the headline and the whole opening paragraph
   of Global Supply, with the text unreadable underneath it.

   Two things fix it, and both are needed. The copy is given its own stacking
   position so it is genuinely above the artwork rather than relying on source
   order; and on a phone the plates drop to the weight of background texture,
   which is the only honest role for a drawing sitting under a paragraph.
   ========================================================================== */
.band > .wrap,
.phead > .wrap,
.hero > .wrap,
section > .wrap,
.foot > .wrap {
  position: relative;
  z-index: 2;
}

@media (max-width: 760px) {
  .art,
  .phead__art,
  .hero__art {
    z-index: 0 !important;
    /* Visible artwork, not a watermark.
       This was briefly dropped to 0.14 to stop the plates overwhelming the
       copy — which fixed the collision by making the drawings almost
       disappear, and that is not a fix, it is a deletion. The collision is
       solved properly now: the copy has its own stacking position above the
       art, and each plate is sized by proportion and anchored past the edge
       rather than dropped into the column. So the drawings read again. */
    opacity: 0.62 !important;
  }

  /* Nothing may reach past the screen. The grove was 515px wide inside a
     390px viewport — running off both edges at once, so what a visitor saw
     was a meaningless slice out of the middle of a wider drawing rather than
     a composition. */
  .art { max-width: 100vw; }
  .art[data-art="peacock-grove"] { width: 112vw !important; }
}

/* The panorama, and one plate that was painting over the words on desktop too.
   `peacock-grove` carries z-index: 1, which put it above the copy on every
   screen — the mobile stacking fix caught it on phones, and this catches it
   everywhere else. Its own placement is full-bleed by design, so it keeps that
   and is simply held to the width of the screen rather than 132vw of it. */
.art[data-art="peacock-grove"] { z-index: 0 !important; }

@media (max-width: 760px) {
  .art[data-art="peacock-grove"] {
    width: 100vw !important;
    left: 50% !important;
    right: auto !important;
    transform: translate(-50%, -50%) !important;
  }
}

/* ==========================================================================
   THE PLATES GET THEIR OWN SPACE ON A PHONE
   --------------------------------------------------------------------------
   Everything above tried to keep the desktop composition on a phone: the plate
   absolutely positioned, the copy running across it. On a 1280px canvas that
   works, because a drawing anchored at 68% of the width sits in margin the
   text never reaches. At 390px there is no margin. Every attempt to make the
   drawing bigger therefore made the collision worse, and every attempt to fix
   the collision made the drawing smaller or fainter — which is the loop the
   last few passes were stuck in.

   A narrow screen cannot show a drawing and a paragraph in the same place, so
   it should not try. Each plate is the first child of its section already, so
   taking it out of absolute positioning drops it exactly where it belongs: a
   drawn plate at the head of the section, at full strength, with nothing on
   top of it and the copy beginning underneath.

   This costs vertical space, which is a real trade. It buys artwork that can
   actually be seen — which is the whole reason the engine exists.
   ========================================================================== */
@media (max-width: 760px) {
  .art,
  .phead__art,
  .hero__art {
    position: relative !important;
    inset: auto !important;
    transform: none !important;
    /* Big enough to read as a drawing. The inline widths are tuned against a
       desktop column and are overridden; the inline aspect-ratio is kept, so
       each plate keeps its own proportions. */
    width: min(70vw, 320px) !important;
    max-width: 100% !important;
    height: auto !important;
    margin: 4px auto clamp(16px, 3.5vw, 26px) !important;
    /* Nothing is behind it and nothing is over it, so it is allowed to be a
       drawing rather than a wash. */
    opacity: 0.92 !important;
    z-index: 0 !important;
    /* The desktop plates fade at one edge to blend into the margin. In flow,
       centred, that mask only makes the drawing look damaged. */
    mask-image: none !important;
    -webkit-mask-image: none !important;
  }

  /* The panorama is a band by design — it keeps the full width and a shape
     that does not swallow the screen. */
  .art[data-art="peacock-grove"] {
    width: 100% !important;
    aspect-ratio: 16 / 9 !important;
    margin-inline: 0 !important;
  }

  /* A section that exists only to carry a plate no longer needs its own
     padding on top of the plate's margin. */
  .phead .art + .wrap { padding-top: 0; }
}

/* The panorama is a band, not a plate.
   `peacock-grove` is the one drawing that is not an ornament beside the copy —
   it is a full-bleed divider between two chapters of the Story page, and its
   section exists to hold it. Pulling it into flow with the others turned it
   into a 219px strip sitting above roughly 500px of empty section, which is
   worse than either the original or the fix. It goes back to covering its band,
   and the band is given the height the drawing actually needs rather than the
   height it inherited from a desktop layout. */
@media (max-width: 760px) {
  .art[data-art="peacock-grove"] {
    position: absolute !important;
    inset: 0 !important;
    width: 100% !important;
    height: 100% !important;
    aspect-ratio: auto !important;
    margin: 0 !important;
    transform: none !important;
    opacity: 0.85 !important;
  }
  /* The band that carries it. Tall enough for the grove to read as a horizon,
     short enough that it is a pause between chapters and not a blank screen. */
  .band:has(> .art[data-art="peacock-grove"]) {
    min-height: 0;
    height: clamp(200px, 46vw, 260px);
    padding-block: 0;
    overflow: hidden;
  }
}

/* ==========================================================================
   AMBIENT MOTION ONLY WHILE YOU ARE LOOKING AT IT
   --------------------------------------------------------------------------
   The gate above holds the one-shots — strokes drawing, petals blooming —
   until a plate scrolls into view. The ambient loops were never added to it:
   every tail-sway, feather-sway, crest-sway, bell-sway, leaf-sway and lamp
   flicker on the page started at load and ran for its full eighteen to
   twenty-eight seconds, on every plate, whether or not it was anywhere near
   the screen.

   Measured on a phone-class CPU: 464 animations still running on Collections
   six seconds after load, p99 frame time 192ms, worst frame 292ms. Each of
   those is a transform on a child of an SVG, and a transform on an SVG child
   re-rasterises the whole drawing — which is why scrolling stuttered and a tap
   took a moment to register.

   The earlier note in this file says nothing pauses anything any more because
   every loop is bounded. Bounded is not the same as cheap: three cycles of an
   eight-second sway is still twenty-four seconds of repainting a peacock the
   reader has already scrolled past.

   So ambient motion is now tied to visibility. A plate you are looking at is
   fully alive — nothing is removed, nothing is shortened, the drawings behave
   exactly as designed. A plate off screen costs nothing. The one-shots are
   deliberately excluded: pausing a half-drawn stroke is what used to strand
   these drawings mid-line.
   ========================================================================== */
.art-gate .tail-sway,
.art-gate .tail-drift,
.art-gate .head-bob,
.art-gate [class*="feather-sway"],
.art-gate [class*="crest-sway"],
.art-gate [class*="bell-sway"],
.art-gate [class*="leaf-sway"],
.art-gate .lamp-flicker,
.art-gate .chisel,
.art-gate .chip,
.art-gate > svg {
  animation-play-state: paused;
}

/* On screen: everything runs. */
.art-gate.is-in .tail-sway,
.art-gate.is-in .tail-drift,
.art-gate.is-in .head-bob,
.art-gate.is-in [class*="feather-sway"],
.art-gate.is-in [class*="crest-sway"],
.art-gate.is-in [class*="bell-sway"],
.art-gate.is-in [class*="leaf-sway"],
.art-gate.is-in .lamp-flicker,
.art-gate.is-in .chisel,
.art-gate.is-in .chip,
.art-gate.is-in > svg {
  animation-play-state: running;
}

/* Scrolled past: it rests again. Listed after .is-in so it wins when the
   observer has set both at different times. */
.art-gate.is-out .tail-sway,
.art-gate.is-out .tail-drift,
.art-gate.is-out .head-bob,
.art-gate.is-out [class*="feather-sway"],
.art-gate.is-out [class*="crest-sway"],
.art-gate.is-out [class*="bell-sway"],
.art-gate.is-out [class*="leaf-sway"],
.art-gate.is-out .lamp-flicker,
.art-gate.is-out .chisel,
.art-gate.is-out .chip,
.art-gate.is-out > svg {
  animation-play-state: paused;
}


/* ==========================================================================
   EVERY PLATE ON A PHONE — ONE RULE, NOT SEVEN
   --------------------------------------------------------------------------
   The first pass at this fixed the Story grove and nothing else, so Portfolio,
   Global Supply and Journal still opened badly. Measured per plate on a
   phone-class CPU:

     archway (Collections)   649 nodes, 359 animated  (fade-in ×316)
     peacock (Portfolio)     318 nodes, 269 animated  (fade-in ×182)
     jharokha (Global)       254 nodes, 204 animated  (fade-in ×143)
     stone-artisan           121 nodes, 110 animated  (fade-in ×86)
     lotus-mandala           133 nodes, 104 animated  (fade-in ×52)
     heritage-tools          101 nodes,  87 animated

   The same shape every time: a few hundred elements each fading themselves in
   from zero, and a handful of ornaments scaling forever. Each is an animation
   on a child of an SVG, and every one of them re-rasterises the whole drawing.

   Two decisions, applied everywhere rather than plate by plate.

   The reveal: strokes still draw themselves in — that is the signature and it
   is kept in full. What stops is the several-hundred-element staggered fade
   behind it, which at this size is not perceptible as a stagger; those parts
   simply arrive with the drawing.

   The life: the ornaments that carry each drawing's character keep moving, and
   move MORE than they do on a desktop. A 1.2° feather sway reads on a 900px
   plate and is invisible on a 273px one, which is why the last pass left the
   peacock looking static. On a phone the few movements that remain are given
   an amplitude that can actually be seen.
   ========================================================================== */
@media (max-width: 760px) {
  /* ---- the reveal: instant for the bulk, drawn for the strokes ---------- */
  .art .bloom-center,
  .panel__art .bloom-center,
  .art .bloom-petal,
  .panel__art .bloom-petal,
  .art .bloom-then-breathe,
  .art .bloom-then-twinkle,
  .art .bloom-then-shimmer,
  .art .jali-dot,
  .panel__art .jali-dot,
  .art .jali-shimmer,
  .panel__art .jali-shimmer,
  .art .lamp-glow,
  .panel__art .lamp-glow,
  .panel__art .bloom-then-breathe {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  .art .twinkle,
  .art .twinkle-dot,
  .panel__art .twinkle,
  .panel__art .twinkle-dot {
    animation: none !important;
    opacity: 0.85 !important;
    transform: none !important;
  }

  /* ---- the many, subtle, invisible-at-this-size: still ------------------ */
  .art [class*="feather-sway"],
  .panel__art [class*="feather-sway"],
  .art [class*="leaf-sway"],
  .panel__art [class*="leaf-sway"],
  .art [class*="bell-sway"],
  .panel__art [class*="bell-sway"] {
    animation: none !important;
  }

  /* ---- the few that carry the character: kept, and made visible --------- */
  @keyframes sway-phone {
    0%, 100% { transform: rotate(-5deg); }
    50%      { transform: rotate(5deg); }
  }
  @keyframes drift-phone {
    0%, 100% { transform: rotate(-2.2deg) scale(1); }
    50%      { transform: rotate(2.2deg) scale(1.02); }
  }
  @keyframes bob-phone {
    0%, 100% { transform: rotate(-3deg) translateY(0); }
    50%      { transform: rotate(2deg) translateY(-3px); }
  }

  /* The crest is the most legible movement a peacock has at any size, so it
     is the one that stays — swinging 10° here against 6° on a desktop, which
     is what makes it read on a plate a third the size. These loop rather than
     running a fixed number of cycles, which is affordable now that they only
     run while the plate is actually on screen. */
  .art [class*="crest-sway"],
  .panel__art [class*="crest-sway"] {
    animation: sway-phone 4.5s ease-in-out infinite !important;
  }
  .art .tail-sway,
  .art .tail-drift,
  .panel__art .tail-sway,
  .panel__art .tail-drift {
    animation: drift-phone 7s ease-in-out infinite !important;
  }
  .art .head-bob,
  .panel__art .head-bob {
    animation: bob-phone 5s ease-in-out infinite !important;
  }
}


/* ==========================================================================
   A PLATE THAT IS OFF SCREEN STOPS COMPLETELY
   --------------------------------------------------------------------------
   The rest-on-exit rule above names eleven selectors — the sways, the bob,
   the flicker, the chisel and the chip — and sets animation-play-state on
   them. That property is not inherited, so it reaches exactly the nodes it
   names and nothing beneath them. Everything else inside a drawing carried
   on running for the whole visit: the vine scrolls turning, the glow
   breathing, the petals opening, the dots twinkling, the jali shimmering.

   Counted in the markup, the nodes that were never paused:

     Collections   367        Global Supply  132
     Homepage       58        Story           22
     Portfolio      17        Journal          7

   On Collections that is 367 animating nodes still being composited while
   the reader is thousands of pixels past them, for the entire length of a
   20,000px page. It is invisible work, and invisible work during a scroll is
   exactly what a dropped frame is made of.

   One rule, scoped inside a plate that the observer has already marked as
   off screen, stops all of it — including any animation added to these
   drawings later, which the eleven-selector list could never have covered.
   Nothing changes on screen: `is-out` is only ever set on a plate the reader
   has scrolled away from, and it is removed the moment it comes back. */
.art-gate.is-out,
.art-gate.is-out * {
  animation-play-state: paused !important;
}
