/* GENERATED from ez-grade.css by scripts/build-deploy.mjs — do not edit by hand.
   Every .ez-scope rule is prefixed with body.ez-graded, a class added by
   mu-plugins/eyezon-sections.php, so the grade cannot leak onto pages that
   have not opted in. Re-run after any ez-grade.css change, then purge LiteSpeed. */

:root {
  --ez-grade-bg-i: #0F0D0A;
  --ez-grade-bg-i-rgb: 15, 13, 10;
  --ez-grade-bg-ii: #EDE9E2;
  --ez-grade-bg-ii-rgb: 237, 233, 226;
  --ez-grade-bg-iii: #100E0A;
  --ez-grade-bg-iii-rgb: 16, 14, 10;
  /* Accent, named by ROLE not hue, so a palette change cannot leave a variable
     called "emerald" holding amber. --ez-accent-rgb exists because rgba()
     cannot take a hex, and two translucent accent surfaces need the channels
     separately; deriving them from one place is the point of this refactor.
     --ez-accent-on is the label colour that sits ON an accent fill. */
  --ez-accent: #E8A33D;
  --ez-accent-rgb: 232, 163, 61;
  --ez-accent-hover: #F2B85C;
  --ez-accent-pressed: #C8862A;
  --ez-accent-muted: rgba(232, 163, 61, 0.18);
  --ez-accent-on: #04120C;
  /* Accent surfaces on LIGHT ground need their own value: #E8A33D measures
     1.78:1 on #EDE9E2 and cannot carry text at any size. This is the same
     hue family, darkened until it clears the floor with margin.
     Note the numerals were already failing before the palette flip — emerald
     gave 2.78:1 against a 3.0 large-text floor. This fixes that too. */
  --ez-accent-on-light: #7D4F10;
  --ez-accent-price: var(--ez-accent-on-light);
  --ez-grade-sodium: #C98A42;
  /* Midpoint for the light<->dark dissolves. sRGB interpolates #EDE9E2 to
     #0F0D0A straight through neutral grey — rgb(78,75,71) at the halfway
     point — and a chromaless band between two warm tones reads as fog.
     Tuned by measurement, not picked: scripts/tune-dissolve.mjs samples the
     composited band and counts how many rows fall below chroma 12, the point
     where a mid-tone stops reading as warm. The first value here, #544A3F,
     was still leaving 4-6 rows in that dead zone (min chroma 8, mean 15) —
     better than no stop, not actually warm. #6B5540 scores 0 dead rows with
     mean chroma 28 across every position tested.
     A token, not a literal in the gradients: it is a palette value and must
     move with the palette, same as --ez-accent. */
  --ez-dissolve-warm: #6B5540;
  --ez-grade-text-dark: #0B1115;
  --ez-grade-text-soft: #4F5D59;
  --ez-grade-text-light: #F6F2EB;
  /* Headings set their colour explicitly rather than inheriting it: the theme
     ships a bare `h1, h2, h3 { color: #343842 }`, and a direct rule on the
     element beats an inherited value from any ancestor no matter how specific
     the ancestor's selector is. Act II re-points this token for light ground. */
  --ez-grade-head: var(--ez-grade-text-light);
  --ez-grade-stroke: rgba(255,255,255,0.16);
  --ez-grade-dissolve: linear-gradient(90deg, rgba(var(--ez-grade-bg-i-rgb),0) 0%, rgba(var(--ez-grade-bg-i-rgb),0.12) 30%, rgba(var(--ez-grade-bg-i-rgb),0.4) 100%);
}

/* The page backdrop behind the grade. Keyed to the opt-in class, not to
   `home`: as `body.home` this leaked onto the real homepage the moment the
   stylesheet was enqueued and repainted all 16 of its sections near-black. */
body.ez-graded {
  background: var(--ez-grade-bg-i);
}

body.ez-graded .ez-scope {
  color: var(--ez-grade-text-light);
  background: transparent;
  font-family: 'Jost', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  line-height: 1.78;
  letter-spacing: .004em;
}

/* --------------------------------------------------------------------------
   PATCH 4 OF 4 ON THE SAME CONFLICT — read before adding a fifth.

   eyezon-custom.css lines 385-411 carry a global, unscoped
     .site, .page, p, li, dd, dt, blockquote, input, textarea, select,
     button, ... { font-family: "Inter", sans-serif !important; }
   and it is enqueued AFTER this stylesheet by design, so it wins every tie.
   The rule above sets Jost on .ez-scope without !important, and any
   !important beats any non-important declaration regardless of specificity,
   so body copy inside the grade rendered Inter — 21 text leaves, all <p>.

   These declarations answer it at (0,2,1) + !important once the build
   prefixes them with body.ez-graded, which beats `p` at (0,0,1). Scoped to
   the graded page, so this cannot reach any other page.

   This is the fourth patch on that block; it does not remove the cause. The
   real fix is retiring the global rules outright — tracked in STATUS.md as
   scheduled work (option B), to be done after homepage sign-off with its own
   verification pass, not folded into another change.

   The selector list deliberately mirrors the offending block rather than
   just <p>: only <p> is affected today, but a future section using <li> or
   <blockquote> would silently inherit the same defect.
   -------------------------------------------------------------------------- */
body.ez-graded .ez-scope p,
body.ez-graded .ez-scope li,
body.ez-graded .ez-scope dd,
body.ez-graded .ez-scope dt,
body.ez-graded .ez-scope blockquote,
body.ez-graded .ez-scope input,
body.ez-graded .ez-scope textarea,
body.ez-graded .ez-scope select,
body.ez-graded .ez-scope button {
  font-family: 'Jost', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
}

body.ez-graded .ez-scope * { box-sizing: border-box; }

body.ez-graded .ez-scope .ez-grade-shell {
  position: relative;
  overflow: hidden;
  background: transparent;
}

/* No vertical floor by default. The floor exists so an act with a photograph
   behind it fills the frame; a text-only act has no backdrop to hold up, so a
   floor there only manufactures empty space — and because the box centres its
   content, the surplus splits above AND below, doubling the apparent gap
   between one section's last line and the next section's first.
   Measured at 1440 before this change: zones carried 396px of slack, the
   stats band 416px. */
body.ez-graded .ez-scope .ez-act {
  position: relative;
  padding: clamp(3rem, 7vw, 5rem) clamp(1.25rem, 4vw, 2.5rem);
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  isolation: isolate;
}

/* The two acts that carry a full-bleed photograph and should read as frames. */
body.ez-graded .ez-scope .ez-act[data-act="i"],
body.ez-graded .ez-scope .ez-act[data-act="iii"] {
  min-height: clamp(24rem, 78vh, 40rem);
}

/* The stats band is photographed too, but four short cards are a band, not a
   full act. It gets a lower floor: enough for the image behind to read as a
   photograph, not so much that the figures float in the middle of a void. */
body.ez-graded .ez-scope .ez-act[data-act="i-stats"] {
  min-height: clamp(18rem, 46vh, 26rem);
}

body.ez-graded .ez-scope .ez-act[data-act="i"] {
  background: linear-gradient(180deg, rgba(var(--ez-grade-bg-i-rgb),0.96) 0%, rgba(var(--ez-grade-bg-i-rgb),0.92) 100%);
}

body.ez-graded .ez-scope .ez-act[data-act="ii"] {
  background: linear-gradient(180deg, rgba(var(--ez-grade-bg-ii-rgb),0.98) 0%, rgba(var(--ez-grade-bg-ii-rgb),0.97) 100%);
  color: var(--ez-grade-text-dark);
}

body.ez-graded .ez-scope .ez-act[data-act="iii"] {
  background: linear-gradient(180deg, rgba(var(--ez-grade-bg-iii-rgb),0.96) 0%, rgba(var(--ez-grade-bg-iii-rgb),0.9) 100%);
  background-color: var(--ez-grade-bg-iii);
}

body.ez-graded .ez-scope .ez-act::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 10%, rgba(201,138,66,0.15), transparent 45%);
  opacity: .9;
  pointer-events: none;
  z-index: 0;
}

body.ez-graded .ez-scope .ez-act::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 140px;
  background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.12) 50%, transparent 100%);
  pointer-events: none;
  z-index: 1;
  mix-blend-mode: screen;
}

/* The stylesheet default serves the FAQ -> Act III seam: ii-faq (#E4EAE7) is
   above, iii (#100E0A) below, so the ramp runs light -> dark. The Act I -> II
   dissolve is the mirror of this and carries its own inline stops.
   180deg, not 90deg: a dissolve blends the act above into the act below, so it
   must run top-to-bottom. At 90deg both blocks ran left-to-right, which left a
   hard edge where each end of the ramp met the wrong neighbour. */
/* The stylesheet default serves the ii-faq -> Act III seam. It carries the warm
   mid-stop for the same reason the zones dissolves do: without it the ramp runs
   #EDE9E2 -> #100E0A through neutral grey, measured at chroma 8-9 through its
   whole midsection, and it lands on a photographed act whose ground is warm.
   58%, not 50%: that is where #6B5540's own lightness falls between these two
   ends, so the ramp stays perceptually even instead of plunging early. */
/* 100px, not 140px. Both heights measured 0 rows below chroma 12 once the warm
   mid-stop was in, so the stop did the work and the extra 40px bought nothing —
   and at 390 a 140px band is over a fifth of a 640px viewport spent on a
   transition. Between two passing options, the tighter one. */
body.ez-graded .ez-scope .ez-dissolve {
  height: 100px;
  background: linear-gradient(180deg, var(--ez-grade-bg-ii) 0%, var(--ez-dissolve-warm) 58%, var(--ez-grade-bg-iii) 100%);
  width: 100%;
}

/* A dissolve sits between two sections that each carry their own vertical
   padding, so the seam was padding + 140px + padding. The dissolve replaces
   the adjacent padding rather than adding to it. Both dissolves are treated
   the same — the Act I->II seam has the identical defect, and fixing only the
   FAQ one would trade a spacing bug for an inconsistency. The sections are
   named explicitly rather than matched with :has() so the rule does not
   depend on selector support. */
   ii-pricing and ii-zones join the list for the two seams that bracket the
   dark zones act. The section BELOW each dissolve needs no entry here — the
   `.ez-dissolve + .ez-act` rule under this one already reduces its top
   padding, and it now matches zones and faq for free. */
body.ez-graded .ez-scope .ez-act[data-act="i-stats"],
body.ez-graded .ez-scope .ez-act[data-act="ii-pricing"],
body.ez-graded .ez-scope .ez-act[data-act="ii-zones"],
body.ez-graded .ez-scope .ez-act[data-act="ii-faq"],
body.ez-graded .ez-scope .ez-act[data-act="a1"],
body.ez-graded .ez-scope .ez-act[data-act="a3"] {
  padding-bottom: 0;
}

body.ez-graded .ez-scope .ez-dissolve + .ez-act {
  padding-top: clamp(3rem, 6vw, 4.5rem);
}

/* The ::after screen band is a 140px rgba(255,255,255,.12) sweep at the foot
   of every act. On Act II's light grounds it is imperceptible. On zones —
   the one dark act in that run — it lifts the blacks from rgb(8,7,5) to
   rgb(27,26,24), which put a visible edge where the band starts AND left the
   section brighter than the dissolve it hands off to, so the seam still read
   as a line after the plate was regenerated. Suppressed here only. */
body.ez-graded .ez-scope .ez-act[data-act="ii-zones"]::after {
  content: none;
}

body.ez-graded .ez-scope .ez-act[data-act="ii"]::after {
  background: linear-gradient(90deg, rgba(var(--ez-grade-bg-i-rgb),0.04) 0%, rgba(var(--ez-grade-bg-i-rgb),0.08) 50%, rgba(var(--ez-grade-bg-i-rgb),0.04) 100%);
}

body.ez-graded .ez-scope .ez-act > * {
  position: relative;
  z-index: 2;
}

body.ez-graded .ez-scope .ez-grid {
  width: min(1160px, 100%);
  display: grid;
  gap: clamp(1.25rem, 2vw, 2rem);
}

body.ez-graded .ez-scope .ez-stack {
  display: grid;
  gap: clamp(1rem, 2vw, 1.5rem);
}

/* Act III's kicker sits on dark ground and takes the accent. This was an inline
   colour literal on the element; moved here so the value reads from the token
   instead of being a hardcoded hex the next palette change would miss. */
body.ez-graded .ez-scope .ez-act[data-act="iii"] .ez-kicker {
  color: var(--ez-accent);
}

body.ez-graded .ez-scope .ez-kicker {
  text-transform: uppercase;
  letter-spacing: .24em;
  font-size: .78rem;
  font-weight: 600;
  color: rgba(255,255,255,0.72);
}

body.ez-graded .ez-scope .ez-act[data-act="ii"] .ez-kicker,
body.ez-graded .ez-scope .ez-act[data-act="ii"] .ez-muted {
  color: var(--ez-grade-text-soft);
}

/* `^="ii"` would also catch `iii`, which is dark ground — hence the explicit
   pair. Re-pointing the token here means the single heading rule below picks
   up the right colour per act instead of needing a per-act override. */
body.ez-graded .ez-scope .ez-act[data-act="ii"],
body.ez-graded .ez-scope .ez-act[data-act^="ii-"] {
  --ez-grade-head: var(--ez-grade-text-dark);
}

body.ez-graded .ez-scope h1,
body.ez-graded .ez-scope h2,
body.ez-graded .ez-scope h3 {
  color: var(--ez-grade-head);
}

/* eyezon-custom.css:424 sets `font-family: "Space Grotesk" !important` and
   `font-weight: 700 !important` on a bare h2..h6 list. !important vs
   !important is settled by specificity, and .ez-scope h1 (0,1,1) — which the
   build prefixes to body.ez-graded .ez-scope h1 (0,2,2) — outranks it. The
   !important here exists purely to reach that contest; it is not a
   specificity patch over our own rules. */
body.ez-graded .ez-scope h1,
body.ez-graded .ez-scope h2 {
  margin: 0;
  font-family: 'Jost', sans-serif !important;
  font-weight: 500 !important;
  line-height: 1.05;
  letter-spacing: -.02em;
}

body.ez-graded .ez-scope h1 {
  font-size: clamp(2.4rem, 5.6vw, 4.3rem);
}

body.ez-graded .ez-scope h2 {
  font-size: clamp(1.9rem, 4.7vw, 3.1rem);
}

body.ez-graded .ez-scope p,
body.ez-graded .ez-scope li,
body.ez-graded .ez-scope .ez-meta {
  margin: 0;
  color: inherit;
  font-size: 1rem;
}

body.ez-graded .ez-scope .ez-hero-copy {
  max-width: 700px;
}

body.ez-graded .ez-scope .ez-hero-copy .ez-lead {
  font-size: clamp(1.05rem, 1.7vw, 1.2rem);
  color: rgba(255,255,255,0.76);
}

body.ez-graded .ez-scope .ez-act[data-act="ii"] .ez-hero-copy .ez-lead {
  color: var(--ez-grade-text-soft);
}

/* ---- Act I hero: copy left 45%, image right 55% ----------------------
   The media carries a left-edge gradient that starts at the Act I ground
   colour and clears by 40%, so the copy column never meets image detail
   and the plate dissolves into the page instead of showing a hard seam. */
/* The media is no longer a grid cell — it is absolutely positioned against the
   section — so the grid carries the copy alone. */
body.ez-graded .ez-scope .ez-hero-grid {
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
  gap: 0;
}

body.ez-graded .ez-scope .ez-hero-grid .ez-hero-copy {
  position: relative;
  z-index: 2;
  max-width: 560px;
  padding-right: clamp(1.5rem, 3vw, 2.5rem);
}

/* F1 — full-bleed right half. Absolute against the section, so it clears the
   .ez-grid 1160px cap and the section's own padding and reaches the true
   viewport edge. Touches top, right and bottom; only the left edge dissolves,
   which is the one protecting the copy column.
   Not full-bleed behind the desktop copy: the h1 runs a per-character reveal,
   and characters travelling 50px across varying image luminance flicker in and
   out of legibility mid-animation. The split keeps that gesture clean. */
body.ez-graded .ez-scope .ez-hero-media {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: auto;
  width: 54%;
  z-index: 0;
  min-width: 0;
}

/* <picture> is an inline wrapper by default and would collapse the fill. */
body.ez-graded .ez-scope .ez-hero-media picture {
  display: block;
  width: 100%;
  height: 100%;
}

body.ez-graded .ez-scope .ez-hero-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* 60% rather than centre keeps the camera framed as the column narrows. */
  object-position: 60% 50%;
  border-radius: 0;
}

/* Left edge only. Fading all four turned the image into a floating plate; a
   plate reads as "content in a box", photography reads as photography. */
body.ez-graded .ez-scope .ez-hero-media::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(90deg, var(--ez-grade-bg-i) 0%, rgba(var(--ez-grade-bg-i-rgb), 0) 60%);
}

@media (max-width: 860px) {
  body.ez-graded .ez-scope .ez-hero-grid {
    grid-template-columns: 1fr;
    gap: clamp(1.5rem, 4vw, 2rem);
  }

  body.ez-graded .ez-scope .ez-hero-grid .ez-hero-copy {
    max-width: none;
    padding-right: 0;
  }

  /* Mobile is the opposite call to desktop: the image goes full-bleed BEHIND
     the copy, because at this width a side-by-side split leaves neither the
     text nor the photograph enough room. Legibility comes from a vertical
     scrim rather than from separation. Measured against rendered pixels — the
     image drawn into a canvas with this scrim over it — the brightest pixel
     behind the h1 gives 7.18:1, well clear of the 4.5:1 floor. */
  body.ez-graded .ez-scope .ez-hero-media {
    position: absolute;
    inset: 0;
    width: 100%;
  }

  /* The 9:16 mobile asset is graded darkest at the top, and that is the only
     band where the h1 clears 4.5:1 — measured 5.0:1 across the top quarter
     against 4.4:1 across the upper third. So the copy is pinned to the top of
     the section rather than centred in it; centring is what pushed the headline
     down into the lighter region. The image itself is unchanged by this. */
  body.ez-graded .ez-scope .ez-act[data-act="i"] {
    align-items: flex-start;
  }

  body.ez-graded .ez-scope .ez-act[data-act="i"] .ez-hero-copy {
    padding-top: 0;
  }

  body.ez-graded .ez-scope .ez-hero-media::after {
    background: linear-gradient(180deg, rgba(var(--ez-grade-bg-i-rgb), 0.55) 0%, rgba(var(--ez-grade-bg-i-rgb), 0.88) 65%, var(--ez-grade-bg-i) 100%);
  }
}

/* Both buttons already carry an identical 1px border (see .ez-btn below), so
   the usual ghost-vs-solid height mismatch does not apply here. These two
   declarations are belt-and-braces: align-items pins the boxes to one height
   even if a future variant changes padding, and border-box stops any added
   border widening the box instead of the padding. Verified by measuring
   getBoundingClientRect().height on both buttons — they must match exactly. */
body.ez-graded .ez-scope .ez-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: .9rem;
}

body.ez-graded .ez-scope a,
body.ez-graded .ez-scope button {
  color: var(--ez-accent);
  text-decoration: none;
}

body.ez-graded .ez-scope .ez-btn {
  display: inline-flex;
  box-sizing: border-box;
  align-items: center;
  justify-content: center;
  min-height: 2.8rem;
  padding: .85rem 1.2rem;
  border-radius: 999px;
  border: 1px solid var(--ez-accent-muted);
  background: transparent;
  color: var(--ez-accent);
  font-weight: 600;
}

body.ez-graded .ez-scope .ez-btn--primary {
  background: var(--ez-accent);
  color: var(--ez-accent-on);
  border-color: var(--ez-accent);
}

body.ez-graded .ez-scope .ez-btn:hover,
body.ez-graded .ez-scope .ez-btn:focus-visible {
  background: rgba(var(--ez-accent-rgb), 0.22);
}

body.ez-graded .ez-scope .ez-btn--primary:active {
  background: var(--ez-accent-pressed);
  color: var(--ez-accent-on);
}

body.ez-graded .ez-scope .ez-btn--primary:hover,
body.ez-graded .ez-scope .ez-btn--primary:focus-visible {
  background: var(--ez-accent-hover);
  color: var(--ez-accent-on);
}

body.ez-graded .ez-scope .ez-card {
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: 1.2rem;
  padding: 1.25rem;
  background: rgba(255,255,255,0.06);
  backdrop-filter: blur(12px);
}

body.ez-graded .ez-scope .ez-act[data-act="ii"] .ez-card {
  background: rgba(255,255,255,0.55);
  border-color: rgba(11,17,21,0.08);
}

body.ez-graded .ez-scope .ez-reveal {
  opacity: 1;
  transform: none;
  transition: opacity 420ms ease, transform 420ms ease;
}

body.ez-graded .ez-scope .ez-reveal.is-primed {
  opacity: 0;
  transform: translateY(10px);
}

body.ez-graded .ez-scope .ez-reveal.is-in {
  opacity: 1;
  transform: none;
}

body.ez-graded .ez-scope .ez-reveal:nth-child(2) { transition-delay: 70ms; }
body.ez-graded .ez-scope .ez-reveal:nth-child(3) { transition-delay: 140ms; }
body.ez-graded .ez-scope .ez-reveal:nth-child(4) { transition-delay: 210ms; }
body.ez-graded .ez-scope .ez-reveal:nth-child(5) { transition-delay: 280ms; }

/* Block entrances — measured from the live Secury theme: elements like
   div.elementor-element-*.animated-slow run `animation: 2s both fadeInUp`.
   Reimplemented as a transition, not a raw CSS animation, because
   animation-fill-mode: both applies its 0% keyframe (opacity:0) before the
   trigger fires too — exactly the invisible-without-JS state the fail-safe
   rule below exists to prevent. is-primed/is-in reproduces the same 2s
   entrance timing without that risk. */
body.ez-graded .ez-scope .ez-reveal-block {
  opacity: 1;
  transform: none;
  transition: opacity 2s ease, transform 2s ease;
}

body.ez-graded .ez-scope .ez-reveal-block.is-primed {
  opacity: 0;
  transform: translateY(10px);
}

body.ez-graded .ez-scope .ez-reveal-block.is-in {
  opacity: 1;
  transform: none;
}

/* Line/underline reveals — measured: scaleY 0 -> 1, no opacity change. */
body.ez-graded .ez-scope .ez-reveal-line {
  opacity: 1;
  transform: scaleY(1);
  transform-origin: top;
  transition: transform 420ms ease;
}

body.ez-graded .ez-scope .ez-reveal-line.is-primed {
  transform: scaleY(0);
}

body.ez-graded .ez-scope .ez-reveal-line.is-in {
  transform: scaleY(1);
}

/* Icon reveals — measured: opacity 0 -> 1 only, no transform. */
body.ez-graded .ez-scope .ez-reveal-icon {
  opacity: 1;
  transition: opacity 420ms ease;
}

body.ez-graded .ez-scope .ez-reveal-icon.is-primed {
  opacity: 0;
}

body.ez-graded .ez-scope .ez-reveal-icon.is-in {
  opacity: 1;
}

/* Character reveal — Act I h1 ONLY. Measured: per-character translateX
   50px -> 0, opacity 0 -> 1, 0.06s stagger, opacity 0.25s ease, transform
   0.45s cubic-bezier(0.26,-0.14,0,1.01). Per-span stagger delay is set
   inline by JS (transition-delay), not here. */
/* Keeps the inline-block character spans from breaking mid-word. */
body.ez-graded .ez-scope .ez-word {
  display: inline-block;
  white-space: nowrap;
}

body.ez-graded .ez-scope .ez-char {
  display: inline-block;
  opacity: 1;
  transform: none;
  transition: opacity 0.25s ease, transform 0.45s cubic-bezier(0.26, -0.14, 0, 1.01);
}

body.ez-graded .ez-scope .ez-char.is-primed {
  opacity: 0;
  transform: translateX(50px);
}

body.ez-graded .ez-scope .ez-char.is-in {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  body.ez-graded .ez-scope .ez-reveal,
  body.ez-graded .ez-scope .ez-reveal.is-primed,
  body.ez-graded .ez-scope .ez-reveal.is-in,
  body.ez-graded .ez-scope .ez-reveal-block,
  body.ez-graded .ez-scope .ez-reveal-block.is-primed,
  body.ez-graded .ez-scope .ez-reveal-block.is-in,
  body.ez-graded .ez-scope .ez-reveal-line,
  body.ez-graded .ez-scope .ez-reveal-line.is-primed,
  body.ez-graded .ez-scope .ez-reveal-line.is-in,
  body.ez-graded .ez-scope .ez-reveal-icon,
  body.ez-graded .ez-scope .ez-reveal-icon.is-primed,
  body.ez-graded .ez-scope .ez-reveal-icon.is-in,
  body.ez-graded .ez-scope .ez-char,
  body.ez-graded .ez-scope .ez-char.is-primed,
  body.ez-graded .ez-scope .ez-char.is-in,
  /* Chips: the JS guard already skips priming under reduced motion, so this is
     the second line of defence, not the only one — it also covers the case
     where .is-primed is already on the element when the preference changes
     mid-session. No stagger, everything visible immediately. */
  body.ez-graded .ez-scope .ez-zone-chip,
  body.ez-graded .ez-scope .ez-zone-chips.is-primed .ez-zone-chip,
  body.ez-graded .ez-scope .ez-zone-chips.is-in .ez-zone-chip {
    transition: none !important;
    transform: none !important;
    opacity: 1 !important;
    transition-delay: 0ms !important;
  }
}

body.ez-graded .ez-scope .ez-grain {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: .038;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="160" height="160" viewBox="0 0 160 160"><filter id="n"><feTurbulence type="fractalNoise" baseFrequency="0.75" numOctaves="2" seed="5"/></filter><rect width="100%" height="100%" filter="url(%23n)" opacity="0.35"/></svg>');
  mix-blend-mode: soft-light;
}

@media (prefers-reduced-motion: reduce) {
  body.ez-graded .ez-scope .ez-grain { display: none; }
}

body.ez-graded .ez-scope .ez-counter-num {
  font-family: 'Bebas Neue', -apple-system, sans-serif;
  font-size: clamp(2.8rem, 8vw, 4.8rem);
  font-weight: 500;
  color: transparent;
  -webkit-text-stroke: 1px var(--ez-accent);
  text-stroke: 1px var(--ez-accent);
  line-height: 1;
  display: block;
  letter-spacing: 0;
  margin-bottom: .65rem;
  transition: color 280ms ease, -webkit-text-stroke-color 280ms ease, text-stroke-color 280ms ease;
}

body.ez-graded .ez-scope .ez-counter-num.is-filled {
  color: var(--ez-accent);
  -webkit-text-stroke-color: transparent;
  text-stroke-color: transparent;
}

/* Bebas Neue is numerals-only. Any trailing word sits in Jost and skips the
   stroke-digit treatment, which applies to digits. */
body.ez-graded .ez-scope .ez-counter-word {
  font-family: 'Jost', sans-serif;
  font-weight: 500;
  color: var(--ez-accent);
  -webkit-text-stroke: 0;
  text-stroke: 0;
}

body.ez-graded .ez-scope .ez-counter-label {
  font-size: .95rem;
  color: inherit;
  font-weight: 600;
}

/* Section 2 — Trust stats (Act I, night — #0A1114 with #0F181B cards).
   24/7 is not a count-up value ("/" would corrupt the digit parse in the
   counter script), so it's a static equivalent of .ez-counter-num.is-filled
   rather than a real .ez-counter-num. */
body.ez-graded .ez-scope .ez-stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: clamp(1rem, 2.5vw, 1.5rem);
}

body.ez-graded .ez-scope .ez-stat-card {
  background: #0F181B;
  border-radius: 14px;
  padding: 1.5rem 1.25rem;
  text-align: center;
}

body.ez-graded .ez-scope .ez-stats-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: .3;
  pointer-events: none;
}

/* Zones background. Same geometry as .ez-stats-bg, deliberately NOT the same
   opacity: this asset ships tone-matched to #0F0D0A with its vignette and its
   top/bottom fade bands already burnt in, so the .3 above would be a second
   darkening layer over a pre-darkened image and would crush it. Any future
   "let's add a scrim for contrast" here is the wrong fix — remeasure instead. */
/* ---- /about/ Section 2 camera plate ----
   Unlike Section 1's band this IS a text background. The asset is already
   tone-matched to --ez-grade-bg-i, so it takes NO scrim: contrast is earned by
   PLACING the copy where the photograph is dark, not by dimming the
   photograph. Measured clear regions — right half/upper on desktop, top third
   on mobile — are what the rules below target.
   Section 2 sits on bg-i for this reason; see the markup comment. */
body.ez-graded .ez-scope .ez-act[data-act="a2"] {
  align-items: flex-start;   /* .ez-act centres by default; the copy sits high */
  overflow: hidden;
}

body.ez-graded .ez-scope .ez-s2-media {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

body.ez-graded .ez-scope .ez-s2-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

body.ez-graded .ez-scope .ez-act[data-act="a2"] .ez-grid {
  position: relative;
  z-index: 1;
  width: 100%;
}

/* DESKTOP — full-bleed plate, copy in the right half, NO vertical constraint.
   The re-graded asset dims the street lamp and the lit path on the right while
   leaving the camera untouched, so the whole right half now carries white copy.
   Measured on the raw frame, white #F6F2EB against the right half at the 95th
   percentile (the lightest realistic ground, not the mean):
       original    45-100% band  7.74:1     re-graded  14.61:1
       original    full right    11.04:1    re-graded  15.10:1
   The earlier 1672/423 crop existed only to keep copy out of that lower band.
   With the band fixed in the asset, the crop is no longer needed and the plate
   fills the section again. Still NO scrim — the grade was done in the image. */
@media (min-width: 861px) {
  body.ez-graded .ez-scope .ez-s2-media img {
    object-position: left center;
  }
  body.ez-graded .ez-scope .ez-act[data-act="a2"] {
    min-height: clamp(30rem, 42vw, 40rem);
  }
  body.ez-graded .ez-scope .ez-act[data-act="a2"] .ez-grid {
    max-width: 1160px;
  }
  body.ez-graded .ez-scope .ez-s2-copy {
    margin-left: auto;
    margin-right: 0;
    max-width: 44%;
  }
}

/* MOBILE — the header-band pattern from Section 1, not an overlay.
   The 4:5 crop puts the camera directly under the text, so there is no band
   tall enough to hold three paragraphs. The plate leaves the flow's background
   entirely and becomes an in-flow band above the copy, which then sits on
   solid ground. More reliable than shaving the copy into a 30% strip, and it
   makes Sections 1 and 2 read as one system on a phone. */
@media (max-width: 860px) {
  body.ez-graded .ez-scope .ez-act[data-act="a2"] {
    flex-direction: column;
    align-items: stretch;
  }
  body.ez-graded .ez-scope .ez-s2-media {
    /* relative, not static: it returns to the flow but must still be the
       containing block for the ::after hairline, which would otherwise
       resolve against the section and ring the whole act. */
    position: relative;
    width: 100%;
    aspect-ratio: 4 / 5;
    border-radius: 16px;
    margin-bottom: clamp(1rem, 4vw, 1.5rem);
  }
  /* Matches Section 1's band so the two read as the same component. */
  body.ez-graded .ez-scope .ez-s2-media::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    border: 1px solid rgba(var(--ez-accent-rgb), .14);
    pointer-events: none;
  }
  body.ez-graded .ez-scope .ez-s2-copy {
    max-width: 100%;
    margin: 0;
  }
}

/* ---- /about/ Section 1 header band ----
   A lit shopfront, measured ~1.2:1 overall. It carries NO text at any position
   and takes NO scrim: a scrim would raise contrast but flatten the lit-sign
   effect the photograph depends on. The copy sits below it on solid
   --ez-grade-bg-i, which is why this is a header band and not a background.

   Same framed treatment as the zones plate — 20px/16px radius, amber hairline,
   the same shadow — but in FLOW, not absolutely positioned: it is the first
   child of .ez-grid (900px) while the text stack is 760px, so the band reads
   wider than the copy it heads, the same overhang that frames the zones panel. */
body.ez-graded .ez-scope .ez-about-band {
  position: relative;
  width: 100%;
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
}

/* Hairline on its own layer above the image. CLAUDE.md: an inset shadow on the
   wrapper paints BEHIND the wrapper's content, and an <img> is content, so the
   ring would vanish under the photograph. Border on the ::after, not
   box-shadow: inset, per the brief. Token-derived so it follows the palette. */
body.ez-graded .ez-scope .ez-about-band::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid rgba(var(--ez-accent-rgb), .14);
  pointer-events: none;
}

/* aspect-ratio is declared per breakpoint as well as via the width/height
   attributes: <picture> swaps a 21:9 source for a 4:5 one, and the attributes
   on <img> only describe the desktop file, so without this the mobile swap
   would reserve the wrong box and shift the page. */
body.ez-graded .ez-scope .ez-about-band img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 21 / 9;
  object-fit: cover;
}

@media (max-width: 700px) {
  body.ez-graded .ez-scope .ez-about-band {
    border-radius: 16px;
  }
  body.ez-graded .ez-scope .ez-about-band img {
    aspect-ratio: 4 / 5;
  }
}

/* The plate is a framed panel, not wallpaper. Absolutely positioned, so none of
   this touches the flow — the kicker, H2, map, chips and tail keep their exact
   positions.
   left/right are the section's own horizontal padding and margin-inline:auto
   centres it, so the panel is a frame around the content rather than a column
   matching it. At 1080 it is deliberately wider than BOTH content columns —
   .ez-grid is 900 (the map) and .ez-stack is 700 (kicker, H2, chips) — which
   is what gives the framed read. Measured x=180 w=1080 at 1440.
   Two bounds, both verified, before changing this number:
     floor   900px — the map lives in .ez-grid and would overflow a narrower
                     panel. The map is a SIBLING of this element, so widening
                     the panel never moves a marker; markers follow .ez-grid.
     ceiling ~1355px — 1440 minus the section's 42.5px padding either side.
                     At or past that the panel goes edge-to-edge and stops
                     reading as a frame at all. */
body.ez-graded .ez-scope .ez-zones-media {
  position: absolute;
  top: 48px;
  bottom: 48px;
  left: clamp(1.25rem, 4vw, 2.5rem);
  right: clamp(1.25rem, 4vw, 2.5rem);
  max-width: 1080px;
  margin-inline: auto;
  border-radius: 20px;
  overflow: hidden;
  z-index: 0;
  pointer-events: none;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
}

/* The hairline goes on a pseudo-element, not in the wrapper's box-shadow. An
   inset shadow paints behind the element's content, so with the plate <img>
   sitting in that content it would be invisible. This layer is positioned
   above the image and inherits the radius. Token-derived, not a literal, so
   it follows a palette change. */
body.ez-graded .ez-scope .ez-zones-media::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(var(--ez-accent-rgb), .14);
  pointer-events: none;
}

/* Mobile needs the panel WIDER than the content, not padded inside it. What
   gives desktop its breathing room is the overhang: the panel is the 900px grid
   while the kicker, H2 and chips sit in a 700px stack, so 100px of frame shows
   either side. At 390 the grid and the stack are both 334px, so that overhang
   is zero and the text lands flush on the frame edge.
   Padding the panel would only shrink the plate inside it — the text is not a
   child of the panel, it is a sibling — so the fix is to let the panel out to
   the section edge. left/right:0 against the section's 20px horizontal padding
   yields exactly 20px of overhang per side, matching desktop's relationship at
   this width. The panel goes horizontally full-bleed as a result; the 14px
   radius, 28px vertical inset and drop shadow still read it as a card. */
@media (max-width: 640px) {
  body.ez-graded .ez-scope .ez-zones-media {
    top: 28px;
    bottom: 28px;
    left: 0;
    right: 0;
    border-radius: 14px;
  }
}

/* Same trap as the hero at line ~326: <picture> is an inline wrapper by
   default and would collapse the fill, so the img would size to nothing. */
body.ez-graded .ez-scope .ez-zones-media picture {
  display: block;
  width: 100%;
  height: 100%;
}

body.ez-graded .ez-scope .ez-zones-bg {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  opacity: 1;
  display: block;
}

/* 0.20, not 0.4. act3-cta.webp carries bright CCTV monitor tiles through its
   middle third, and at 0.4 they sat directly behind the quietest type on the
   page. Measured per text block — p95 luminance of the real composited pixels
   inside each block's box, text hidden so the glyphs themselves are not sampled
   — three blocks failed 4.5:1 at 0.4 while the section as a whole passed. That
   is why a section-wide figure is the wrong instrument here.
   The binding block is the footer legal line at 1440: 3.45:1 at 0.4, 4.11 at
   0.3, 4.48 at 0.25, 4.92 at 0.2. So 0.2 is the HIGHEST opacity that clears the
   floor everywhere — chosen deliberately over 0.15 to keep as much of the
   photograph as the contrast budget allows. */
body.ez-graded .ez-scope .ez-cta-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: .2;
  pointer-events: none;
}

/* At 390 the CTA photo crops so a brighter region sits behind the kicker,
   which is the smallest type in Act III: measured 4.45:1 at opacity .2, which
   is 0.05 short of the floor. This must come AFTER the base rule — the two
   have equal specificity, so source order is what decides. Desktop stays .2. */
@media (max-width: 860px) {
  body.ez-graded .ez-scope .ez-cta-bg {
    opacity: .15;
  }
}

body.ez-graded .ez-scope .ez-stat-static {
  font-family: 'Bebas Neue', -apple-system, sans-serif;
  font-size: clamp(2.8rem, 8vw, 4.8rem);
  font-weight: 500;
  color: var(--ez-accent);
  line-height: 1;
  display: block;
  letter-spacing: 0;
  margin-bottom: .65rem;
}

body.ez-graded .ez-scope .ez-glow {
  position: absolute;
  inset: auto;
  width: clamp(10rem, 30vw, 22rem);
  height: clamp(10rem, 30vw, 22rem);
  background: radial-gradient(circle, rgba(201,138,66,0.24) 0%, rgba(201,138,66,0.04) 55%, transparent 72%);
  pointer-events: none;
  z-index: 0;
}

body.ez-graded .ez-scope .ez-glow--one { top: 8%; left: -4%; }
body.ez-graded .ez-scope .ez-glow--two { bottom: 8%; right: -4%; }

body.ez-graded .ez-scope .ez-act[data-act="ii"] .ez-glow {
  background: radial-gradient(circle, rgba(201,138,66,0.28) 0%, rgba(201,138,66,0.06) 50%, transparent 74%);
}

/* Section 3 — Three segments. Colours per EYEZON-HOMEPAGE-CONTENT.md:
   background #E4EAE7 (shared with existing Act II), H2 #0D1614,
   body #4B5854, cards #F1F5F2. */
body.ez-graded .ez-scope .ez-persona-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(1.25rem, 3vw, 2rem);
}

body.ez-graded .ez-scope .ez-persona-card {
  background: #F1F5F2;
  border-radius: 14px;
  padding: 1.5rem;
  display: grid;
  gap: .75rem;
  align-content: start;
  box-shadow: -8px 12px 28px rgba(13, 22, 20, 0.12);
}

body.ez-graded .ez-scope .ez-persona-card h3 {
  margin: 0;
  font-family: 'Jost', sans-serif !important;
  font-weight: 500 !important;
  font-size: 1.15rem;
  color: #0D1614;
}

body.ez-graded .ez-scope .ez-persona-card p {
  margin: 0;
  color: #4B5854;
  font-size: .95rem;
}

body.ez-graded .ez-scope .ez-persona-meta {
  color: #4B5854;
  font-size: .85rem;
  font-weight: 600;
}

/* Retained deliberately although no card currently uses it. The three persona
   photographs return as one set once the families install shot exists; until
   then all three cards are heading-first so the row reads as a decision rather
   than a missing asset. The --placeholder variant was deleted, not commented
   out: an empty tinted box beside two photographs is the stand-in the release
   scope explicitly rules out. */
body.ez-graded .ez-scope .ez-persona-image {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: 14px;
  display: block;
}

/* Section 10 — brand signature. Two weights and two colours turn the repeated
   taglines into a lockup instead of two leftover lines of body text. The rule
   above is the only emerald in Act III's footer block, so it reads as a mark
   rather than a divider. */
body.ez-graded .ez-scope .ez-lockup {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .4rem;
  text-align: center;
  padding-top: 1.25rem;
}

body.ez-graded .ez-scope .ez-lockup::before {
  content: "";
  width: 40px;
  height: 1px;
  background: var(--ez-accent);
  margin-bottom: .45rem;
}

body.ez-graded .ez-scope .ez-lockup-primary {
  font-family: 'Jost', sans-serif;
  font-weight: 500;
  font-size: 1.05rem;
  color: #F7F2EB;
}

body.ez-graded .ez-scope .ez-lockup-secondary {
  font-family: 'Jost', sans-serif;
  font-weight: 400;
  font-size: .92rem;
  letter-spacing: .08em;
  color: #B7AE9E;
}

body.ez-graded .ez-scope .ez-lockup-legal {
  margin-top: .6rem;
  font-size: .85rem;
  color: #B7AE9E;
}

/* Section 4 — How it works. Numbered steps in Bebas Neue/emerald, connecting
   line uses .ez-reveal-line (scaleY 0 -> 1, no opacity change per spec). */
body.ez-graded .ez-scope .ez-process-band {
  width: 100%;
  /* The img carries width/height attributes, which act as presentational
     hints. With both dimensions definite, aspect-ratio is ignored and the
     band renders at its full 1086px intrinsic height. */
  height: auto;
  aspect-ratio: 3 / 1;
  object-fit: cover;
  border-radius: 14px;
  display: block;
}

body.ez-graded .ez-scope .ez-steps {
  display: grid;
  gap: 0;
}

body.ez-graded .ez-scope .ez-step {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 1.25rem;
}

body.ez-graded .ez-scope .ez-step-marker {
  display: grid;
  justify-items: center;
  gap: .35rem;
}

/* On light ground — see --ez-accent-on-light. */
body.ez-graded .ez-scope .ez-step-num {
  font-family: 'Bebas Neue', -apple-system, sans-serif;
  font-size: 1.8rem;
  font-weight: 500;
  color: var(--ez-accent-on-light);
  line-height: 1;
}

body.ez-graded .ez-scope .ez-step-line {
  width: 2px;
  height: 2.5rem;
  background: var(--ez-accent);
}

body.ez-graded .ez-scope .ez-step-body {
  padding-bottom: 1.75rem;
}

body.ez-graded .ez-scope .ez-step-body h3 {
  margin: 0 0 .35rem;
  font-family: 'Jost', sans-serif !important;
  font-weight: 500 !important;
  font-size: 1.05rem;
  color: #0D1614;
}

body.ez-graded .ez-scope .ez-step-body p {
  margin: 0;
  color: #4B5854;
  font-size: .95rem;
}

/* Section 6 — Pricing ladder. Prices in Bebas Neue, --ez-accent-price, per spec. No
   motion specified for this section in the content doc — single block
   reveal only, no per-row stagger invented. */
body.ez-graded .ez-scope .ez-pricing-list {
  display: grid;
  gap: 0;
  max-width: 700px;
  margin: 0 auto;
  width: 100%;
}

body.ez-graded .ez-scope .ez-pricing-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: .25rem 1rem;
  padding: 1rem 0;
  border-bottom: 1px solid rgba(13, 22, 20, 0.1);
}

body.ez-graded .ez-scope .ez-pricing-name {
  grid-column: 1;
  font-weight: 600;
  color: #0D1614;
}

body.ez-graded .ez-scope .ez-pricing-price {
  grid-column: 2;
  grid-row: 1;
  justify-self: end;
  font-family: 'Bebas Neue', -apple-system, sans-serif;
  font-size: 1.6rem;
  font-weight: 500;
  color: var(--ez-accent-price);
}

body.ez-graded .ez-scope .ez-pricing-for {
  grid-column: 1 / -1;
  color: #4B5854;
  font-size: .9rem;
}

/* The theme's fixed header is 75px at desktop and 50px at mobile (measured on
   the live page, class .site-header.clone, z-index 1100). It floats over the
   page, so anything that scrolls this section flush to the viewport top —
   an anchor, scrollIntoView, a deep link — parked the header over the northern
   markers and hid Mango, Dimna and the shop pin, which is the single marker
   this section exists to show.
   scroll-margin-top and not padding: padding would add permanent dead space
   for every reader, when the problem only occurs at the scroll landing point.
   Clamped so it clears 75px at desktop and still clears 50px at mobile. */
body.ez-graded .ez-scope .ez-act[data-act="ii-zones"] {
  scroll-margin-top: clamp(3.75rem, 6vw, 5.5rem);
}

/* Section 7 — Zones. Chips #F1F5F2 with #0D1614 text per spec. */
body.ez-graded .ez-scope .ez-zone-chips {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  max-width: 700px;
  margin: 0 auto;
  width: 100%;
}

body.ez-graded .ez-scope .ez-zone-chip {
  background: #F1F5F2;
  color: #0D1614;
  padding: .5rem 1rem;
  border-radius: 999px;
  font-size: .9rem;
  font-weight: 600;
  /* Identical signature to .ez-reveal — same durations, same easing, so the
     chips read as the same motion language as everything else on the page. */
  transition: opacity 420ms ease, transform 420ms ease;
}

/* Staggered chip entrance.
   No JS was added: .ez-zone-chips already carries .ez-reveal, so the existing
   IntersectionObserver primes it and flips it to .is-in — the same observer,
   in the same pass, that starts the map's marker pulses. The stagger is pure
   CSS keyed off the container's state.

   Fail-safe, and it is the reason this is built inside-out: the chips have NO
   opacity rule of their own, so their default state is visible. Only the
   observer's .is-primed hides them, and the JS applies that solely inside the
   `!reduceMotion && 'IntersectionObserver' in window` guard. JS off, IO
   missing, or reduced motion: chips render at full opacity and never animate.
   Nothing here can gate visibility on JS. */
body.ez-graded .ez-scope .ez-zone-chips.is-primed .ez-zone-chip {
  opacity: 0;
  transform: translateY(8px);
}

body.ez-graded .ez-scope .ez-zone-chips.is-in .ez-zone-chip {
  opacity: 1;
  transform: none;
}

/* The container itself must NOT also fade. It is a .ez-reveal, so it would run
   its own 420ms opacity ramp over the top and wash the stagger out into one
   blur. Specificity (0,4,0) to beat .ez-scope .ez-reveal.is-primed at (0,3,0). */
body.ez-graded .ez-scope .ez-zone-chips.ez-reveal.is-primed {
  opacity: 1;
  transform: none;
}

body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(1)  { transition-delay: 0ms; }
body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(2)  { transition-delay: 70ms; }
body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(3)  { transition-delay: 140ms; }
body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(4)  { transition-delay: 210ms; }
body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(5)  { transition-delay: 280ms; }
body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(6)  { transition-delay: 350ms; }
body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(7)  { transition-delay: 420ms; }
body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(8)  { transition-delay: 490ms; }
body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(9)  { transition-delay: 560ms; }
body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(10) { transition-delay: 630ms; }
body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(11) { transition-delay: 700ms; }
body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(12) { transition-delay: 770ms; }
body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(13) { transition-delay: 840ms; }
body.ez-graded .ez-scope .ez-zone-chips .ez-zone-chip:nth-child(14) { transition-delay: 910ms; }

/* Section 8 — FAQ. No heading given in the content doc, plain Q&A list. */
body.ez-graded .ez-scope .ez-faq-list {
  display: grid;
  gap: 1.5rem;
}

body.ez-graded .ez-scope .ez-faq-item h3 {
  margin: 0 0 .5rem;
  font-family: 'Jost', sans-serif !important;
  font-weight: 500 !important;
  font-size: 1.05rem;
  color: #0D1614;
}

body.ez-graded .ez-scope .ez-faq-item p {
  margin: 0;
  color: #4B5854;
  font-size: .95rem;
}

/* ==========================================================================
   Ken Burns — the four photographed acts.
   ==========================================================================
   Time-based only. There is deliberately no scroll listener: scroll-linked
   parallax is the thing that costs frames on a mid-range Android, and this
   effect does not need scroll position to work.

   FAIL-SAFE: this is pure CSS and entirely additive. No element below has a
   transform in its default state, so with this whole block removed — or on a
   browser that ignores it — the images sit exactly where they do now. No JS is
   involved at all, so there is nothing that can half-run.

   Desktop only. Everything lives inside a min-width query so phones never
   allocate the compositor layers, never pay the battery cost, and never carry
   the will-change hint.

   Clipping: each animated image needs an ancestor with overflow:hidden or the
   7% scale spills past the section edge. Those are set alongside each rule
   rather than centrally, so the pairing stays visible.
   ========================================================================== */

@media (min-width: 900px) {

  /* Hero — SCALE ONLY, no translate, ever.
     The left edge of this image is tone-matched to the Act I ground so the
     dissolve lands on an exact colour match. Any horizontal movement drags
     that matched edge across the ramp and the seam comes back. Scaling from
     the centre keeps the left edge where it is. */
  @keyframes ez-kb-hero {
    from { transform: scale(1); }
    to   { transform: scale(1.07); }
  }

  body.ez-graded .ez-scope .ez-hero-media {
    overflow: hidden;
  }

  body.ez-graded .ez-scope .ez-hero-media img {
    will-change: transform;
    animation: ez-kb-hero 24s ease-in-out infinite alternate;
  }

  /* Stats background. Translate stays inside the 1.5% budget. */
  @keyframes ez-kb-gate {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(-1%, -0.6%, 0) scale(1.07); }
  }

  body.ez-graded .ez-scope .ez-act[data-act="i-stats"] {
    overflow: hidden;
  }

  body.ez-graded .ez-scope .ez-stats-bg {
    will-change: transform;
    /* Negative delays rather than positive: the offset still breaks the unison,
       but every image is already in motion at first paint instead of sitting
       still for up to nine seconds. */
    animation: ez-kb-gate 24s ease-in-out -3s infinite alternate;
  }

  /* Process band. Horizontal drift only — the band is wide and short, so
     vertical movement reads as a wobble. */
  @keyframes ez-kb-process {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(1.2%, 0, 0) scale(1.07); }
  }

  body.ez-graded .ez-scope .ez-kb-frame {
    display: block;
    overflow: hidden;
    border-radius: 14px;
  }

  body.ez-graded .ez-scope .ez-process-band {
    will-change: transform;
    animation: ez-kb-process 24s ease-in-out -6s infinite alternate;
  }

  /* Act III CTA background. */
  @keyframes ez-kb-cta {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(-0.8%, -1%, 0) scale(1.07); }
  }

  body.ez-graded .ez-scope .ez-act[data-act="iii"] {
    overflow: hidden;
  }

  body.ez-graded .ez-scope .ez-cta-bg {
    will-change: transform;
    animation: ez-kb-cta 24s ease-in-out -9s infinite alternate;
  }
}

/* No motion at all, and drop the will-change so the compositor layers are
   released rather than merely idling. */
@media (prefers-reduced-motion: reduce) {
  body.ez-graded .ez-scope .ez-hero-media img,
  body.ez-graded .ez-scope .ez-stats-bg,
  body.ez-graded .ez-scope .ez-process-band,
  body.ez-graded .ez-scope .ez-cta-bg {
    animation: none;
    will-change: auto;
    transform: none;
  }
}


/* ==========================================================================
   Coverage map — regenerated, copied from assets/map/ez-map.css.
   ==========================================================================
   Do not hand-edit here; edit the generator and re-drop, then re-run
   scripts/install-map.mjs. TWO local changes are applied on every copy, so a
   fresh drop cannot reintroduce either:

   1. The generator emits .ez-grid for the map's gridline group, which is also
      the page layout wrapper's class. Renamed to .ez-map-grid here and in the
      SVG.
   2. The generator writes var(--ez-grade-accent, ...). That token does not
      exist — the accent token is --ez-accent — so every accent in the map fell
      back to the literal hex and was decoupled from the palette. It looked
      right only because the fallback matched the then-current amber. Repointed
      to --ez-accent.
   ========================================================================== */
/* EYEZON coverage map — scoped, additive, fail-safe.
   All colours read from tokens with fallbacks, so a palette
   switch (amber <-> emerald) needs no edit here. */

body.ez-graded .ez-scope .ez-map {
  width: 100%;
  height: auto;
  display: block;
  font-family: Jost, system-ui, sans-serif;
}

/* Semi-transparent, not opaque. The zones section now carries a photographic
   background; a solid fill here read as a hard-edged black slab sitting on top
   of that texture. fill-opacity rather than a baked rgba() so the hue still
   comes from the token and follows a palette change. */
body.ez-graded .ez-scope .ez-map-bg  { fill: var(--ez-grade-bg-i, #0F0D0A); fill-opacity: .55; }

/* The plate underneath already carries street texture and a river, so the SVG's
   own line work was a second, conflicting geography drawn over the first.
   Hidden in CSS rather than cut from the SVG because the file is generated
   upstream and a hand-edit would be overwritten by the next drop.
   NOTE the selector is .ez-map-grid, NOT .ez-grid — .ez-grid is the page's
   layout wrapper, and display:none on that would blank every section on the
   page. That collision is exactly why the map's group was renamed.
   Markers, labels, shop pin and halo are deliberately kept. */
body.ez-graded .ez-scope .ez-map-grid,
body.ez-graded .ez-scope .ez-rivers,
body.ez-graded .ez-scope .ez-river,
body.ez-graded .ez-scope .ez-confluence,
body.ez-graded .ez-scope .ez-rlabel {
  display: none;
}

body.ez-graded .ez-scope .ez-map-grid line {
  stroke: var(--ez-map-grid, rgba(255,255,255,.045));
  stroke-width: 1;
}

body.ez-graded .ez-scope .ez-river {
  fill: none;
  stroke: url(#ez-river);
  stroke-width: 9;
  stroke-linecap: round;
  opacity: .85;
}
body.ez-graded .ez-scope .ez-river--minor { stroke-width: 6; opacity: .7; }

body.ez-graded .ez-scope .ez-confluence {
  fill: var(--ez-map-river, #1E3A44);
  stroke: rgba(255,255,255,.22);
  stroke-width: 1;
}
body.ez-graded .ez-scope .ez-rlabel {
  fill: rgba(255,255,255,.34);
  font-size: 12px;
  letter-spacing: .06em;
}

/* --- zone markers: visible by default, animation is additive --- */
body.ez-graded .ez-scope .ez-dot {
  fill: var(--ez-accent, #E8A33D);
}
body.ez-graded .ez-scope .ez-ping {
  fill: none;
  stroke: var(--ez-accent, #E8A33D);
  stroke-width: 1.5;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
}
body.ez-graded .ez-scope .ez-zlabel {
  fill: rgba(255,255,255,.78);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: .01em;
}

/* --- shop pin --- */
body.ez-graded .ez-scope .ez-shop-pin {
  fill: var(--ez-accent, #E8A33D);
}
body.ez-graded .ez-scope .ez-shop-eye  { fill: var(--ez-grade-bg-i, #0F0D0A); }
body.ez-graded .ez-scope .ez-shop-label {
  fill: #FFF;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .04em;
}

/* --- animation: only once the section is in view --- */
body.ez-graded .ez-scope .ez-map.is-in .ez-ping {
  animation: ez-map-ping 3.2s cubic-bezier(.26,-.14,0,1.01) infinite;
  animation-delay: var(--d, 0s);
}
@keyframes ez-map-ping {
  0%   { opacity: .85; transform: scale(.55); }
  70%  { opacity: 0;   transform: scale(2.6); }
  100% { opacity: 0;   transform: scale(2.6); }
}

body.ez-graded .ez-scope .ez-map.is-in .ez-shop-halo {
  animation: ez-map-breathe 4.5s ease-in-out infinite;
  transform-box: fill-box;
  transform-origin: center;
}
@keyframes ez-map-breathe {
  0%,100% { opacity: .55; transform: scale(1); }
  50%     { opacity: 1;   transform: scale(1.12); }
}

/* NOTE: font-size inside a viewBox is in USER-SPACE units, not CSS px.
   At 390 viewport the scale is ~0.37, so 14 units renders ~5px. Raising
   the size enough to be legible (~35 units) collides badly. The zone
   chips directly below the map already carry every name legibly, so the
   in-map labels are hidden on small screens instead. */
@media (max-width: 640px) {
  body.ez-graded .ez-scope .ez-zlabel,
  body.ez-graded .ez-scope .ez-rlabel { display: none; }
  body.ez-graded .ez-scope .ez-shop-label { font-size: 22px; }
  body.ez-graded .ez-scope .ez-dot  { r: 6; }
  body.ez-graded .ez-scope .ez-ping { stroke-width: 2.5; }
}

@media (prefers-reduced-motion: reduce) {
  body.ez-graded .ez-scope .ez-map.is-in .ez-ping,
  body.ez-graded .ez-scope .ez-map.is-in .ez-shop-halo {
    animation: none;
  }
  body.ez-graded .ez-scope .ez-ping { opacity: .35; }
}


/* ==========================================================================
   /about/ — type scale only.
   ==========================================================================
   Scoped to .ez-about so the homepage is untouched. Every structural class on
   that page is the homepage's; only the sizes differ, and they are pinned to
   the exact desktop/mobile endpoints in the spec rather than reusing the
   homepage's clamps, which top out slightly higher (h1 68.8px, h2 49.6px).
   Specificity is (0,2,1) against the base rules' (0,1,1), and the base
   font-family/weight !important declarations are deliberately not fought —
   they already set Jost 500, which is what this page wants.
   ========================================================================== */
body.ez-graded .ez-scope.ez-about .ez-kicker {
  font-size: clamp(0.8125rem, 1vw, 0.875rem);   /* 13 -> 14px */
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
}

body.ez-graded .ez-scope.ez-about h1 {
  font-size: clamp(2.375rem, 5vw, 4rem);        /* 38 -> 64px */
  line-height: 1.05;
}

body.ez-graded .ez-scope.ez-about h2 {
  font-size: clamp(1.875rem, 3.6vw, 2.75rem);   /* 30 -> 44px */
  line-height: 1.1;
}

body.ez-graded .ez-scope.ez-about p {
  font-size: clamp(1.0625rem, 1.4vw, 1.1875rem); /* 17 -> 19px */
  font-weight: 400;
  line-height: 1.65;
}

body.ez-graded .ez-scope.ez-about .ez-step-body h3 {
  font-size: clamp(1.1875rem, 1.7vw, 1.375rem);  /* 19 -> 22px */
  line-height: 1.25;
}

body.ez-graded .ez-scope.ez-about .ez-step-body p {
  font-size: clamp(1rem, 1.3vw, 1.0625rem);      /* 16 -> 17px */
}

/* The one numeral on the page. Bebas is numerals-only across the site, so it
   is named here and nowhere else on /about/. */
body.ez-graded .ez-scope.ez-about .ez-about-figure-row {
  display: flex;
  align-items: baseline;
  gap: .75rem;
  flex-wrap: wrap;
}

body.ez-graded .ez-scope.ez-about .ez-about-figure {
  font-family: 'Bebas Neue', -apple-system, sans-serif;
  font-size: clamp(3rem, 6vw, 4.5rem);           /* 48 -> 72px */
  font-weight: 500;
  line-height: 1;
  color: var(--ez-accent);
  letter-spacing: .01em;
}

body.ez-graded .ez-scope.ez-about .ez-about-figure-label {
  font-size: clamp(1.0625rem, 1.4vw, 1.1875rem);
  color: #B7AE9E;
}

/* The tel: link renders 97x23px as inline text — one pixel under WCAG 2.5.8
   Target Size (Minimum), which asks for 24x24 CSS px. inline-block plus 2px of
   block padding takes it to 27px without moving the line or changing the type.
   Scoped to /about/: the homepage carries the same 97x23 link and is out of
   scope for this change, logged in STATUS instead. */
body.ez-graded .ez-scope.ez-about a[href^="tel:"] {
  display: inline-block;
  padding-block: 2px;
}

/* Same suppression the zones section already carries. That 140px
   rgba(255,255,255,.12) screen band at the foot of every act is invisible on
   Act II's light grounds but lifts the blacks on a dark one, drawing a visible
   horizontal edge — here across the coverage panel and behind the closing CTA.
   Applied to every .ez-about act rather than naming two: this page alternates
   grounds, and a per-section list would need editing every time a section
   changes colour. */
body.ez-graded .ez-scope.ez-about .ez-act::after {
  content: none;
}

/* ---- /services/ package grid and speciality list ----
   TWO new components, and the only ones this page needed.

   Everything else on /services/ reuses the homepage vocabulary — .ez-act,
   .ez-grid, .ez-stack, .ez-kicker, .ez-actions, .ez-btn, .ez-card,
   .ez-dissolve, .ez-reveal. These two exist because nothing in the grade
   could carry them:

     .ez-pkg   six cards, each with an eyebrow, name, price, description, a
               contents list, an AMC line and its own CTA. .ez-pricing-row is
               a flat name/price/for line on a 1fr/auto grid and cannot hold
               a list or a button.
     .ez-svc   five tappable cards that are whole-card links to the speciality
               pages. .ez-card is a static container with no link affordance.

   Both sit on --ez-grade-bg-ii (light ground), so they take the LIGHT-ground
   accent token. #E8A33D measures 1.78:1 on #EDE9E2 and cannot carry text —
   --ez-accent-on-light exists for exactly this and is used throughout below. */
body.ez-graded .ez-scope .ez-pkg-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: clamp(1rem, 2vw, 1.5rem);
  width: 100%;
}

body.ez-graded .ez-scope .ez-pkg {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: .6rem;
  padding: 1.5rem 1.4rem 1.4rem;
  border-radius: 1.2rem;
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid rgba(13, 22, 20, 0.10);
}

/* The recommendation card. A ring, not a fill: a filled card would out-weigh
   the other five and read as the only option rather than the usual one. */
body.ez-graded .ez-scope .ez-pkg--pick {
  border-color: rgba(var(--ez-accent-rgb), 0.55);
}

/* OUT OF FLOW, deliberately. As a flex child this badge pushed every element
   below it down by 30px on the one card that has it — label, price, description
   and contents list all sat 30px lower than the same rows on the other five,
   and the six price lines shared no baseline. Measured: yLabel 55 vs 25,
   yPrice 123 vs 93.
   Absolutely positioned so the six cards have identical internal geometry
   whether a badge is present or not, and adding a badge to another card can
   never shift its contents. It straddles the top border and sits in the grid
   gap (24px desktop, 16px mobile), so it collides with nothing above.
   Filled rather than bare text because it crosses the card edge: half of it
   sits on the card, half on the section ground, and only a solid fill reads
   consistently across both. --ez-accent-on on --ez-accent measures 8.88:1. */
body.ez-graded .ez-scope .ez-pkg-badge {
  position: absolute;
  top: -0.72rem;
  left: 1.4rem;              /* flush with the card's own padding-left */
  margin: 0;
  padding: 0.16rem 0.6rem;
  border-radius: 999px;
  background: var(--ez-accent);
  color: var(--ez-accent-on);
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  white-space: nowrap;
}

body.ez-graded .ez-scope .ez-pkg-for {
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #4B5854;
}

body.ez-graded .ez-scope .ez-pkg-name {
  margin: 0;
  color: #0D1614;
  font-size: 1.5rem;
  font-weight: 500;
  line-height: 1.2;
}

/* One string, not four elements. The Elementor original split "Starting from",
   "₹", "8,999" and "onwards" across separate nodes, which rendered as
   "₹8,999onwards" with no space. */
body.ez-graded .ez-scope .ez-pkg-price {
  font-family: 'Bebas Neue', 'Jost', sans-serif;
  font-size: 2rem;
  line-height: 1;
  letter-spacing: .01em;
  color: var(--ez-accent-on-light);
}

body.ez-graded .ez-scope .ez-pkg-desc {
  margin: 0;
  color: #4B5854;
}

body.ez-graded .ez-scope .ez-pkg-box {
  margin-top: .2rem;
  padding-top: .8rem;
  border-top: 1px solid rgba(13, 22, 20, 0.10);
}

body.ez-graded .ez-scope .ez-pkg-box-h {
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #4B5854;
  margin-bottom: .5rem;
}

body.ez-graded .ez-scope .ez-pkg-box ul {
  margin: 0;
  padding-left: 1.1rem;
  display: grid;
  gap: .35rem;
}

body.ez-graded .ez-scope .ez-pkg-box li {
  color: #4B5854;
  font-size: .95rem;
  line-height: 1.55;
}

body.ez-graded .ez-scope .ez-pkg-amc {
  margin-top: .2rem;
  font-size: .9rem;
  color: #4B5854;
}

/* Pushes the CTA to the card foot so six cards of unequal length still line
   their buttons up. */
body.ez-graded .ez-scope .ez-pkg .ez-actions {
  margin-top: auto;
  padding-top: .8rem;
  width: 100%;
}

/* FULL WIDTH, not text width. .ez-btn is an inline-flex sized by its label, so
   the six buttons came out at six widths — 211, 213, 218, 231, 234 and 249px —
   purely because "Get Industrial Fortress quote" is longer than "Get Home Guard
   quote". Six ragged buttons down a card grid read as unfinished.
   100% rather than a fixed px so they stay identical at every breakpoint and
   through any future rename; a fixed width would only be correct until someone
   changes a package name. Same approach as the exit popup's CTA. */
body.ez-graded .ez-scope .ez-pkg .ez-btn {
  width: 100%;
}

body.ez-graded .ez-scope .ez-pkg-foot {
  font-size: .82rem;
  color: #4B5854;
}

/* ---- speciality list ---- */
body.ez-graded .ez-scope .ez-svc-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: clamp(.75rem, 1.6vw, 1.1rem);
  width: 100%;
}

body.ez-graded .ez-scope .ez-svc {
  display: block;
  padding: 1.2rem 1.2rem 1.3rem;
  border-radius: 1rem;
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid rgba(13, 22, 20, 0.10);
  text-decoration: none;
  transition: border-color 160ms ease, transform 160ms ease;
}

body.ez-graded .ez-scope .ez-svc:hover,
body.ez-graded .ez-scope .ez-svc:focus-visible {
  border-color: rgba(var(--ez-accent-rgb), 0.55);
  transform: translateY(-2px);
}

body.ez-graded .ez-scope .ez-svc h3 {
  margin: 0 0 .4rem;
  color: #0D1614;
  font-size: 1.15rem;
  font-weight: 500;
}

body.ez-graded .ez-scope .ez-svc p {
  margin: 0;
  color: #4B5854;
  font-size: .95rem;
  line-height: 1.6;
}

@media (prefers-reduced-motion: reduce) {
  body.ez-graded .ez-scope .ez-svc { transition: none; }
  body.ez-graded .ez-scope .ez-svc:hover,
  body.ez-graded .ez-scope .ez-svc:focus-visible { transform: none; }
}

/* ---------------------------------------------------------------------------
   /projects/ — six property-type blocks, per projects-page-spec.md.

   DESKTOP: copy sits ON the image, inside the dark zone MEASURED for that
   photograph. --ez-zone is set per block in the markup (62% for house,
   apartment, shop-counter, warehouse; 55% for office and industrial) and is
   the left edge of the copy column. There are NO scrims: legibility comes
   from the image itself, 4.9:1 at the worst measured band. Replacing an image
   invalidates its zone -- re-measure before moving the copy.

   MOBILE (<=860px): header-band. The picture element swaps to the 4:5 crop,
   the copy leaves the image entirely and sits on solid --ez-grade-bg-i. No
   block carries text over its image at 390.
--------------------------------------------------------------------------- */
body.ez-graded .ez-scope .ez-proj-act {
  /* .ez-act is display:flex and centres its children, so a block declaring
     width:100% still shrank to content (240px) and the copy overflowed. This
     section stacks full-bleed images instead, so it opts out of the flex box.
     Scoped to .ez-proj-act: every other Act keeps the shared behaviour. */
  display: block;
  padding-left: 0;
  padding-right: 0;
}
body.ez-graded .ez-scope .ez-proj-blk {
  position: relative;
  width: 100%;
  isolation: isolate;
}
body.ez-graded .ez-scope .ez-proj-blk + .ez-proj-blk {
  margin-top: 2px;
}
body.ez-graded .ez-scope .ez-proj-blk picture,
body.ez-graded .ez-scope .ez-proj-blk img {
  display: block;
  width: 100%;
  height: auto;
}
body.ez-graded .ez-scope .ez-proj-blk-copy {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: var(--ez-zone, 62%);
  right: 0;
  padding: 0 clamp(1.25rem, 4vw, 3.5rem) 0 clamp(1rem, 2.5vw, 2rem);
}
body.ez-graded .ez-scope .ez-proj-blk-copy h2 {
  margin: 0 0 .6rem;
  color: #F7F2E9;
  font-size: clamp(1.05rem, 1.9vw, 1.8rem);
  line-height: 1.15;
}
body.ez-graded .ez-scope .ez-proj-blk-copy p {
  margin: 0;
  color: #D8D0C2;
  font-size: clamp(.82rem, 1.05vw, 1rem);
  line-height: 1.55;
  max-width: 34ch;
}

@media (max-width: 860px) {
  body.ez-graded .ez-scope .ez-proj-blk {
    display: block;
  }
  body.ez-graded .ez-scope .ez-proj-blk-copy {
    position: static;
    transform: none;
    left: auto;
    right: auto;
    background: var(--ez-grade-bg-i);
    padding: 1.5rem clamp(1.1rem, 5vw, 2rem) 2.25rem;
  }
  body.ez-graded .ez-scope .ez-proj-blk-copy p {
    max-width: 60ch;
  }
  body.ez-graded .ez-scope .ez-proj-blk + .ez-proj-blk {
    margin-top: 0;
  }
}

/* ---------------------------------------------------------------------------
   Package hardware line (services-final-corrections.md section 3).

   Sits between the box contents and the AMC line, in the slot the retention
   claims used to occupy. It is deliberately quieter than the AMC line: this
   is the answer to "how long does it keep footage", and the honest answer
   routes to the site visit rather than to a number.
--------------------------------------------------------------------------- */
body.ez-graded .ez-scope .ez-pkg-retention {
  margin-top: .55rem;
  font-size: .86rem;
  line-height: 1.5;
  color: color-mix(in srgb, currentColor 78%, transparent);
  max-width: 46ch;
}
