/* =====================================================================
   ZESTY SNIPPETS
   Small hand-lettered asides in the custom brush script, parked in the
   empty margin beside real content. Rendered by refinery_zesty() —
   inc/zesty.php has the reasoning; this file is only the look.

   Loaded site-wide: a snippet can be dropped on any template, and the
   sheet is small. The brush @font-face lives here rather than in the
   shared stylesheet, so the 24 KB face is only fetched by pages that
   load this file.
   ===================================================================== */

/* ---------------------------------------------------------------------
   The brush face.

   ⚠️ THIS IS NOT A REVERSAL OF 2026-08-13. `.accent-brush` — the accent
   word inside a display heading — is Poppins Light 300 and stays that
   way; the rule at the end of style.css, and the :has() colour swap it
   guards, both depend on it. The brush family is declared here and asked
   for by `.zesty` alone.

   Bump the ?v= on both urls if the binary is ever re-cut. The filename
   never changes, so without it browsers keep serving the old metrics.
   (The face was re-spaced to 8/10 bearings in 2026-08 — it carries its
   own spacing, so do NOT add letter-spacing to compensate.)
   --------------------------------------------------------------------- */
@font-face {
  font-family: "Custom Brush Script";
  src:
    url("../fonts/CustomBrushScript-Regular.woff2?v=10") format("woff2"),
    url("../fonts/CustomBrushScript-Regular.ttf?v=10") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ---------------------------------------------------------------------
   The snippet.

   TWO ELEMENTS ON PURPOSE. The wrapper does placement and nothing else;
   `.zesty__inner` carries the flex box and the tilt. That split is what
   lets a snippet also be a `.reveal` — the reveal animation drives
   `transform` on the element it is applied to, so a rotation on the same
   element is wiped the moment `.is-visible` lands (`.reveal.is-visible`
   is (0,2,0) and beats `.zesty`). Keep every transform off `.zesty`.
   --------------------------------------------------------------------- */
.zesty {
  /* 2026-08-21, Kay, in two passes: doubled off the original
     clamp(1.35rem, 2.4vw, 2.1rem), which read as a caption rather than as
     handwriting, then back 25% from there. */
  --zesty-size: clamp(2.025rem, 3.6vw, 3.15rem);
  --zesty-tilt: -3deg;

  /* How thick a mark's brush should be, in em of the words beside it.
     0.09em is the MEASURED stem width of the brush face itself (rendered at
     200px, median local run length through every opaque pixel = 18px), so a
     mark at this target and the letters next to it are drawn with visually
     the same pen. Raise it to make every mark heavier and larger together;
     it is the only size knob the marks have. */
  --zesty-stroke-target: 0.09;
  --zesty-gap: 0.45em;
  --zesty-color: var(--chartreuse);

  display: inline-block;

  font-family: "Custom Brush Script", var(--font-primary), sans-serif;
  font-size: var(--zesty-size);
  font-weight: 400;
  line-height: 1.05;
  color: var(--zesty-color);

  /* Display headings on this site are uppercased by text-transform, so a
     snippet placed inside or beside one inherits it and the whole point
     of writing these lowercase is lost. State the opposite explicitly. */
  text-transform: none;
  letter-spacing: 0;

  /* Never eat a click meant for the tile behind it. */
  pointer-events: none;

  /* SITS ON TOP OF ITS NEIGHBOURS, ALWAYS (2026-08-25, Kay).
     A snippet is decoration parked in another component's margin, so it has no
     say in the source order and can be painted over by any positioned sibling
     that happens to come later — a hero backdrop, a section's ::before wash, a
     sticky bar. `position:relative` is only here to make z-index apply; it
     changes no layout, because the element still occupies its normal box.
     `.zesty--hero` overrides position to absolute and keeps its own z-index. */
  position: relative;
  z-index: 3;
  user-select: none;
}

.zesty__inner {
  display: flex;
  align-items: center;
  gap: var(--zesty-gap);
  transform: rotate(var(--zesty-tilt));
  transform-origin: center;
}

.zesty__text {
  display: block;
  /* Capped on the WORDS, not the wrapper — a row layout's doodle would
     otherwise eat a third of the line box and wrap "we do a lot" onto
     three lines. The unit is em of the snippet's own size, so the wrap
     point tracks the type.

     5.5em is the wrap point at the current size: a short line ("we do a
     lot", "have a wander") stays on one, a long one breaks. It went down
     from 8em when the type doubled — 8em of a 67px face is 540px, wide
     enough for a hero aside to walk into the headline beside it. */
  max-width: 5.5em;
}

/* The doodle. currentColor through a mask, so `tone` recolours the
   drawing and the words together — see inc/zesty.php for why this is not
   an <img>. Only the PNG's alpha is read; its own colour is ignored.

   ⚠️ THE WIDTH IS DERIVED, NOT SET. Each drawing carries the fraction of
   its own width that its brush stroke occupies (`--zesty-doodle-stroke`,
   measured off the alpha channel and stored in refinery_zesty_doodles()).
   Scaling by target ÷ that fraction lands every mark on the same stroke
   weight as the type — which is the point: these were drawn with several
   different brushes, so one shared em width made the check and the plus
   read as a marker beside fine handwriting while the arrows read fine.

   Do not replace this with a per-mark em width. That is what it was
   before 2026-08-21, and it is why the marks all had to be re-judged by
   eye every time the type size moved. */
.zesty__mark {
  flex: 0 0 auto;
  display: block;
  width: calc(var(--zesty-stroke-target) / var(--zesty-doodle-stroke, 0.09) * 1em);
  aspect-ratio: var(--zesty-doodle-ratio, 1);
  background-color: currentColor;
  -webkit-mask: var(--zesty-doodle) center / contain no-repeat;
          mask: var(--zesty-doodle) center / contain no-repeat;

  /* Aim the drawing. Only two arrows were ever drawn — left and right — so
     every other direction is one of those spun. The box does not rotate
     with it, which is what keeps the line box (and the words beside it)
     from jumping when a mark is aimed. */
  rotate: var(--zesty-spin, 0deg);
}

/* Somewhere with no mask support the mark box would be a solid chartreuse
   rectangle — much worse than the drawing. Paint the PNG directly there;
   it is already brand chartreuse, so only `tone` is lost. */
@supports not ((-webkit-mask-image: url("")) or (mask-image: url(""))) {
  .zesty__mark {
    background-color: transparent;
    background: var(--zesty-doodle) center / contain no-repeat;
  }
}

/* --- Where the doodle sits ------------------------------------------ */
.zesty--mark-before .zesty__inner { flex-direction: row; }
.zesty--mark-after  .zesty__inner { flex-direction: row-reverse; }

.zesty--mark-above .zesty__inner,
.zesty--mark-below .zesty__inner {
  flex-direction: column;
  align-items: center;
}
.zesty--mark-above,
.zesty--mark-below { --zesty-gap: 0.3em; }
.zesty--mark-below .zesty__inner { flex-direction: column-reverse; }

/* --- Tone ------------------------------------------------------------ */
.zesty--chartreuse { --zesty-color: var(--chartreuse); }
.zesty--white      { --zesty-color: var(--white); }
.zesty--dark       { --zesty-color: var(--dark); }

/* --- Placement ------------------------------------------------------- */

/* Parked in the empty right-hand column of a hero, level with the
   headline. Needs a positioned ancestor — `.page-hero` carries
   `position:relative` where this is used.

   Vertically centred by stretching to the hero's full height and using
   flex, NOT by `top:50%; translateY(-50%)`: see the transform note above.

   It is decoration in dead space, so below the width where that dead
   space exists it is removed outright rather than folded into the
   reading order. */
.zesty--hero {
  position: absolute;
  /* Matches the base z-index above. Scoped to whatever positioned ancestor the
     hero provides, so this only orders it against that hero's own children. */
  z-index: 3;
  top: 0;
  bottom: 0;
  right: clamp(1.5rem, 6vw, 6rem);
  display: flex;
  align-items: center;
}

/* A tighter wrap point than the general 5.5em, because this is the one
   placement with a hard neighbour — a headline capped at 760px sitting
   immediately to its left. A long line wraps to two here and stays out of
   the title rather than running into it. */
.zesty--hero .zesty__text { max-width: 3.75em; }

/* Block-level, pushed to one side of whatever contains them.
   `width: fit-content` is load-bearing: a full-width box makes
   `margin-left:auto` a no-op and every one of these silently sits left. */
.zesty--right,
.zesty--left,
.zesty--center {
  display: block;
  width: fit-content;
}
.zesty--right  { margin-left: auto; }
.zesty--left   { margin-right: auto; }
.zesty--center { margin-inline: auto; }

/* --- Narrow screens --------------------------------------------------- */
/* No empty column left to sit in.

   ⚠️ THIS NUMBER IS TIED TO --zesty-size AND MOVES WITH IT. Re-derive it
   whenever the type or the stroke target changes:

     available  = 0.94W - 792     (viewport, less the 6vw `right` offset,
                                   less the headline's 32 + 760px)
     needed     = (mark + 0.45 gap + 3.75 text) em,  em = min(0.036W, 50.4)

   The binding mark is the WIDEST one in the registry, which is `bracket`
   at 3.15em (thin brush, so stroke-matching makes it long) — not the
   arrows anyone would actually park in a hero. At 1200px that needs 318px
   against 336px available, so every mark in the set clears. It was 1100px
   originally, 1360px while the type was doubled. */
@media (max-width: 1200px) {
  .zesty--hero { display: none; }
}

@media (max-width: 768px) {
  .zesty { --zesty-size: clamp(1.7rem, 6.8vw, 2.4rem); }
}

/* The tilt is static, but it is still a transform on text — honour the
   preference and sit the snippet straight. */
@media (prefers-reduced-motion: reduce) {
  .zesty { --zesty-tilt: 0deg; }
}

/* Kern-pair patch for the brush face. See refinery_zesty_kern() in
   inc/zesty.php for WHY this exists — CustomBrushScript has no `te` pair, so
   "notebook" sets as "not ebook". The span wraps the SECOND character of the
   pair and pulls it back onto the first.

   margin-left, not letter-spacing: letter-spacing on an inline element adds
   its value AFTER the last character too, which would push the following
   glyph out by the same amount and just move the gap one letter along.

   The value rides in on --zesty-kern (PHP sets it per pair) with the default
   duplicated here, so the rule still does something sane if the property is
   ever missing. Delete this rule AND the PHP helper together when the font is
   rebuilt with a real kern table. */
.zesty__kern {
  margin-left: var(--zesty-kern, -0.1em);
}
