/**
 * SingleNotAlone texture and surface utilities.
 *
 * ==========================================================================
 * CLASS INDEX  (every custom class in this file, per the brief's requirement)
 * ==========================================================================
 *
 * .texture-grain   Film grain over dark surfaces. Inline SVG fractal noise at
 *                  4% opacity. Kills the flat-gradient look on plum.
 * .texture-paper   Barely-there fibrous tint plus an edge vignette for warm
 *                  off-white surfaces. Felt, not seen.
 * .foil            Metallic gold. A 3-stop gradient (deep, bright, deep)
 *                  instead of flat gold, with a slow sweep on hover.
 *                  Modifiers: .foil--rule (1px hairline), .foil--fill (button).
 * .bloom           One large soft off-centre radial of warm purple-gold light
 *                  on a dark surface, so the section has a light source rather
 *                  than a symmetric top-down gradient. Drifts over 60s.
 *                  Modifiers: .bloom--tl (top left), .bloom--right.
 * .hairline        1px gold rule at 24% opacity, for separating content inside
 *                  a section instead of boxing it.
 * .u-measure       Caps a text block at the reading measure.
 * .u-label         The mono uppercase label treatment, used on section
 *                  eyebrows only and never on individual cards.
 *
 * ==========================================================================
 * RULES
 * ==========================================================================
 *
 * 1. No hard-coded hex values. Every colour comes from a token in tokens.css.
 *    The only exceptions are inside the SVG data URIs, where a CSS variable
 *    cannot reach; those are noted individually and kept to ivory and plum.
 *
 * 2. All texture is generated. Every pattern here is an inline SVG data URI,
 *    so the page makes zero extra requests for decoration and none of it
 *    competes with the LCP image for bandwidth.
 *
 * 3. Texture never sits at strength under body text. Each utility either
 *    masks itself away from the reading area or runs at an opacity where it
 *    cannot move a measured contrast ratio. design/contrast-report.md assumes
 *    text sits on a solid token colour, and that has to stay true.
 *
 * 4. Everything that moves is off under prefers-reduced-motion: reduce.
 */

/* ---------------------------------------------------------------------------
 * .texture-grain
 *
 * feTurbulence at a high base frequency reads as photographic grain rather
 * than as a pattern. 4% is the point where it registers as material without
 * becoming visible noise: below 3% it does nothing, above 6% it looks dirty.
 * ------------------------------------------------------------------------ */

.texture-grain {
  position: relative;
  isolation: isolate;
}

.texture-grain::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.04;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23g)'/%3E%3C/svg%3E");
}

/* ---------------------------------------------------------------------------
 * .texture-paper
 *
 * Two parts: a very fine fibre tint, and a vignette that darkens the outer
 * edges by about 3%. The vignette is what makes an off-white section feel like
 * a sheet rather than a fill, and it is the cheaper half of the effect.
 * ------------------------------------------------------------------------ */

.texture-paper {
  position: relative;
  isolation: isolate;
}

.texture-paper::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.025;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='p'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23p)'/%3E%3C/svg%3E");
}

.texture-paper::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
    120% 80% at 50% 40%,
    transparent 55%,
    rgba(27, 11, 42, 0.035) 100%
  );
}

/* ---------------------------------------------------------------------------
 * .foil
 *
 * Flat gold is the single most template-looking surface available. Three stops
 * give it a direction and therefore an implied light source. The sweep on hover
 * is a background-position shift, not a repaint, so it stays cheap.
 *
 * .foil is for FILLS and RULES only. Gold as a word uses --gold-text, which is
 * darkened to clear AA on light grounds; plain --gold is 2.30:1 on cream.
 * ------------------------------------------------------------------------ */

.foil {
  background-image: linear-gradient(
    100deg,
    var(--gold-deep) 0%,
    var(--gold) 28%,
    var(--gold-bright) 50%,
    var(--gold) 72%,
    var(--gold-deep) 100%
  );
  background-size: 220% 100%;
  background-position: 0% 50%;
  transition: background-position 600ms var(--motion-easing);
}

.foil:hover,
.foil:focus-visible {
  background-position: 100% 50%;
}

/* A hairline that carries the same metal. Height comes from the border box, so
   the gradient needs a real element rather than a border colour. */
.foil--rule {
  display: block;
  width: 100%;
  height: 1px;
  border: 0;
  opacity: 0.75;
}

/* ---------------------------------------------------------------------------
 * .bloom
 *
 * A single large radial, deliberately off-centre. The current dark sections use
 * a symmetric top-down gradient, which is the thing that reads as generated:
 * real light comes from somewhere. Warm purple through gold, fading to nothing
 * well before the edges so it never becomes a visible shape.
 *
 * The drift is 60s and moves a few percent. At that speed it is not perceived
 * as animation, only as the section not being quite static.
 * ------------------------------------------------------------------------ */

.bloom {
  position: relative;
  isolation: isolate;
}

.bloom::before {
  content: "";
  position: absolute;
  z-index: -2;
  pointer-events: none;
  width: 120%;
  height: 130%;
  top: -30%;
  left: -20%;
  background: radial-gradient(
    circle at 50% 50%,
    rgba(201, 162, 39, 0.13) 0%,
    rgba(91, 42, 134, 0.26) 30%,
    rgba(27, 11, 42, 0) 62%
  );
}

.bloom--right::before {
  left: auto;
  right: -18%;
  top: -18%;
}

@media (prefers-reduced-motion: no-preference) {
  .bloom::before {
    animation: sna-bloom-drift 60s ease-in-out infinite alternate;
  }

  @keyframes sna-bloom-drift {
    from {
      transform: translate3d(0, 0, 0) scale(1);
    }
    to {
      transform: translate3d(3%, 2%, 0) scale(1.06);
    }
  }
}

/* ---------------------------------------------------------------------------
 * .hairline
 *
 * Replaces card borders and boxed layouts wherever content only needs
 * separating rather than containing. Gold at low opacity so it reads as a rule
 * on both grounds.
 * ------------------------------------------------------------------------ */

.hairline {
  border: 0;
  border-top: 1px solid color-mix(in srgb, var(--gold) 26%, transparent);
  margin: 0;
  width: 100%;
}

/* Fallback for engines without color-mix. */
@supports not (color: color-mix(in srgb, red 50%, blue)) {
  .hairline {
    border-top-color: rgba(201, 162, 39, 0.26);
  }
}

/* ---------------------------------------------------------------------------
 * Small helpers
 * ------------------------------------------------------------------------ */

.u-measure {
  max-width: var(--measure);
}

.u-label {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--gold-text);
}

/* On dark grounds the label can use full gold: 7.70:1 on plum. */
.sna-band-dark .u-label,
.texture-grain .u-label {
  color: var(--brand-gold-200);
}
