/* ==========================================================================
   Texture & depth system

   The previous version ran at 3.5–7% opacity, which is below the perceptual
   threshold — the texture existed but rendered as flat colour. This version
   targets the visible 13–20% band the client asked for.

   The calibration that matters: 15–25% is correct for a STROKE-ONLY motif and
   wrong for filled shapes. Navy at 18% over #f8fafc resolves to a soft
   blue-grey hairline — clearly visible. The same 18% on a filled petal
   produces a solid field that destroys text contrast. So every motif here is
   ~90% stroke, and any fill is capped at 0.25 alpha inside the SVG itself.

   Dual ink: a navy structural plate plus an amber plate at 0.35 alpha, offset
   and rotated off the navy grid. At composite opacity the amber reads as a
   faint warm cast in the interstices — the warmth of the client's reference
   without altering the palette.

   DISCIPLINE: never more than grain + one pattern + optional bloom per
   section. Everything is a generated data-URI or gradient — no image
   requests, so the strict CSP is untouched.
   ========================================================================== */

/* Any element carrying a .tex-* class needs a stacking context, with its own
   content lifted above the texture layers. */
[class*="tex-"] {
    position: relative;
    isolation: isolate;

    /* Load-bearing. .tex-mandala::before is inset:0 — i.e. as tall as the whole
       section — and then rotated. A rotated box's axis-aligned bounding box
       grows with its height, and these sections run 1400-3200px tall once
       content stacks on a phone, so a few degrees of rotation threw the box
       hundreds of pixels sideways and expanded the scrollable width of <html>.
       body{overflow-x:hidden} hid it, so nothing looked wrong on desktop — but
       on a phone a document wider than the viewport triggers shrink-to-fit:
       the layout viewport became 841px on a 360px screen, which zoomed the
       whole page out, pushed the hamburger off-screen, and stopped every
       max-width media query below 841px from ever matching.

       clip, NOT hidden: overflow:hidden creates a scroll container, which would
       trap the sticky .compare-head headers. clip does not.

       No overflow-clip-margin. It was set to 24px to spare the featured price
       card's inset:-14px blur glow, but a clip margin is still scrollable
       overflow — it put the document 24px over the viewport at every width and
       re-armed the same shrink-to-fit bug in miniature. The cards sit well
       inside their section's padding, so clipping at the padding box costs
       nothing visible. */
    overflow: clip;
}
[class*="tex-"] > * {
    position: relative;
    z-index: 1;
}
[class*="tex-"]::before,
[class*="tex-"]::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
}

/* --------------------------------------------------------------------------
   1 · Grain — universal
   Doubled from the old .035. This is what stops large flat fills reading as
   cheap; it belongs on every section, including white ones.
   -------------------------------------------------------------------------- */

.tex-grain::after {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.78' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
    opacity: 0.07;
    mix-blend-mode: multiply;
}

/* Multiply vanishes on dark surfaces — overlay, and slightly stronger. */
.surface-inverse.tex-grain::after,
.tex-grain.is-dark::after {
    opacity: 0.11;
    mix-blend-mode: overlay;
}

/* --------------------------------------------------------------------------
   2 · Mandala — the signature ornament

   Authored in an 800×800 viewBox. Every repeated element is defined once in
   <defs> and instanced with <use transform="rotate(...)">, which is what keeps
   a genuinely rich twelve-band ornament near 4KB instead of 15KB.

   Bands, outside in: keyline · scallop · outer petals · beads · double
   keyline · ogee arches · primary petals · spokes+nodes · amber petals
   (offset plate) · inner ring · rosette · centre.
   -------------------------------------------------------------------------- */

:root {
    --tex-mandala: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 800 800'%3E%3Cdefs%3E%3Cpath id='p1' d='M400 104C438 140 442 186 400 212C358 186 362 140 400 104Z'/%3E%3Cpath id='p2' d='M400 196C430 232 432 268 400 296C368 268 370 232 400 196Z'/%3E%3Cpath id='ar' d='M368 176Q400 138 432 176'/%3E%3Cpath id='sc' d='M376 24Q400 44 424 24'/%3E%3Cpath id='sp' d='M400 250V196'/%3E%3Cpath id='pa' d='M400 252C424 268 426 288 400 302C374 288 376 268 400 252Z'/%3E%3Ccircle id='bd' cx='400' cy='90' r='4'/%3E%3Ccircle id='nd' cx='400' cy='190' r='6'/%3E%3C/defs%3E%3Cg fill='none' stroke='%230d47a1' stroke-width='1.1'%3E%3Ccircle cx='400' cy='400' r='396'/%3E%3Ccircle cx='400' cy='400' r='286'/%3E%3Ccircle cx='400' cy='400' r='280' stroke-width='0.6'/%3E%3Ccircle cx='400' cy='400' r='150'/%3E%3Ccircle cx='400' cy='400' r='64' stroke-width='1.4'/%3E%3Cg stroke-width='1.6'%3E%3Cuse href='%23p2'/%3E%3Cuse href='%23p2' transform='rotate(30 400 400)'/%3E%3Cuse href='%23p2' transform='rotate(60 400 400)'/%3E%3Cuse href='%23p2' transform='rotate(90 400 400)'/%3E%3Cuse href='%23p2' transform='rotate(120 400 400)'/%3E%3Cuse href='%23p2' transform='rotate(150 400 400)'/%3E%3Cuse href='%23p2' transform='rotate(180 400 400)'/%3E%3Cuse href='%23p2' transform='rotate(210 400 400)'/%3E%3Cuse href='%23p2' transform='rotate(240 400 400)'/%3E%3Cuse href='%23p2' transform='rotate(270 400 400)'/%3E%3Cuse href='%23p2' transform='rotate(300 400 400)'/%3E%3Cuse href='%23p2' transform='rotate(330 400 400)'/%3E%3C/g%3E%3Cg%3E%3Cuse href='%23p1'/%3E%3Cuse href='%23p1' transform='rotate(15 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(30 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(45 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(60 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(75 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(90 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(105 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(120 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(135 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(150 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(165 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(180 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(195 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(210 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(225 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(240 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(255 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(270 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(285 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(300 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(315 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(330 400 400)'/%3E%3Cuse href='%23p1' transform='rotate(345 400 400)'/%3E%3C/g%3E%3Cg stroke-width='0.9'%3E%3Cuse href='%23ar'/%3E%3Cuse href='%23ar' transform='rotate(22.5 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(45 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(67.5 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(90 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(112.5 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(135 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(157.5 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(180 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(202.5 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(225 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(247.5 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(270 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(292.5 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(315 400 400)'/%3E%3Cuse href='%23ar' transform='rotate(337.5 400 400)'/%3E%3C/g%3E%3Cg stroke-width='0.9'%3E%3Cuse href='%23bd'/%3E%3Cuse href='%23bd' transform='rotate(20 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(40 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(60 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(80 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(100 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(120 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(140 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(160 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(180 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(200 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(220 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(240 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(260 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(280 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(300 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(320 400 400)'/%3E%3Cuse href='%23bd' transform='rotate(340 400 400)'/%3E%3C/g%3E%3Cg stroke-width='0.8'%3E%3Cuse href='%23sp'/%3E%3Cuse href='%23sp' transform='rotate(30 400 400)'/%3E%3Cuse href='%23sp' transform='rotate(60 400 400)'/%3E%3Cuse href='%23sp' transform='rotate(90 400 400)'/%3E%3Cuse href='%23sp' transform='rotate(120 400 400)'/%3E%3Cuse href='%23sp' transform='rotate(150 400 400)'/%3E%3Cuse href='%23sp' transform='rotate(180 400 400)'/%3E%3Cuse href='%23sp' transform='rotate(210 400 400)'/%3E%3Cuse href='%23sp' transform='rotate(240 400 400)'/%3E%3Cuse href='%23sp' transform='rotate(270 400 400)'/%3E%3Cuse href='%23sp' transform='rotate(300 400 400)'/%3E%3Cuse href='%23sp' transform='rotate(330 400 400)'/%3E%3C/g%3E%3Cg stroke-width='0.9'%3E%3Cuse href='%23nd'/%3E%3Cuse href='%23nd' transform='rotate(60 400 400)'/%3E%3Cuse href='%23nd' transform='rotate(120 400 400)'/%3E%3Cuse href='%23nd' transform='rotate(180 400 400)'/%3E%3Cuse href='%23nd' transform='rotate(240 400 400)'/%3E%3Cuse href='%23nd' transform='rotate(300 400 400)'/%3E%3C/g%3E%3Ccircle cx='400' cy='400' r='14' fill='%230d47a1' fill-opacity='0.25' stroke='none'/%3E%3C/g%3E%3Cg fill='none' stroke='%23f59e0b' stroke-opacity='0.35' stroke-width='0.9' transform='rotate(7.5 400 400)'%3E%3Ccircle cx='400' cy='400' r='376'/%3E%3Ccircle cx='400' cy='400' r='236' stroke-width='0.7'/%3E%3Cg%3E%3Cuse href='%23pa'/%3E%3Cuse href='%23pa' transform='rotate(45 400 400)'/%3E%3Cuse href='%23pa' transform='rotate(90 400 400)'/%3E%3Cuse href='%23pa' transform='rotate(135 400 400)'/%3E%3Cuse href='%23pa' transform='rotate(180 400 400)'/%3E%3Cuse href='%23pa' transform='rotate(225 400 400)'/%3E%3Cuse href='%23pa' transform='rotate(270 400 400)'/%3E%3Cuse href='%23pa' transform='rotate(315 400 400)'/%3E%3C/g%3E%3Cg%3E%3Cuse href='%23sc' transform='rotate(0 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(15 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(30 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(45 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(60 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(75 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(90 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(105 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(120 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(135 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(150 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(165 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(180 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(195 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(210 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(225 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(240 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(255 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(270 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(285 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(300 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(315 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(330 400 400)'/%3E%3Cuse href='%23sc' transform='rotate(345 400 400)'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");

    /* Compound mask: the motif may appear only in the corner AND outside the
       reading column. `intersect` is what enforces the "and". */
    --tex-gutter: linear-gradient(90deg, #000 0%, transparent 26%, transparent 74%, #000 100%);
}

.tex-mandala::before {
    background-image: var(--tex-mandala);
    background-repeat: no-repeat;
    background-position: var(--tex-ax, 92%) var(--tex-ay, -22%);
    background-size: var(--tex-size, 760px) var(--tex-size, 760px);
    opacity: var(--tex-op, 0.20);
    mix-blend-mode: multiply;
    transform: rotate(var(--tex-rot, 0deg)) scaleX(var(--tex-flip, 1));

    /* Gutter mask only. The motif is already placed in a corner dead zone, so
       protecting the centre reading column is all that's needed — and a single
       mask layer is far more predictable than compositing two. (An earlier
       version added a radial centred on the background-position variable,
       which for off-canvas positions like -22% masked the motif away entirely.) */
    -webkit-mask-image: var(--tex-gutter);
    mask-image: var(--tex-gutter);
}

/* Optional extra falloff where a section is short and the motif would crowd. */
.tex-mandala.is-soft::before {
    -webkit-mask-image: var(--tex-gutter), linear-gradient(#000, transparent 85%);
    mask-image: var(--tex-gutter), linear-gradient(#000, transparent 85%);
    -webkit-mask-composite: source-in;
    mask-composite: intersect;
}

/* On dark surfaces the motif is inverted to white and can carry more weight. */
.surface-inverse.tex-mandala::before,
.tex-mandala.is-dark::before {
    filter: brightness(0) invert(1);
    mix-blend-mode: normal;
    opacity: calc(var(--tex-op, 0.20) * 0.55);
}

/* Full-bleed sections with no centre column can show the motif everywhere. */
.tex-mandala.is-full::before {
    -webkit-mask-image: none;
    mask-image: none;
}

/* --- Six plates, so consecutive sections never look stamped from one die.
       The motif has 8/12/16/24-fold symmetry, so rotation genuinely reads as
       a different ornament. --- */
/* Positions keep roughly 60–70% of the motif on canvas — pushed further out
   it reads as a stray arc rather than an ornament. Multiply over a near-white
   surface is weak, so light plates sit at the top of the 15–25% band while
   the darker/denser ones sit lower. */
.plate-a { --tex-ax: 96%;  --tex-ay: -8%;  --tex-size: 720px;  --tex-rot: 0deg;   --tex-flip: 1;  --tex-op: 0.24; }
.plate-b { --tex-ax: -6%;  --tex-ay: 96%;  --tex-size: 660px;  --tex-rot: 18deg;  --tex-flip: -1; --tex-op: 0.22; }
.plate-c { --tex-ax: 102%; --tex-ay: 54%;  --tex-size: 820px;  --tex-rot: -12deg; --tex-flip: 1;  --tex-op: 0.20; }
.plate-d { --tex-ax: 50%;  --tex-ay: -26%; --tex-size: 960px;  --tex-rot: 7deg;   --tex-flip: 1;  --tex-op: 0.17; }
.plate-e { --tex-ax: -8%;  --tex-ay: 6%;   --tex-size: 700px;  --tex-rot: -24deg; --tex-flip: -1; --tex-op: 0.23; }
.plate-f { --tex-ax: 94%;  --tex-ay: 104%; --tex-size: 780px;  --tex-rot: 33deg;  --tex-flip: 1;  --tex-op: 0.21; }

/* --------------------------------------------------------------------------
   3 · Jaali — mid-frequency lattice, seamless 96×96
   Provides the layered density of the reference image at a smaller scale.
   -------------------------------------------------------------------------- */

.tex-jaali::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E%3Cg fill='none' stroke='%230d47a1' stroke-width='0.9'%3E%3Ccircle cx='0' cy='0' r='34'/%3E%3Ccircle cx='96' cy='0' r='34'/%3E%3Ccircle cx='0' cy='96' r='34'/%3E%3Ccircle cx='96' cy='96' r='34'/%3E%3Ccircle cx='48' cy='48' r='34'/%3E%3C/g%3E%3Cg fill='none' stroke='%23f59e0b' stroke-opacity='0.3' stroke-width='0.7'%3E%3Cpath d='M0 48H96M48 0V96'/%3E%3Cpath d='M0 0L96 96M96 0L0 96'/%3E%3C/g%3E%3C/svg%3E");
    opacity: 0.13;
    mix-blend-mode: multiply;
    -webkit-mask-image: linear-gradient(#000, transparent 58%);
    mask-image: linear-gradient(#000, transparent 58%);
}

/* --------------------------------------------------------------------------
   4 · Border band — an ornamental ribbon at a section edge
   Only 44px tall and touches no text, so it can run at full strength. This is
   what makes the page feel composed rather than wallpapered.
   -------------------------------------------------------------------------- */

.tex-band-top::before,
.tex-band-bottom::after {
    height: 44px;
    inset: auto 0 auto 0;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='44' viewBox='0 0 120 44'%3E%3Cg fill='none' stroke='%230d47a1' stroke-width='1'%3E%3Cpath d='M0 34Q30 8 60 34Q90 8 120 34'/%3E%3Ccircle cx='30' cy='22' r='2.6'/%3E%3Ccircle cx='90' cy='22' r='2.6'/%3E%3C/g%3E%3Cg fill='none' stroke='%23f59e0b' stroke-opacity='0.4' stroke-width='0.8'%3E%3Cpath d='M0 40Q30 18 60 40Q90 18 120 40'/%3E%3C/g%3E%3C/svg%3E");
    background-repeat: repeat-x;
    opacity: 0.26;
}
.tex-band-top::before    { top: 0; }
.tex-band-bottom::after  { bottom: 0; transform: scaleY(-1); }

/* --------------------------------------------------------------------------
   5 · Dots — technical rhythm
   -------------------------------------------------------------------------- */

.tex-dots::before {
    background-image: radial-gradient(circle, rgba(13, 71, 161, 0.28) 1px, transparent 1px);
    background-size: 24px 24px;
    opacity: 0.45;
    -webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 40%, #000 30%, transparent 75%);
    mask-image: radial-gradient(ellipse 80% 60% at 50% 40%, #000 30%, transparent 75%);
}

/* --------------------------------------------------------------------------
   6 · Grid — two-frequency. A single-frequency grid looks like a CSS demo.
   -------------------------------------------------------------------------- */

.tex-grid::before {
    background-image:
        linear-gradient(to right,  rgba(13, 71, 161, 0.10) 1px, transparent 1px),
        linear-gradient(to bottom, rgba(13, 71, 161, 0.10) 1px, transparent 1px),
        linear-gradient(to right,  rgba(13, 71, 161, 0.05) 1px, transparent 1px),
        linear-gradient(to bottom, rgba(13, 71, 161, 0.05) 1px, transparent 1px);
    background-size: 96px 96px, 96px 96px, 24px 24px, 24px 24px;
    -webkit-mask-image: linear-gradient(to bottom, transparent, #000 25%, #000 75%, transparent);
    mask-image: linear-gradient(to bottom, transparent, #000 25%, #000 75%, transparent);
}

.surface-inverse.tex-grid::before {
    background-image:
        linear-gradient(to right,  rgba(125, 211, 252, 0.14) 1px, transparent 1px),
        linear-gradient(to bottom, rgba(125, 211, 252, 0.14) 1px, transparent 1px),
        linear-gradient(to right,  rgba(125, 211, 252, 0.07) 1px, transparent 1px),
        linear-gradient(to bottom, rgba(125, 211, 252, 0.07) 1px, transparent 1px);
}
.surface-inverse.tex-dots::before {
    background-image: radial-gradient(circle, rgba(125, 211, 252, 0.30) 1px, transparent 1px);
}

/* --------------------------------------------------------------------------
   7 · Bloom — a single soft light source. Replaces the old blurred blobs.
   -------------------------------------------------------------------------- */

.tex-bloom::before {
    background:
        radial-gradient(60% 50% at var(--bx, 50%) var(--by, 0%),
            rgba(56, 189, 248, 0.22), rgba(13, 71, 161, 0.10) 45%, transparent 72%);
}

.surface-inverse.tex-bloom::before {
    background:
        radial-gradient(65% 55% at var(--bx, 50%) var(--by, 0%),
            rgba(56, 189, 248, 0.26), rgba(21, 101, 192, 0.20) 46%, transparent 74%);
}

/* Mesh drift is expensive (it animates a full-bleed pseudo-element forever),
   so it is limited to the dark CTA band. */
.tex-mesh::before {
    background:
        radial-gradient(38% 44% at 12% 18%, rgba(13, 71, 161, 0.16), transparent 62%),
        radial-gradient(34% 40% at 88% 26%, rgba(56, 189, 248, 0.18), transparent 60%),
        radial-gradient(44% 50% at 62% 92%, rgba(125, 211, 252, 0.14), transparent 65%);
}
.surface-inverse.tex-mesh::before {
    background:
        radial-gradient(40% 46% at 10% 12%, rgba(21, 101, 192, 0.55), transparent 62%),
        radial-gradient(36% 42% at 90% 30%, rgba(56, 189, 248, 0.28), transparent 60%),
        radial-gradient(46% 52% at 55% 96%, rgba(13, 71, 161, 0.45), transparent 66%);
    animation: meshDrift 26s ease-in-out infinite alternate;
}

@keyframes meshDrift {
    0%   { transform: translate3d(0, 0, 0) scale(1); }
    50%  { transform: translate3d(-1.5%, 1.5%, 0) scale(1.06); }
    100% { transform: translate3d(1.5%, -1%, 0) scale(1.03); }
}

/* --------------------------------------------------------------------------
   Readability helper — any copy that must cross texture gets a local plane.
   1px of background-coloured shadow under type restores crispness for free.
   -------------------------------------------------------------------------- */

.on-texture { text-shadow: 0 1px 0 rgba(248, 250, 252, 0.9); }

/* --------------------------------------------------------------------------
   Glass — for cards over texture, so the texture reads through
   -------------------------------------------------------------------------- */

.glass {
    background: rgba(255, 255, 255, 0.74);
    backdrop-filter: blur(14px) saturate(140%);
    -webkit-backdrop-filter: blur(14px) saturate(140%);
    border: 1px solid rgba(255, 255, 255, 0.85);
    box-shadow: var(--el-2), var(--el-lip);
}

.surface-inverse .glass,
.glass.is-dark {
    background: rgba(255, 255, 255, 0.055);
    border-color: rgba(255, 255, 255, 0.12);
    box-shadow: none;
}

/* Without backdrop-filter a translucent panel makes text unreadable, so fall
   back to a solid fill rather than a wash. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .glass { background: rgba(255, 255, 255, 0.96); }
    .surface-inverse .glass, .glass.is-dark { background: rgba(15, 23, 42, 0.92); }
}

/* --------------------------------------------------------------------------
   Edges — never a solid full-width 1px line between sections
   -------------------------------------------------------------------------- */

.rule-fade {
    height: 1px;
    border: 0;
    background: linear-gradient(90deg,
        transparent, rgba(15, 23, 42, 0.10) 15%, rgba(15, 23, 42, 0.10) 85%, transparent);
}

.hairline-top    { border-top: 1px solid var(--hair-1); }
.hairline-bottom { border-bottom: 1px solid var(--hair-1); }

@media (prefers-reduced-motion: reduce) {
    .surface-inverse.tex-mesh::before { animation: none !important; }
}

/* --------------------------------------------------------------------------
   Aurora wash — faint drifting color atmosphere for light sections.
   Composed as background-image layers on the element itself (the ::before /
   ::after slots are already taken by mandala and grain), so it stacks with
   background-color and never affects text contrast (all layers ≤5% alpha).
   Drift is a slow background-position crawl; the global reduced-motion block
   freezes it. -------------------------------------------------------------- */
.tex-aurora {
    background-image: var(--aurora-blue), var(--aurora-sky), var(--aurora-warm);
    background-repeat: no-repeat;
    background-size: 160% 160%, 150% 150%, 140% 140%;
    animation: auroraDrift 120s var(--ease-in-out) infinite alternate;
}

@keyframes auroraDrift {
    from { background-position: 0% 0%,   100% 0%,  70% 100%; }
    to   { background-position: 30% 20%, 70% 25%,  45% 75%; }
}
