/*
 * OnyxInsights Flex — report request form.
 *
 * Styles the Gravity Form embedded by the request-form band on the six Flex
 * report detail pages. Three pages, one report per submission:
 *
 *   page 1  Choose the scope that fits your need.
 *   page 2  Tell us about your request.
 *   page 3  Review your selections.
 *
 * Enqueued by the theme layer in inc/oi-flex-request-form.php, in the `theme`
 * group, which is what puts it after Gravity Forms' Orbital stylesheets.
 * BOCO-24033 (D16) moved it out of the form definition; BOCO-24038 (D19) gave
 * it the card and the two columns.
 *
 * ---------------------------------------------------------------------------
 * THE MOBILE FRAME IS A SECOND SPEC, NOT A SCALED-DOWN FIRST ONE
 * ---------------------------------------------------------------------------
 * Almost every rule here was measured against the 1440 card (`6796:3634` and
 * down). The 390 frame — `6796:4199` and down, on the mobile page `6796:3958` —
 * draws the *same* elements at its own sizes, and BOCO-24089 items 6 and 7 are
 * the first pass to take them: the progress title, the step title, the step
 * intro and the divider's padding all differ, and none of them differ by a
 * ratio. So a value here that has no phone counterpart has not been checked
 * against one; it has not been ruled out either.
 *
 * The phone query in this file is **560**, and the band around the form switches
 * at Elementor's **767**. That is not an inconsistency to tidy — a stylesheet
 * cannot move where an Elementor `_mobile` control takes effect. Figma draws no
 * frame between 391 and 1024, so 561-767 is extrapolation on both sides.
 *
 * There are now **five** widths where something in this file changes, and only
 * three of them are queries. In descending order: the two grid tracks flex
 * continuously from 1360 down (no query — an `fr` ratio, BOCO-24089 round 5),
 * `1024` steps the card's and the two panels' padding to match where the *band*
 * steps, `767` hands the status box to the bottom sheet, `640` keeps the
 * reporting period in a row of its own (BOCO-24721), and `560` is the phone
 * type ramp. A change that belongs to one of those is almost never a change to
 * another; check which before adding a query.
 *
 * **640 is the odd one and is not this component's width at all** — it is
 * Gravity Forms' own grid breakpoint, below which every field is full width. It
 * is in this list so nobody tidies it into 560 or 767; the rule that uses it
 * says why at length.
 *
 * ---------------------------------------------------------------------------
 * WHAT DOES *NOT* BELONG IN THIS FILE
 * ---------------------------------------------------------------------------
 * Field geometry, colour and typography — the height, padding, radius, border,
 * font and label of every input, select, textarea and button. Gravity Forms
 * styles all of those from its own custom properties, so they are set as CSS
 * API properties in `oiflex_form_css_properties()` and the form's style
 * settings, not as rules here. The long comment at the top of
 * inc/oi-flex-request-form.php explains why, and it is not a stylistic
 * preference: a rule here has to out-specify both Orbital and the Vamtam
 * Elementor kit, and setting the property Gravity Forms reads sidesteps the
 * contest entirely.
 *
 * What belongs here is the `.gfrr-*` components — markup this component adds
 * itself, which collides with nothing — and the card and column layout.
 *
 * ---------------------------------------------------------------------------
 * WHY EVERY RULE IS `:has(.gfrr-step-head)`
 * ---------------------------------------------------------------------------
 * `.gform_wrapper` is Gravity Forms' class, not ours, and nothing stops a second
 * Gravity Form sharing a report detail page — an event form, a newsletter block.
 * A bare `.gform_wrapper` rule here would give that form this one's 1200 card,
 * two-column grid and white panel.
 *
 * The form id would be the precise scope, but it is deliberately not a literal
 * anywhere in this component — it lives in the `request_form_id` ACF field, read
 * in PHP. `.gfrr-step-head` is the next best thing and is exactly equivalent in
 * practice: it is markup only this form's HTML fields emit, and it is present on
 * all three steps, which are all in the DOM at once.
 *
 * This mirrors `isOurs()` in the JS, which gates the delegated listeners on the
 * same question for the same reason.
 */

/*
 * ⚠️ `.gfrr-confirm` JOINS THIS SELECTOR LIST AND NO OTHER, ON PURPOSE.
 *
 * The confirmation screen (BOCO-24040) renders *after* Gravity Forms has run
 * `jQuery('#gform_wrapper_8').replaceWith(…)`, so by then neither
 * `.gform_wrapper` nor `.gfrr-step-head` is in the document and this selector's
 * first half matches nothing. The confirmation reads the same brand tokens, so
 * it is added here rather than given a block of its own — a second copy of the
 * palette is the quiet-duplication trap CLAUDE.md is emphatic about, and it
 * would surface as a colour drifting on one screen only.
 *
 * The *other* rule carrying this same selector — the card grid immediately
 * below — deliberately does not gain `.gfrr-confirm`: the confirmation is a
 * single centred card with no status column beside it, so it brings its own
 * box rather than inheriting a two-column grid it would have to undo.
 *
 * ⚠️ ONE HONEST QUALIFICATION, RAISED IN REVIEW. The confirmation block below
 * says three times that this screen "inherits nothing" and "brings its own".
 * That is true of every *layout* rule and false of this one: **the palette and
 * `--gfrr-card-width` are shared, and they are shared through a selector list
 * whose other member uses `:has()`.** A selector list is not forgiving — if any
 * selector in the group fails to parse, the whole rule is dropped — so an
 * engine that could not parse `:has()` would take the tokens away from
 * `.gfrr-confirm` too. `inline-size: var(--gfrr-card-width)` would then compute
 * to `auto` and collapse the card to the ~682px the block below warns about,
 * and the medallion and dots would go transparent.
 *
 * **Left as it is, deliberately, and the reasoning is the point.** The failure
 * is unreachable here: `:has()` is baseline, and the whole component — every
 * rule in this file — already requires it, so an engine that cannot parse it
 * never renders the form the customer had to submit to reach this screen. Both
 * available fixes cost more than the risk. Splitting `.gfrr-confirm` into its
 * own block duplicates the palette, which is the trap named above. Wrapping the
 * first selector in `:where()` decouples the parse but silently drops that rule
 * from (0,2,0) to (0,0,0); and moving the `:where()` inside — as
 * `.gform_wrapper:where(:has(…))` — is worse still, because an ignored `:has()`
 * inside a forgiving list makes the selector match **every** `.gform_wrapper`
 * on the page, which is the exact bare-wrapper bug the top of this file exists
 * to prevent.
 *
 * So the coupling is real, it is one-directional, and it is written down here
 * rather than designed away. If `--gfrr-*` ever moves to `:root`, this note is
 * the reason it would be safe to.
 */
.gform_wrapper:has(.gfrr-step-head),
.gfrr-confirm {
  /* ---- Brand palette, resolved from the Elementor global colours the theme
     defines rather than restated as hex. The literal after each comma is the
     fallback for a context that renders this form outside an Elementor page,
     where the global custom properties are not emitted; an unresolved var()
     with no fallback computes to transparent and the form loses its colour
     silently. The fallbacks are the same values the kit holds today. */
  --gfrr-ink: var(--e-global-color-vamtam_accent_1, #071c2b);
  --gfrr-teal: var(--e-global-color-vamtam_accent_2, #72d0eb);
  --gfrr-muted-surface: var(--e-global-color-vamtam_accent_3, #f2f2f2);
  --gfrr-field-surface: var(--e-global-color-vamtam_accent_5, #ffffff);

  /* ---- Neutrals sampled from the mocks. These are not brand colours and have
     no token to bind to, so they stay literal. */
  --gfrr-rule: #c2c5c8;
  --gfrr-required: #d1284c;

  /* The reporting-period chip's fill (D43, Figma 7335:711). A light cyan tint
     and the only one in the component. **It is not a mix of `--gfrr-teal`** —
     that would be #b9e8f5 and this is #c5ecf8 — and it is none of the eight
     `vamtam_accent_*` globals, checked rather than assumed. So it is a sampled
     literal like the two above, and a `color-mix()` "tidy-up" would change the
     colour design chose. */
  --gfrr-chip-surface: #c5ecf8;

  /* Body copy. This was a color-mix of the ink until the mock was measured;
     Figma states it outright (#374A55 on 6796:3646 and 6796:3688), so it is a
     sampled neutral like the others rather than something derived. */
  --gfrr-muted: #374a55;
  --gfrr-on-ink: var(--e-global-color-vamtam_accent_5, #ffffff);
  --gfrr-on-ink-muted: rgba(255, 255, 255, 0.68);

  --gfrr-radius: 24px;
  --gfrr-radius-sm: 16px;

  /*
   * ---- The card and its two columns (D19 / Figma 6796:3634).
   *
   * THE TWO COLUMN TOKENS ARE `fr` WEIGHTS, NOT THE LENGTHS THEY READ AS, and
   * the numbers are still Figma's. At the card's full 1200 the 1120 of inner
   * width less the 32 gap is 1088, which `728fr : 360fr` divides into exactly
   * 728 and 360 — so the design's own two figures are what the ratio is written
   * from, and the layout at 1360 and above is the measured one to the pixel.
   * Below 1360 the same ratio shrinks both columns rather than collapsing them.
   * BOCO-24089 round 5; the argument is at *The two columns hold to 768* below.
   *
   * `--gfrr-col-aside-min` is the floor that stops the ratio taking the status
   * panel below what its own content needs. Measured, not chosen: the panel's
   * `min-content` is 230.4 on all six reports at its base 28px padding, and the
   * binding child is `.gfrr-status-total` — the label's longest word, the 12px
   * gap, and the `white-space: nowrap` figure. 240 clears that with the padding
   * reduced to 20 in the side-by-side window, leaving 200 of content against a
   * needed 174.4. The headroom is for the figure, which is *data*: every report
   * starts from $499.00 today, and a longer price makes that child wider.
   */
  --gfrr-card-width: 1200px;
  --gfrr-card-pad-x: 40px;
  --gfrr-card-pad-y: 48px;
  --gfrr-col-form: 728fr;
  --gfrr-col-aside: 360fr;
  --gfrr-col-aside-min: 240px;
  --gfrr-col-gap: 32px;
}

/* ------------------------------------------------------------ the form card */

/*
 * Figma 6796:3634: a 1200 card, 48/40 padding, so 1120 of inner width, split
 * 728 + 32 + 360 across a form column and a status-box column.
 *
 * The grid lives on `.gform_wrapper` rather than on Gravity Forms' own
 * `.gform_fields`, and D19 gives the structural reason: that grid re-flows per
 * row of fields, so a sidebar beside *every* row is not something it can
 * express. The wrapper is the only ancestor of both the fields and the footer.
 *
 * The card itself is not painted. Figma draws it #F2F2F2 inside a band panel
 * that is already #F2F2F2, so the visible card is the white form column below;
 * this is a width and a grid, nothing more.
 *
 * THE SECOND COLUMN IS FILLED as of BOCO-24036 (sub-task 07, D17/D18), by
 * `.gfrr-status` below. It arrives as a direct child of `.gform_wrapper` from
 * `gform_form_after_open` in inc/oi-flex-request-form-status.php — not the
 * Gravity Forms HTML field D17 assumed, which lands inside `.gform_fields`,
 * three levels down, where it cannot be a grid item here.
 *
 * BOTH TRACKS FLEX (BOCO-24089 round 5). The form column's `minmax(0, …)` is
 * what it always was — a zero floor, so a wide field or a long word overflows
 * the *element* rather than pushing the track wider than its share. The aside's
 * floor is a length, because a track that keeps shrinking past its content is
 * the failure this replaced a collapse with. See the token block above for
 * where 240 comes from.
 */
.gform_wrapper:has(.gfrr-step-head) {
  max-width: var(--gfrr-card-width);
  margin-inline: auto;
  padding: var(--gfrr-card-pad-y) var(--gfrr-card-pad-x);
  display: grid;
  grid-template-columns:
    minmax(0, var(--gfrr-col-form))
    minmax(var(--gfrr-col-aside-min), var(--gfrr-col-aside));
  column-gap: var(--gfrr-col-gap);
  align-items: start;
}

/*
 * The wrapper-level siblings of the form, pinned to the form's column.
 *
 * `.gform_heading` is the required-fields legend, which would otherwise take a
 * grid row of its own above the card.
 *
 * `.gform_validation_errors` is Gravity Forms' failed-submission banner
 * (`form_display.php:1349`, emitted between `.gform_anchor` and `.gform_heading`
 * on any invalid postback) and it is the **one wrapper child nothing here
 * positions**. Measured, it currently lands above the form at both 1440 and 900
 * — so this is not a bug being fixed but the last unpinned child being pinned,
 * because auto-placement is the wrong thing to be relying on for a banner that
 * carries `autofocus` and is the scroll target for a validation failure. Left to
 * the auto-flow it would move the day another wrapper-level child appears.
 */
.gform_wrapper:has(.gfrr-step-head) .gform_heading,
.gform_wrapper:has(.gfrr-step-head) .gform_validation_errors {
  grid-column: 1;
}

/*
 * BOCO-24089 item 2 — the legend goes. No frame draws it: it is absent from the
 * mobile page (`6796:3958`, and `6796:4203` opens straight on the progress
 * block) and from the 1440 card (`6796:3635`), so this is not a mobile-only
 * removal and it is not scoped to a query.
 *
 * `.gform_required_legend` rather than the `.gform_heading` that wraps it,
 * because the wrapper is a real positioned grid child above — the shortcode
 * passes `title="false" description="false"`, so the legend is all the wrapper
 * holds *today*, and hiding the wrapper would silently swallow a title or
 * description the day someone turns one on.
 *
 * ---------------------------------------------------------------------------
 * WHY THIS IS CSS AND NOT THE FILTER IT LOOKS LIKE
 * ---------------------------------------------------------------------------
 * `gform_required_legend` (form-scoped as `gform_required_legend_8`) is the
 * obvious answer and it does not work: `form_display.php:1385` emits
 * `<p class='gform_required_legend'>{$required_legend}</p>` **unconditionally**
 * once `$display_required_legend` is true, so an empty return leaves an empty
 * `<p>` in the grid — the spacing moves rather than closing.
 *
 * The other route is `$form['requiredIndicator'] = 'text'` via `gform_pre_render`
 * (`form_display.php:1352-1353` is the gate), which does drop the legend
 * entirely — but it also replaces every field's asterisk with the word
 * "(Required)", which the mock draws as an asterisk throughout. So the only
 * mechanism that removes the legend and nothing else is this one.
 *
 * Nothing accessible is lost with it. The legend explains the asterisk; what
 * actually carries "this field is required" to assistive technology is
 * `aria-required` on the controls, which Gravity Forms emits either way and this
 * file does not touch.
 */
.gform_wrapper:has(.gfrr-step-head) .gform_required_legend {
  display: none;
}

/*
 * The white panel. Figma 6796:3637: 728 wide, radius 24, 40/48 padding, and a
 * one-pixel drop shadow. It holds the progress bar, the fields and the footer,
 * which is why the panel is the <form> rather than an inner wrapper.
 */
.gform_wrapper:has(.gfrr-step-head) form {
  grid-column: 1;
  background: var(--gfrr-field-surface);
  border-radius: var(--gfrr-radius);
  padding: 40px 48px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}

/* ------------------------------------------------------------- status box */

/*
 * Figma 6796:3694: a 360 panel, radius 24, 28 padding, 20 between blocks,
 * top-aligned beside the 728 form panel. BOCO-24036 (D3, D5, D6, D7, D17, D18);
 * the markup is built in inc/oi-flex-request-form-status.php.
 *
 * `align-self: start` is deliberate and is the design, not a shortcut: the box
 * is 384.59 tall inside an 803-tall row. The *column* spans the form; the box
 * does not stretch to it.
 *
 * ---------------------------------------------------------------------------
 * WHY `order: 1`, WHICH IS THE ONE LINE HERE THAT IS LOAD-BEARING
 * ---------------------------------------------------------------------------
 * The box is rendered on `gform_form_after_open`, which fires immediately after
 * the opening wrapper div — so it is the wrapper's **first** DOM child. Gravity
 * Forms fires nothing between `</form>` and the wrapper's closing `</div>`, so
 * first-child is the only wrapper-level insertion point there is.
 *
 * Grid auto-placement would then put the box in **row 1**, beside
 * `.gform_anchor`, and row 1's height would become the box's own — pushing
 * `.gform_heading` and the whole form **414px** down the page. That is measured,
 * not predicted: delete the `order` line and `verify-24036.mjs` reports the box
 * 414px above the form's top. The wrapper's children, in order, are two
 * `<style>` elements, `.gform_anchor`, `.gform_heading`, `<form>`.
 *
 * Auto-placement runs in **order-modified document order**, so one declaration
 * makes the box the last item placed. Its column is definite (2) and the search
 * starts from the cursor left by the form, so it lands in the form's own row —
 * exactly the placement a hook after the form would have given, expressed where
 * the layout already lives. Nothing needs to know how many rows there are.
 *
 * It used to earn its keep twice. Until BOCO-24089 round 5 the grid collapsed to
 * one column at 1359 and this line was also what made the box fall *beneath*
 * the form rather than above it. That layout is gone — the two columns now hold
 * to 768 and the sheet takes over below — so the 414px regression is the whole
 * of what this line prevents, at **every** width from 768 up rather than only
 * the widest. The assertion moved with it: `verify-24036.mjs` used to measure
 * the stacking at 900 and now measures the row placement there instead.
 *
 * ---------------------------------------------------------------------------
 * The mock paints this panel with a texture (`6796:3694` has an IMAGE fill,
 * imageRef 5bb360ab) over the navy. Flat ink is shipped instead: the texture is
 * an asset that would have to be exported and committed through
 * `figma-assets/`, which nothing on the form side does today. Recorded as an
 * intent finding rather than approximated with a gradient — the panel reads
 * correctly flat, and a wrong texture is worse than none.
 */
.gfrr-status {
  grid-column: 2;
  order: 1;
  align-self: start;
  display: flex;
  flex-direction: column;
  gap: 20px;
  border-radius: var(--gfrr-radius);
  padding: 28px;
  background: var(--gfrr-ink);
  color: var(--gfrr-on-ink);
}

/*
 * The mobile sheet's collapsed bar, and everything it needs to *not* be at any
 * other width. BOCO-24037 / D20; the bar itself is styled in the
 * `max-width: 767px` block far below, which is where the whole of that design
 * lives.
 *
 * `display: none` rather than `visibility` or a clip: this has to leave the
 * accessibility tree and tab order as well as the page, since above 767 the box
 * has no collapsed state for the control to disclose and an `aria-expanded`
 * button beside permanently-visible content would be stating something false.
 * See the READING ORDER note in inc/oi-flex-request-form-status.php.
 *
 * ---------------------------------------------------------------------------
 * THERE IS NO SPECIFICITY CONTEST HERE, AND AN EARLIER VERSION OF THIS FILE
 * FOUGHT ONE ANYWAY
 * ---------------------------------------------------------------------------
 * Gravity Forms' framework theme claims every `<button>` inside `.gform_wrapper`
 * at specificity **(0,5,1)** and sets its `display`, `padding-inline`,
 * `padding-block`, `min-block-size`, `block-size`, `border-width`, `line-height`,
 * `appearance`, font and colour from `--gf-local-*` values it assigns *in the
 * same rule*. So neither a plain class nor this file's usual answer — set the
 * custom property Gravity Forms reads — works: measured, `--gf-local-display:
 * none` on the class read back `inline-flex`, and `--gf-local-padding-x: 0` left
 * `padding-left` at 28px. The property is only a sidestep when the *assignment*
 * is reachable, and here it is not.
 *
 * The first fix out-specified it at (0,6,0), twice, borrowing Gravity Forms'
 * `.gform-theme` class to get the numbers up. **That was the wrong answer, and
 * this is the note that stops someone reinstating it.** Both of the rules in
 * question wrap their escape hatches in `:where()`, which contributes no
 * specificity:
 *
 *     …> button:not([id*="mceu_"]):not(.mce-open)
 *       :where(:not(.gform-theme-no-framework))
 *       :where(:not(.gform-theme__disable) … :not(.gform-theme__disable-framework *))
 *
 * so the **`gform-theme-no-framework` class on the button** — added in
 * inc/oi-flex-request-form-status.php, where the reasoning lives — makes neither
 * rule match, and everything below needs no specificity at all. The reset
 * stylesheet has no such opt-out and still applies, which is the wanted baseline:
 * borders, padding and appearance come back to browser defaults rather than to a
 * half-applied theme.
 *
 * Why that is better than winning at (0,6,0) rather than merely shorter: a
 * borrowed internal class is a number, and Gravity Forms has raised this
 * selector's specificity between theme generations before. The next bump loses
 * both overrides silently — the toggle reappearing as a full-width themed button
 * above the desktop panel's heading, which is the measured 304x52 / 447.61 state
 * this whole note exists because of. An opt-out cannot fail that way: if the class
 * is ever renamed, the rules that read it stop matching too.
 *
 * ---------------------------------------------------------------------------
 * ONE COMPETITOR SURVIVES THE OPT-OUT, AND IT IS NOT GRAVITY FORMS
 * ---------------------------------------------------------------------------
 * With the framework theme out of the way the button came back **104px tall at
 * 390 instead of 72**, with `padding-left: 24px` — because the Vamtam Elementor
 * kit styles `button` site-wide at `.elementor-kit-18 button`, which is (0,1,1)
 * and beats a bare `.gfrr-status-toggle`. That is D19's "the kit overrides
 * Gravity Forms site-wide" arriving from a third direction, and 104 is exactly
 * 72 plus the kit's `padding: 16px 24px`.
 *
 * The kit ships no opt-out, so the rules below take **this file's own scoping
 * idiom** — `.gform_wrapper:has(.gfrr-step-head)`, the same prefix every other
 * rule here carries — which is (0,3,0) and clears it without borrowing anyone's
 * class for its specificity value. That is the honest place to land: the
 * prefix is already the house answer to "this rule belongs to our form", and it
 * is being used for what it means rather than for what it scores.
 */
.gform_wrapper:has(.gfrr-step-head) .gfrr-status-toggle {
  display: none;
}

/*
 * The five blocks below the head, wrapped so the sheet has one thing to collapse
 * and one id for `aria-controls`.
 *
 * The gap restates the box's own 20 rather than inheriting it — a flex container
 * does not pass `gap` to a child container, so without this the four inner
 * blocks would sit flush and the panel would lose every gap but the one above
 * it. That is the whole risk this wrapper introduced, and it is why the desktop
 * geometry is asserted rather than assumed.
 */
.gfrr-status-body {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* 6796:3695 — the eyebrow and heading, 8px apart (6796:3698's top padding). */
.gfrr-status-head {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* 6796:3697 — teal, Open Sans 14/16.8, weight 600, letter-spacing 0.84. */
.gfrr-status-eyebrow {
  margin: 0;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gfrr-teal);
}

/* 6796:3699 — white, Open Sans 21/25.2, weight 700. */
.gfrr-status-title {
  margin: 0;
  font-size: 1.3125rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--gfrr-on-ink);
}

/* 6796:3700 and 6796:3703 — a 1px hairline in #FFFFFF1F. */
.gfrr-status-rule {
  block-size: 1px;
  background: rgba(255, 255, 255, 0.12);
}

/*
 * 6796:3701 — the selection card: radius 16, #FFFFFF12, 16 padding, 6 between
 * the report name and the meta line.
 *
 * The mock's instance is 74.2 tall around a single 19-tall name, which is a
 * two-line box with its second line deleted — the geometry the meta line was
 * drawn into. `:empty` on the meta keeps the card one line tall until there is
 * something to show, rather than reserving a gap for a blank paragraph.
 */
.gfrr-status-selection {
  display: flex;
  flex-direction: column;
  gap: 6px;
  border-radius: var(--gfrr-radius-sm);
  padding: 16px;
  background: rgba(255, 255, 255, 0.07);
}

/* 6796:3702 — white, Open Sans 14/18.2, weight 700. Wraps: one report is
   titled `Commission Recovery Report | Funding Analysis Report`. */
.gfrr-status-report {
  margin: 0;
  font-size: 0.875rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--gfrr-on-ink);
  overflow-wrap: anywhere;
}

/* The scope level then the reporting period (D7). Muted against the name, the
   same relationship `.gfrr-card-meta` has to `.gfrr-card-title` in the step 3
   review panel — which shows the same two values from the same JS. */
.gfrr-status-meta {
  margin: 0;
  font-size: 0.8125rem;
  font-weight: 400;
  line-height: 1.5;
  color: var(--gfrr-on-ink-muted);
}

.gfrr-status-meta:empty {
  display: none;
}

/*
 * 6796:3704 — label and figure, space-between, baseline-ish centred.
 *
 * `flex-wrap` is the safety valve on the narrow half of BOCO-24089 round 5's
 * two-column window, and it is why `--gfrr-col-aside-min` could be a round 240
 * rather than a number chosen to survive the worst string. This row is the
 * panel's widest child at `min-content` — the label's longest word, this gap,
 * and a figure that cannot break because it is `white-space: nowrap` — and the
 * figure is *data*: today every report starts from $499.00, and a five-figure
 * price is a wider element with no rule here able to see it coming. Wrapped,
 * the row's `min-content` falls to the figure alone and the panel degrades to
 * label-over-figure instead of overflowing its own padding.
 *
 * It costs nothing where the row fits — a flex line only breaks when it has to —
 * but be clear about where that is, because it is most of the new window rather
 * than its narrow edge. The row needs 172.3 + 12 + 92.6 = 276.9 of content, and
 * the panel offers that at a viewport of **1279** and not at 1278. So the
 * design's `space-between` single line holds from 1440 down to 1279, and from
 * 1278 to 768 the panel reads label-above-figure, left aligned. A deliberate
 * degradation in a range Figma does not draw, and the one *visible* change of
 * round 5 beyond the card staying on the right — flagged to the designer rather
 * than shipped quietly.
 *
 * `row-gap` is stated because `gap: 12px` would otherwise put the column gap
 * between the two lines as well, which is wider than a label and its own figure
 * want to be.
 */
.gfrr-status-total {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  row-gap: 4px;
}

/* 6796:3706 — white at 0.8, Open Sans 15/21, weight 400. */
.gfrr-status-total-label {
  font-size: 0.9375rem;
  font-weight: 400;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.8);
}

/* 6796:3708 — teal, Open Sans 25/26.25, weight 600. `tabular-nums` so the
   figure does not shift width as the digits change under it. */
.gfrr-status-figure {
  font-size: 1.5625rem;
  font-weight: 600;
  line-height: 1.05;
  color: var(--gfrr-teal);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/*
 * 6796:3709 — the eligibility note: radius 16, #72D0EB1A on a 1px #72D0EB40
 * hairline, 16 padding, text white at 0.75, Open Sans 13/19.5.
 *
 * One element rather than the mock's three nested frames; the inner two carry
 * only the padding and the opacity, which are both expressible here.
 */
.gfrr-status-note {
  margin: 0;
  border: 1px solid rgba(114, 208, 235, 0.25);
  border-radius: var(--gfrr-radius-sm);
  padding: 16px;
  background: rgba(114, 208, 235, 0.1);
  font-size: 0.8125rem;
  font-weight: 400;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.75);
}

/*
 * The Total field — form 8 field 19, added by BOCO-24035 — is machinery rather
 * than a control the design draws. The mock puts the figure in the status box
 * above and nothing in the form column, so this hides it instead of letting an
 * unstyled Gravity Forms money input render on all six pages.
 *
 * Hiding it this way is safe on the two counts that matter.
 * `gformCalculateTotalPrice()` writes the input's *value*, which a hidden input
 * still holds and still submits; and Gravity Forms' own notion of a hidden field
 * is conditional logic, not CSS, so `is_field_hidden()` stays false and the
 * total is still computed into the order. Sub-task 07 reads the value from here
 * rather than recomputing it.
 */
.gform_wrapper:has(.gfrr-step-head) .gfield.gfrr-total {
  display: none;
}

/*
 * ---------------------------------------------------------------------------
 * THE REPORTING PERIOD KEEPS ITS OWN ROW — BOCO-24721 / D42.
 * ---------------------------------------------------------------------------
 * One declaration, and it is the whole of this ticket's theme change. It reads
 * as though it were about granularity and it is really about the field *above*
 * granularity, so the reason is worth the paragraphs.
 *
 * D42 puts the reporting period first on the Scope card, in a row of its own,
 * with granularity below it paired with whichever scope detail the level
 * demands. Page 1's field order carries that — `2, 3, 4, 6, 24, 5, 21, 22, 23,
 * 19, 7`, written by `apply-24721-period-row.php` in the pipeline repo — and the
 * order alone is *not* enough, for a reason measured rather than assumed.
 *
 * Gravity Forms lays the fields out as a twelve-column grid on
 * `.gform_fields`, and `layoutGridColumnSpan` becomes a width class: 6 is
 * `gfield--width-half`, 12 is `gfield--width-full`. The period select (field 6)
 * is half width, and so is granularity (field 5). Auto-placement therefore puts
 * them **side by side** as soon as they are adjacent — which is what the
 * reorder makes them on the four *selectable* reports, because the statement
 * that sits between them in the field order (field 24) is `visibility: hidden`
 * there, and Gravity Forms renders that as `position: absolute;
 * inset-inline-start: -9999px`. An absolutely positioned box is not a grid item,
 * so it leaves the grid and granularity flows up beside the period.
 *
 * Two things that look like they would fix it, and do not:
 *
 * - **Gravity Forms' own row spacer.** `GFFormDisplay::get_row_spacer()` seeds
 *   its running total with the field's own span and *then* adds every field
 *   sharing its `layoutGroupId` — the field itself included — so a lone
 *   half-width field totals 12, fails the `$span < 12` test, and emits no
 *   spacer. `layoutSpacerGridColumnSpan` on field 6 would be a no-op.
 * - **Making field 6 full width.** That is one integer in the apply script and
 *   the ticket offers it, but the design draws a half-width select; it is the
 *   *row* that is full width, not the control.
 *
 * So the row break is declared here, and it is declared on granularity rather
 * than on the period because CSS grid has no "break after this item": pinning
 * granularity's column-start to 1 is what makes auto-placement move to the next
 * row before placing it. Its scope detail, still auto-placed, then lands beside
 * it at columns 7–12 — B2 of the design review, unchanged.
 *
 * `.gfield--type-product` is Gravity Forms' own class and field 5 is the only
 * product field on this form. It is used in preference to adding a `cssClass`
 * to the field, because that would be a second edit to the form definition to
 * express something no other part of the form needs to know.
 *
 * **The query is 640, which is Gravity Forms' breakpoint and a fifth width in
 * this file.** Every width class lives inside `@media (min-width: 640px)` in
 * Gravity Forms' foundation stylesheet; below it every `.gfield` is
 * `grid-column: 1 / -1` and the fields stack one per row, so the reorder alone
 * already puts the period first at 390 and this rule must not reach there — a
 * pinned column-start against a `1 / -1` span is a fight over nothing at best.
 * It is deliberately not 560, 767 or 1024: those three are this component's own
 * frames, and this one is the grid Gravity Forms owns. Don't fold it into a
 * neighbour.
 *
 * Specificity: `[0, 3, 0]` — `:has()` contributes its argument's specificity,
 * so the prefix counts as two classes — against foundation's
 * `.gform-theme--foundation .gfield--width-half` at `[0, 2, 0]`. It wins
 * outright rather than on order.
 *
 * `verify-24721.mjs` in the pipeline repo is the only gate that can see any of
 * this: it measures the period slot's box at 1440 and 390 and asserts it does
 * not move as the scope level changes. No PHP gate can — the form definition is
 * identical either way.
 */
@media (min-width: 640px) {
  .gform_wrapper:has(.gfrr-step-head) .gfield--type-product {
    grid-column: 1 / span 6;
  }
}

/*
 * ---------------------------------------------------------------------------
 * THE TWO COLUMNS HOLD TO 768. THEY USED TO COLLAPSE AT 1359.
 * ---------------------------------------------------------------------------
 * BOCO-24089 round 5. Until this round a `max-width: 1359px` block dropped the
 * wrapper to one column and `order: 1` sent the status box beneath the form —
 * where a reader who had scrolled the form into view could not see it at all.
 * It is a running summary of the choices being made directly above it, and 1359
 * is a laptop, not an edge case, so it was being missed on the commonest window
 * size there is. The box now stays in column 2 from 1360 down to 768, and the
 * bottom sheet of BOCO-24037 takes over at 767.
 *
 * WHAT REPLACED THE COLLAPSE IS THE TRACK LIST, NOT THIS BLOCK. Both columns
 * are `fr` weights in the base rule above, so they shrink together in the
 * design's own 728:360 and no breakpoint is involved in the flexing at all.
 * This block does two paddings, and it exists only because the *band* steps at
 * the same width.
 *
 * Why 768 is the floor: 767 is Elementor's active mobile breakpoint on this
 * install (`mobile = 767`, `tablet = 1024`, from the breakpoints API), the
 * parent theme's stylesheet already breaks at 768, and the sheet is declared
 * against it. The design states two artboards, 1440 and 390, and nothing about
 * the 768..1359 window either way — so this whole range is improvised, and the
 * improvisation is *hold the design's ratio and let both columns give*, not
 * *invent a second layout*.
 *
 * The stacked panel that used to render here is gone, not hidden. Nothing else
 * in this file reaches for it; if you are looking for it because a comment
 * elsewhere mentions it, that comment is stale and wants fixing.
 *
 * ---------------------------------------------------------------------------
 * WHY THE PADDING STEPS AT 1024 AND NOT AT THE OLD 1359
 * ---------------------------------------------------------------------------
 * The card's width is set by the band, not by the viewport, and the band is not
 * linear in it. Measured `.e-con-inner` widths — that element is the cap on
 * `.gform_wrapper`, which shrink-wraps inside it:
 *
 *     vp 1360 -> 1200    vp 1025 -> 865     vp  900 -> 812
 *     vp 1359 -> 1199    vp 1024 -> 936     vp  820 -> 732
 *     vp 1200 -> 1040    vp 1023 -> 935     vp  768 -> 680
 *
 * The band pays 80 a side above 1024 and 24 below it, and the card container
 * inside it pays a further 20 below it — so the card gets *wider* crossing
 * 1025 -> 1024. That jump is the band's and predates this round. A padding step
 * anywhere else would add a second one, so this one lands on the same boundary
 * and the count stays at one.
 *
 * Leaving the step at 1359, where it was, would have been worse than a second
 * jump: it would make both panels grow as the window narrows. Tracks would go
 * 1088 at 1360 to 1119 at 1359 and the status panel from 360 to 370. Holding
 * 48/40 down to 1024 gives 1088 and 1087 across that boundary — 1px, under the
 * tolerance this surface works to (`docs/adr/0003` in the pipeline repo, not a
 * path here), so the seam is flat.
 */
@media (max-width: 1024px) {
  .gform_wrapper:has(.gfrr-step-head) {
    padding: 32px 24px;
  }
}

/*
 * The two panels' own padding, in the side-by-side window only. `min-width` is
 * load-bearing on both rules and for different reasons: below 768 the form
 * column is the whole card again and wants the 40/48 the design states, down to
 * the 560 block that already handles the phone; and the status box below 768 is
 * the fixed sheet, which sets `padding: 0` and builds the bar's inset from
 * there, so a padding from up here would be one more thing that block has to
 * undo.
 *
 * The numbers are the squeeze, not a design: at 768 the form column is 360 and
 * 48 a side would leave 264 of content, while 24 leaves 312. On the status side
 * 20 is what buys the floor its headroom — 240 less 40 is 200 of content
 * against the 174.4 its own children need. See the token block for that sum.
 */
@media (min-width: 768px) and (max-width: 1024px) {
  .gform_wrapper:has(.gfrr-step-head) form {
    padding: 32px 24px;
  }

  .gfrr-status {
    padding: 20px;
  }
}

/* ---------------------------------------------------- the mobile bottom sheet */

/*
 * BOCO-24037 / D20. Figma `6796:4826` collapsed (393x72) and `6796:5302`
 * expanded (393x330), both radius 20/20/0/0 on `#071C2B` under a
 * `0 -4 32 #00000038` shadow.
 *
 * ONE ELEMENT, NOT TWO. Everything below restyles the *same* `.gfrr-status` the
 * desktop panel is — same render function, same data, same two live values. D20
 * says "same data and same render function, different shell" and this block is
 * the shell. If you find yourself adding markup here, that is the signal to stop.
 *
 * The two states' heights are the mock's own arithmetic and they are why one
 * element can serve both: the grip row is 14 (10 padding + a 4 pill), the bar is
 * 58, and 14 + 58 = the collapsed 72. The body is 258 — its five blocks measure
 * 1 + 69 + 1 + 33 + 66 = 170, four 16px gaps make 234, and 24 of bottom padding
 * makes 258 — so 14 + 58 + 258 = the expanded 330. Nothing here sets a height:
 * the numbers are stated so a measurement that disagrees has something to be
 * checked against.
 *
 * ---------------------------------------------------------------------------
 * `position: fixed` PINS HERE, AND THAT WAS CHECKED BEFORE ANY OF THIS WAS
 * WRITTEN
 * ---------------------------------------------------------------------------
 * A fixed element's containing block is the viewport only while no ancestor
 * establishes one — `transform`, `filter`, `backdrop-filter`, `perspective`, a
 * `will-change` naming any of them, `contain` or `container-type`. The chain
 * above this box runs through four Elementor containers, a Vamtam wrapper and
 * `custom-animations.js`, any of which could set one. If one did, the "fixed"
 * sheet would be positioned against *that* box and would scroll away with the
 * page.
 *
 * **The failure is invisible at the top of the page**, which is where a
 * screenshot gets taken. So it was measured across a scroll instead:
 * `containing-block.mjs` in the verification directory walks the chain, finds no
 * ancestor establishing a containing block, and proves the consequence with a
 * fixed probe that holds 0px drift from load, to the form centred, to the page
 * bottom. `verify-24037.mjs` repeats the second leg against this element.
 *
 * That result is what let the sheet stay a child of `.gform_wrapper`. Had it
 * failed, the sheet would have had to be reparented to `<body>` by JS — which
 * would have cost the "direct child of the wrapper" invariant the whole D17
 * amendment rests on, and that `verify-24036.php` asserts. Worth re-running if
 * the theme or the kit ever changes above this band.
 *
 * ---------------------------------------------------------------------------
 * `order: 1` IS NOT DELETED, AND MUST NOT BE
 * ---------------------------------------------------------------------------
 * A fixed element is out of flow and is not a grid item, so `order` does nothing
 * for the sheet. It is still load-bearing everywhere above this block — it is
 * what places the box in the form's own row rather than in row 1 beside
 * `.gform_anchor`, which is a measured 414px of displacement — so it stays in
 * the base rule and this block does not touch it. Scoping a change to it into a
 * media query, rather than removing the line, is the standing instruction here.
 *
 * (Before BOCO-24089 round 5 this note said "stacking below the form from 768 to
 * 1359" as well. There is no stacked layout any more; the two columns hold down
 * to 768.)
 *
 * ---------------------------------------------------------------------------
 * WHAT IS NOT BUILT, ON PURPOSE
 * ---------------------------------------------------------------------------
 * The mock's texture (an IMAGE fill, imageRef 5bb360ab on `6796:3694`) is flat
 * ink here exactly as it is on desktop — an asset that would have to come through
 * `figma-assets/`, which nothing on the form side does. Recorded as an intent
 * finding, not approximated.
 *
 * The expanded mock's eligibility note draws its second clause in bold. It ships
 * uniform, as it already does on desktop: the string is one translated sentence
 * and splitting it into two spans would hand WPML a fragment to translate for the
 * sake of a weight change. Also an intent finding.
 */
@media (max-width: 767px) {
  /*
   * THE SECOND TRACK HAS TO GO WITH THE BOX, AND THIS IS NOT TIDYING.
   *
   * `position: fixed` takes the box out of flow, so it stops being a grid item
   * — but it does not take its *track* with it. The column list is declared on
   * the wrapper, and an absolutely positioned child contributes nothing to
   * sizing it, so column 2 would still be laid out at its own
   * `--gfrr-col-aside-min` and reserve 240px of the card for an element that is
   * no longer in it. The form column would be 240 + 32 narrower than the card
   * for no visible reason.
   *
   * Before BOCO-24089 round 5 nothing here said this, because the `max-width:
   * 1359px` block had already collapsed the grid to one column long before this
   * width was reached. That block is gone; this line is what replaces the half
   * of it that was still doing work at 767.
   */
  .gform_wrapper:has(.gfrr-step-head) {
    grid-template-columns: minmax(0, 1fr);
  }

  .gfrr-status {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 90;
    gap: 0;
    padding: 0;
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -4px 32px rgba(0, 0, 0, 0.22);

    /*
     * The home indicator on a notched phone. It adds to the collapsed 72 rather
     * than eating into it, which is the right way round — the bar's content keeps
     * its height and the sheet grows to clear the inset. On any desktop browser
     * the inset is 0, which is why a gate can still assert a flat 72.
     */
    padding-block-end: env(safe-area-inset-bottom, 0px);
  }

  /*
   * ---------------------------------------------------------------------------
   * THE SEAM UNDER THE SHEET, WHICH IS A RASTERISATION BUG AND NOT A LAYOUT ONE
   * ---------------------------------------------------------------------------
   * Reported as "about 1 pixel of empty space below the sheet", and the reason it
   * survived this long is that **the layout is already perfect**: `inset: auto 0 0
   * 0` puts the border box's bottom edge exactly on the viewport's, and
   * `verify-24037.mjs` measured that gap as 0.000 at every device scale factor
   * tried — 1, 2, 3, 2.75. There is nothing to fix in CSS pixels.
   *
   * The seam appears when **the viewport's height in *device* pixels is not a
   * whole number**, so the bottom edge lands mid-pixel and that last physical row
   * is composited half from the sheet and half from whatever is behind it.
   * Reproduced by sampling the bottom row rather than the geometry:
   *
   *     390x845 @1.5    845 x 1.5  = 1267.5   bottom row rgb(119,130,137)
   *     391x843 @1.25   843 x 1.25 = 1053.75  bottom row rgb(64,80,91)
   *
   * — against the sheet's own `#071C2B`. Both report `gap = 0.000`. Any check
   * written on the rect is blind to this by construction, which is why the gate
   * now reads pixels for it.
   *
   * The fix is to keep the box exactly where it is and paint two pixels further
   * down, off the bottom of the screen, so the blend has the sheet's own colour on
   * both sides of the boundary instead of the page's. A pseudo-element does that
   * without touching a single measurement: it is out of flow, so the sheet is
   * still 72 tall and still bottom-flush, and every assertion built on those
   * numbers is untouched.
   *
   * NOT `bottom: -1px` WITH A PADDING TO COMPENSATE, which is the other way to do
   * this. That moves real geometry — the box becomes 73 tall — to fix something
   * that is not a geometry problem, and it would trade a one-device-pixel seam for
   * a wrong number in every height assertion in the gate.
   *
   * `background: inherit` rather than naming the colour again: whatever the sheet
   * is painted with is what has to continue past the edge. If the mock's texture
   * is ever built, this keeps up with it; a second `var(--gfrr-ink)` would not.
   *
   * It cannot be clicked (it is off-screen, and `pointer-events: none` besides),
   * it inherits `visibility` so it leaves with the sheet, and it moves with the
   * slide because it is a child. It adds no scrollable overflow: a fixed element's
   * descendants do not contribute to the document's scroll height, which the gate
   * asserts rather than assumes.
   */
  .gfrr-status::after {
    content: '';
    position: absolute;
    inset: 100% 0 auto 0;
    block-size: 2px;
    background: inherit;
    pointer-events: none;
  }

  /*
   * ---------------------------------------------------------------------------
   * AND IT SHOWS ONLY WHILE THE FORM BAND IS ON SCREEN (D20b)
   * ---------------------------------------------------------------------------
   * A report detail page is ~10,500px tall at 390. Pinned for the whole of that
   * scroll, the sheet summarises a form the customer is eight bands away from
   * and takes the bottom 72px of every one of them. `watchViewport()` in the JS
   * puts this class on the box whenever `#request-form` — the band container,
   * Figma `6796:3622` — is out of the viewport.
   *
   * `visibility` RATHER THAN `display`, for three reasons, and the third is the
   * one that would be discovered the hard way:
   *
   *   - it takes the sheet out of the accessibility tree *and* out of the tab
   *     order, so the toggle cannot be reached by keyboard while it is not
   *     there, and the two `aria-live` figures stop announcing prices for a form
   *     that is off screen;
   *   - it is one property to reverse, where `display: none` on a flex child
   *     invites a later reader to re-derive the layout;
   *   - **it leaves the box's height measurable.** `--gfrr-status-height` is
   *     published from a `ResizeObserver` on this element; `display: none` would
   *     report 0 and publish `0px`, so the widget lift would come back from a
   *     scroll with the wrong number until the next resize. `visibility` changes
   *     no layout, so the observer sees nothing at all.
   *
   * THE CLASS MEANS HIDDEN, NOT SHOWN, AND THAT DIRECTION IS THE WHOLE SAFETY
   * ARGUMENT. Every way this can fail — no `IntersectionObserver`, the band
   * renamed at the other end, the script never running — leaves the class unset
   * and the sheet visible, which is what shipped before D20b. An `--onscreen`
   * class would have been the same amount of code and would have hidden the
   * sheet permanently on any of those.
   *
   * ---------------------------------------------------------------------------
   * AND IT SLIDES (D20c) — THIS BLOCK USED TO ARGUE THE OPPOSITE
   * ---------------------------------------------------------------------------
   * It shipped un-animated, on the reasoning that the sheet's own open and close
   * are not animated either, so a sheet that slid on scroll but jumped on tap
   * would be inconsistent with itself. That was asked for and overruled: the
   * appear/disappear is the motion a reader actually sees, several times per
   * page, and a bottom sheet snapping in and out of existence reads as a glitch
   * rather than as a decision. Open and close stay instant, which is the status
   * quo and not something this changes.
   *
   * `translate: 0 100%` parks it exactly its own height below the bottom edge —
   * a percentage on `translate` resolves against the element's own border box, so
   * this stays right at 72 and at 347 with nothing to keep in sync. Its shadow
   * spills above the edge during the travel, which is what a sheet sliding out
   * should look like, and vanishes with it: `visibility` flips at the *end* of
   * the transition, not the start.
   *
   * THAT IS WHY `visibility` IS IN THE TRANSITION LIST. It is a discrete
   * property, and the one the spec special-cases the way this needs: going to
   * `hidden` it stays `visible` for the whole duration, so the slide is seen;
   * coming back it flips to `visible` immediately, so the slide is seen then too.
   * Drop it from the list and the sheet vanishes instantly and slides invisibly.
   *
   * ⚠️ It also means `getComputedStyle(box).visibility` reads `visible` for the
   * whole 280ms of a hide. Anything measuring this — `verify-24037.mjs` does,
   * in a dozen places — has to wait for the slide to finish rather than reading
   * after a fixed delay.
   *
   * ⚠️ AND A NON-`none` `translate` MAKES THIS BOX A CONTAINING BLOCK for any
   * `position: fixed` descendant, which is the same mechanism `containing-block
   * .mjs` exists to catch one level up. Nothing inside is fixed — measured, all
   * 21 descendants compute `static` — so it costs nothing today. It is written
   * down because a fixed child added later would break **only while the sheet is
   * hidden**, which is the least reproducible state this component has.
   */
  .gfrr-status {
    transition:
      translate 280ms cubic-bezier(0.32, 0.72, 0, 1),
      visibility 280ms;
  }

  .gfrr-status--offscreen {
    visibility: hidden;
    translate: 0 100%;
  }

  /*
   * The eyebrow and the "Review your selection" heading. The collapsed bar's own
   * label is the sheet's heading at this width — `6796:5302` draws no eyebrow —
   * so showing both would state the same thing twice, once in a bar and once
   * under it.
   */
  .gfrr-status-head {
    display: none;
  }

  /*
   * The bar. The house `:has(.gfrr-step-head)` prefix rather than a bare class,
   * and the note on `.gfrr-status-toggle` above says why: Gravity Forms is opted
   * out, but the Vamtam Elementor kit still styles `button` at (0,1,1) and would
   * otherwise add 32px of its own padding to the collapsed bar.
   *
   * The declarations are the ones a `<button>` needs whatever else is on the page:
   * a button is not a flex column by default, does not fill its parent, and brings
   * its own font, colour and centring from the UA. They are stated rather than
   * inherited so the bar does not depend on which stylesheets happen to be loaded
   * around it.
   */
  .gform_wrapper:has(.gfrr-step-head) .gfrr-status-toggle {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    box-sizing: border-box;
    inline-size: 100%;
    margin: 0;
    border: 0;
    border-radius: 20px 20px 0 0;
    padding: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: start;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
  }

  /* 6796:5303 / 6796:5304 — a 36x4 pill in #FFFFFF38, in a 14-tall row whose
     height is 10px of padding above it. */
  .gfrr-status-grip {
    display: block;
    margin: 10px auto 0;
    inline-size: 36px;
    block-size: 4px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.22);
  }

  /*
   * 6796:5305 — 58 tall, space-between, centred, 20 a side.
   *
   * `align-self: stretch` duplicates the `align-items: stretch` the button rule
   * above already sets, and the duplication is deliberate. Stretching is the one
   * property whose loss was actually *measured* here — the framework theme's
   * `align-items: center` shrink-wrapped this bar to 311.88 of the 350 it should
   * fill, which moves the figure away from the sheet's right edge and reads as a
   * centring mistake rather than as a cascade problem. Said from the child it
   * needs no specificity at all, because nothing else styles this span, so it
   * holds even if the button rule above ever loses its contest again.
   */
  .gfrr-status-bar {
    align-self: stretch;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    block-size: 58px;
    padding-inline: 20px;
  }

  /* 6796:5308 — teal, Open Sans 12/18, weight 600, uppercased here rather than
     in the string. The letter-spacing is the eyebrow's own 0.06em token reused:
     that computes to 0.72px against the mock's stated 0.7, which is a 0.02px
     difference and not worth a second number in this file. */
  .gfrr-status-bar-label {
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.5;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--gfrr-teal);
  }

  .gfrr-status-bar-end {
    display: flex;
    align-items: center;
    gap: 10px;
  }

  /*
   * 6796:4835 / 6796:5311 — teal, Open Sans 17/25.5.
   *
   * THE MOCK GIVES THIS ONE ELEMENT TWO WEIGHTS: 700 collapsed and 800 expanded.
   * One element cannot have both, and no state change in the design justifies a
   * weight shift, so it is 700 — the collapsed state, which is the sheet's
   * resting state and the one the frame named `MobileBottomSheet` at 393x72 is
   * drawn in. Recorded rather than silently picked; if the designer meant 800
   * throughout, the expanded total row below already carries it and this is a
   * one-line change.
   *
   * `tabular-nums` for the same reason the desktop figure has it: the digits
   * change under the reader and the element should not resize while they do.
   */
  .gfrr-status-bar-figure {
    font-size: 1.0625rem;
    font-weight: 700;
    line-height: 1.5;
    color: var(--gfrr-teal);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  /* 6796:4838 — a 10x5 vector in a 20x20 box, 1.67 stroke, taking the teal from
     `currentColor` so it cannot drift from the two elements beside it. */
  .gfrr-status-chevron {
    flex: none;
    inline-size: 20px;
    block-size: 20px;
    color: var(--gfrr-teal);
    transition: transform 160ms ease;
  }

  /* Down collapsed, up expanded — the way round the mock draws it, which is the
     opposite of the convention for a sheet that grows upwards. `6796:4836` is
     the transformed instance and `6796:5312` the base. */
  .gfrr-status--open .gfrr-status-chevron {
    transform: rotate(180deg);
  }

  /*
   * 6796:5314 — the body: no gap between blocks in the mock, each carrying its
   * own 16 of top margin instead. Expressed as a 16 gap, which lands on the same
   * 234 because the sequence has four seams, and leaves one number to change
   * rather than five.
   *
   * The height guard is insurance rather than a measured value. 330 on the
   * shortest phone the design targets leaves room, but the report name wraps —
   * one of the six is `Commission Recovery Report | Funding Analysis Report` —
   * and a sheet taller than its viewport would put the eligibility note out of
   * reach with no way to scroll to it. 60vh keeps the form visible behind an open
   * sheet as well.
   */
  .gfrr-status-body {
    gap: 16px;
    padding: 0 20px 24px;
    max-block-size: 60vh;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  /*
   * Collapsed is the default state, in the markup and here: PHP renders
   * `aria-expanded="false"` with no open class, so the first paint is the
   * collapsed bar and nothing has to be corrected afterwards. `display: none`
   * takes the body out of the accessibility tree with it, which is why the bar
   * carries its own `aria-live` figure — see the status PHP's READING ORDER note.
   */
  .gfrr-status:not(.gfrr-status--open) .gfrr-status-body {
    display: none;
  }

  /* 6796:5317 — the selection card tightens: radius 12 against the desktop 16,
     14 of padding against 16, 4 between the two lines against 6. */
  .gfrr-status-selection {
    gap: 4px;
    border-radius: 12px;
    padding: 14px;
  }

  /* 6796:5321 — Open Sans 12/18 at 0.6, against the desktop 13/1.5 at 0.68. */
  .gfrr-status-meta {
    font-size: 0.75rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, 0.6);
  }

  /* 6796:5326 — 14/21, against the desktop 15/1.4. The 0.8 opacity is unchanged. */
  .gfrr-status-total-label {
    font-size: 0.875rem;
    line-height: 1.5;
  }

  /* 6796:5328 — 22/33 weight 800, against the desktop 25/1.05 weight 600. Open
     Sans 800 is loaded on this site (checked against `document.fonts`), so this
     is the real face and not a synthesised bold. */
  .gfrr-status-figure {
    font-size: 1.375rem;
    font-weight: 800;
    line-height: 1.5;
  }

  /* 6796:5330 / 6796:5332 — radius 12, 14 of padding, 12/18. */
  .gfrr-status-note {
    border-radius: 12px;
    padding: 14px;
    font-size: 0.75rem;
    line-height: 1.5;
  }

  /*
   * -------------------------------------------------------------------------
   * THE BOTTOM OF THIS SITE IS ALREADY OCCUPIED, AND THE MOCK DOES NOT KNOW IT
   * -------------------------------------------------------------------------
   * Measured at 390: **three other fixed elements** paint into the bottom 72px of
   * every page on this site, and two of them stack above this sheet.
   *
   *   .helpButton            168x46 @210,786   the Salesforce chat launcher; its
   *                                            container `.embeddedServiceHelpButton`
   *                                            is z-index 999
   *   #ot-sdk-btn-floating    50x50 @4,784     the OneTrust cookie-settings button,
   *                                            z-index 2147483646
   *   .grecaptcha-badge      256x60 @320,770   the reCAPTCHA attribution badge
   *
   * Without this rule the collapsed bar is unusable rather than merely untidy: the
   * chat launcher covers **the figure and the chevron outright**, and the OneTrust
   * button clips the label to "W YOUR SELECTIONS". The one thing the collapsed bar
   * exists to show — the price — sits behind a third-party widget in the sheet's
   * default state. The mock could not have anticipated it, because `6796:4826`
   * draws a 393-wide sheet on an empty canvas.
   *
   * WHY THEY MOVE AND THE SHEET DOES NOT. Raising the sheet above them was the
   * other option and it is wrong twice over: above OneTrust it would cover the
   * cookie-settings control, which has to stay reachable for consent, and above the
   * chat launcher it would bury a support affordance the customer may want *while*
   * filling this form in. Lifting a floating launcher to clear a bottom bar is also
   * the conventional answer rather than an invention.
   *
   * `translate` rather than `bottom`, for two reasons: it composes with each
   * widget's own offset instead of replacing a value this file would have to know
   * (12px, 10px and 14px, all different), and the reCAPTCHA badge sets its `bottom`
   * as an **inline style**, which no stylesheet rule can outrank. It also composes
   * with, rather than clobbers, any `transform` these widgets animate.
   *
   * THE COLLAPSED HEIGHT IS A LITERAL; THE EXPANDED ONE CANNOT BE. 72 is the mock's
   * own arithmetic and is written here. The expanded height is content-driven —
   * 325.38 with no scope level selected, 347.38 on Commission Recovery once one is
   * (its title wraps to two lines), 329.19 on Market Opportunity — and `6796:5302`
   * is a HUG-height frame, so its 330 is what the designer's content measured, not
   * a spec. A second literal would therefore be wrong on five of six reports. The
   * open-state rule below reads a measured value instead; the script publishes it.
   *
   * ---------------------------------------------------------------------------
   * THE OPEN STATE WAS LEFT OVERLAPPING ONCE, AND THAT WAS REVERSED ON PURPOSE
   * ---------------------------------------------------------------------------
   * The first version of this block lifted by 72 in both states and argued that the
   * expanded overlap could stand: it happens only while the customer holds the sheet
   * open, one tap closes it, and the figure stays legible because the collapsed bar
   * shows it too. Design was asked on BOCO-24037 and **chose to fix it**, and the
   * argument was wrong for a reason that is not aesthetic: with the sheet open the
   * chat launcher lands across "Estimated one-time total" and the figure, and both
   * launchers sit *above* the sheet in the stacking order, so the alternative —
   * letting the sheet paint over them — would make the **OneTrust consent control
   * unreachable** for as long as the sheet is open. Unreachable-sometimes is the
   * same objection that ruled out raising the sheet in the paragraph above; it does
   * not become acceptable by being intermittent. So the widgets travel with the
   * sheet, which is also what a bottom sheet conventionally does.
   *
   * TWO WIDGETS IN THE OPEN STATE, NOT THREE, AND THE THIRD IS A MEASUREMENT RATHER
   * THAN AN OVERSIGHT. Measured at 390 with the sheet open, `.grecaptcha-badge` is
   * `visibility: hidden` and inline-positioned at `right: -186px`; it is `z-index:
   * auto` against this sheet's 90, and `document.elementFromPoint()` at its centre
   * returns `.gfrr-status-body`. It is already behind the sheet and paints nothing,
   * so there is no overlap to fix — and lifting it would move an element that is
   * invisible at the bottom of the screen to being invisible in the middle of it,
   * which is a change with a downside and no upside. It keeps the collapsed lift,
   * which is shipped behaviour and equally a no-op, rather than being churned.
   *
   * `verify-24037.mjs` asserts each widget clears the *measured* collapsed bar and
   * that the two named ones clear the *measured* expanded sheet, so a change to
   * either height that forgets this rule fails a gate rather than silently
   * re-covering the figure. It also asserts the lift still tracks after the sheet
   * grows mid-open, which is the assertion that would catch the published value
   * going stale.
   *
   * Scoped on **the sheet being there** — `body:has(.gfrr-status)` — not on
   * `.gfrr-step-head` like every other rule in this file. That is the one place the
   * house idiom is wrong: the form's step heads render whether or not the box does,
   * and `oiflex_status_box()` returns `''` for a post with no title, while
   * `oiflex_render_status_box()` bails if either sibling helper is missing. In any
   * of those states, and on the Save & Continue path the status PHP documents,
   * keying on the form would lift three third-party widgets 72px to clear a sheet
   * that is not there — an unexplained gap in someone else's UI. Keyed on the
   * sheet, the lift exists exactly when the thing it avoids does.
   *
   * **D20b carries that argument one step further, and D20c moved where it lives.**
   * The sheet is hidden while the form band is off screen, which is most of a
   * ~10,500px page, and a hidden sheet is exactly as absent to a reader as one that
   * never rendered — so the lift has to come off there too, or these rules would
   * hold three third-party controls 72px off the bottom of eight bands that have no
   * sheet in them: the failure this scoping was chosen to avoid, reintroduced by a
   * feature that had nothing to do with it.
   *
   * D20b did that with `:not(.gfrr-status--offscreen)` on both selectors. It is not
   * done that way any more — the release has to *travel* with the sliding sheet
   * rather than happen, so it is a transitioned `transform` further down, and these
   * selectors went back to what they were. The release is not weaker for moving;
   * `verify-24037.mjs` asserts the net resting position rather than the property.
   *
   * These are third-party elements outside `.gform_wrapper`, so the wrapper-scoped
   * idiom cannot reach them either way; this is the same question asked one
   * ancestor up.
   *
   * ⚠️ These are the only rules in this component that move markup belonging to
   * another integration. That was flagged on BOCO-24037 for design to confirm
   * rather than presented as a neutral fix, and the answer came back: keep the
   * lift, and extend it to the open state. Anything further done to these three
   * selectors deserves the same treatment.
   */
  body:has(.gfrr-status) .helpButton,
  body:has(.gfrr-status) #ot-sdk-btn-floating,
  body:has(.gfrr-status) .grecaptcha-badge {
    translate: 0 calc(-72px - env(safe-area-inset-bottom, 0px));
    transition: transform 280ms cubic-bezier(0.32, 0.72, 0, 1);
  }

  /*
   * …and by the sheet's measured height while it is open.
   *
   * ⚠️ THIS RULE MUST STAY BELOW THE ONE ABOVE IT. Both are (0,3,1) — `:has()`
   * and `:not()` each contribute their most specific argument, so both selectors
   * score two classes inside the `:has()` plus one outside — so **source order is
   * the only thing that decides**, and moving this above the collapsed rule would
   * silently revert the open state to 72px with no error and no visible diff in
   * the file. The expanded-state check in `verify-24037.mjs` is what catches it.
   * They were (0,2,1) each before D20b added the `:not()`; what matters is that
   * they moved *together*, and any future guard added to one belongs on both.
   *
   * That is not tidiness, and it was confirmed by breaking it. Take the `:not()`
   * off **this** rule alone and it drops to (0,2,1) while the collapsed rule stays
   * at (0,3,1) — so the collapsed rule then wins on specificity whatever the
   * source order, and the open sheet is lifted by 72 instead of by its own height.
   * `verify-24037.mjs` reports five failures for that one deletion, three of them
   * about the expanded state rather than about the guard. **A guard added to one
   * of a tied pair silently unties them**, and the symptom is nowhere near the
   * edit.
   *
   * `--gfrr-status-height` is written to `document.documentElement` by
   * `publishHeight()` in oi-flex-request-form.js, from a `ResizeObserver` on the
   * box, so it is current before the frame the sheet grows in is painted. That
   * function's docblock carries the measurements and the reasoning; the two things
   * this rule depends on are that the value is the **border box** — so it already
   * contains `env(safe-area-inset-bottom)`, which is why no `env()` is added here
   * and why adding one would double the inset on a notched phone — and that it is
   * published in both states, which is safe only because this selector is gated on
   * `.gfrr-status--open`. A stale value cannot be acted on while the sheet is shut.
   *
   * The 72px fallback is not decoration: if the script never ran, or the browser
   * has no `ResizeObserver`, this degrades to exactly what the collapsed rule does,
   * which is the behaviour that shipped. It never degrades to no lift at all.
   *
   * **This** movement is still not animated, and D20c is the reason it now has to
   * be said rather than assumed. The sheet's open and close are instant — only the
   * chevron rotates — so a widget that slid while the sheet snapped would draw the
   * eye to the travel rather than to the sheet, and for the 280ms of the slide it
   * would sit across the total row, which is the defect this rule exists to fix.
   * The show/hide travel *is* animated, on `transform`, precisely so that this one
   * can stay instant; the rule below carries that and the reasoning.
   *
   * The badge is absent from this list on purpose; see the measurement above.
   */
  body:has(.gfrr-status--open) .helpButton,
  body:has(.gfrr-status--open) #ot-sdk-btn-floating {
    translate: 0 calc(-1 * var(--gfrr-status-height, 72px));
  }

  /*
   * ---------------------------------------------------------------------------
   * AND PUT BACK DOWN WHILE THE SHEET IS AWAY — ON `transform`, NOT `translate`
   * ---------------------------------------------------------------------------
   * D20b released the lift by taking `:not(.gfrr-status--offscreen)` onto both
   * rules above, so the widgets simply stopped being lifted. D20c cannot do that,
   * because the release now has to *travel* with the sliding sheet rather than
   * happen — and the moment `translate` is transitioned to achieve it, **every tap
   * that opens the sheet drags the launchers up over 280ms while the sheet snaps
   * open**. For those 280ms they sit across the total row: a transient, recurring
   * re-enactment of the exact defect BOCO-24037 was reopened to fix.
   *
   * So the two movements are put on two properties, which compose rather than
   * overwrite:
   *
   *   `translate`   how far the sheet lifts them. Changes on open/close, and is
   *                 **not** transitioned — so open/close stays instant, as it was.
   *   `transform`   whether the sheet is there at all. Changes on scroll, and is
   *                 the only thing that animates.
   *
   * `translateY(var(--gfrr-status-height))` is exactly the negation of whichever
   * lift is active: the open rule lifts by that same measured height, and the
   * collapsed rule's `72px + env()` **is** the measured height, since the sheet
   * carries the inset as `padding-block-end` and the box the script measures
   * includes it. Net zero either way, so a hidden sheet leaves all three widgets
   * exactly where the site put them.
   *
   * The `72px` fallback is unreachable rather than approximate: this class is set
   * only by `applyViewport()`, so a page where the script never ran never has it,
   * and the one case where the fallback would be off by the safe-area inset
   * cannot occur.
   *
   * Both lift rules go back to (0,2,1) with the `:not()`s gone, which restores
   * the plain source-order tie they had before D20b — see the note above, which
   * is again the whole story.
   *
   * ⚠️ ONE TRANSIENT IS KNOWN AND ACCEPTED. If the sheet's *height* changes while
   * it is hidden — an AJAX step change re-renders it collapsed — `translate` jumps
   * to the new lift immediately while `transform` animates from the old height to
   * the new one, so the widgets dip below their resting place and return over
   * 280ms.
   *
   * What keeps it out of reach is an **ordering**, not the absence of a step
   * change: reaching Continue puts the band on screen, so the sheet is *shown*
   * before it re-renders, and the height settles while `transform` is 0. Measured
   * across a Continue click started from scroll 0 — the sheet is shown 1.6s before
   * the re-render lands, and the height never moves while the class is on. Stated
   * as the ordering because "nobody can click Continue from there" is the right
   * conclusion from the wrong reason, and the gate's own AC4 does exactly that
   * sequence.
   */
  body:has(.gfrr-status--offscreen) .helpButton,
  body:has(.gfrr-status--offscreen) #ot-sdk-btn-floating,
  body:has(.gfrr-status--offscreen) .grecaptcha-badge {
    transform: translateY(var(--gfrr-status-height, 72px));
  }
}

/*
 * The sheet's slide and the widget travel it drags along with it are both listed
 * here, and they have to be listed **together**: suppress one and the sheet would
 * appear instantly while three third-party controls glided into place after it,
 * or the reverse. The motion is one movement expressed on two elements.
 *
 * `visibility` goes with them rather than being kept — with no slide to be seen,
 * delaying the flip to the end of a 280ms window would just be 280ms of a sheet
 * that is on its way out and still hit-testable.
 *
 * ⚠️ THIS SITE ALREADY HAS A GLOBAL REDUCED-MOTION RESET, AND THAT IS AN ARGUMENT
 * FOR THESE RULES RATHER THAN AGAINST THEM. Measured under `reduce`, *every*
 * element on the page reports `transition-duration: 0s` — including
 * `.grecaptcha-badge`, whose `right 0.3s` is declared by reCAPTCHA in its own
 * inline style and which nothing here touches. Something upstream is zeroing
 * durations with `!important`.
 *
 * So these declarations are not what makes the page still today. They are what
 * makes *this component* still on its own terms, and a component that leans on a
 * theme-wide reset it does not own is one theme update away from animating for a
 * reader who asked it not to.
 *
 * It also means a gate cannot check this by reading `transition-duration`, which
 * says `0s` either way — `verify-24037.mjs` had exactly that check and it passed
 * with half of this rule deleted. It asserts `transition-property: none` instead,
 * which only the `none` shorthand produces; the global reset leaves the property
 * alone. Keep the shorthand for that reason: `transition-duration: 0s` here would
 * be correct and untestable.
 */
@media (prefers-reduced-motion: reduce) {
  .gfrr-status-chevron {
    transition: none;
  }

  .gfrr-status,
  body:has(.gfrr-status) .helpButton,
  body:has(.gfrr-status) #ot-sdk-btn-floating,
  body:has(.gfrr-status) .grecaptcha-badge {
    transition: none;
  }
}

@media (max-width: 560px) {
  .gform_wrapper:has(.gfrr-step-head) {
    padding: 24px 0;
  }

  .gform_wrapper:has(.gfrr-step-head) form {
    padding: 24px 20px;
  }
}

/* ------------------------------------------------------------- progress bar */

.gform_wrapper:has(.gfrr-step-head) .gf_progressbar_wrapper {
  margin: 0 0 26px;
  padding: 0;
}

/* Figma 6796:3641: Open Sans 14/16.8, letter-spacing 0.84 (0.06em), weight 600. */
.gform_wrapper:has(.gfrr-step-head) .gf_progressbar_title {
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--gfrr-ink);
  margin: 0 0 10px;
}

/*
 * The mock shows the step counter alone, so the page name is hidden. This
 * replaces a JS function that rewrote the title's textContent, which flattened
 * the three spans Gravity Forms emits — `.gf_step_current_page`,
 * `.gf_step_page_count`, `.gf_step_page_name` (form_display.php:4785). Gravity
 * Forms' own pagination bundle reads those spans to announce "Step N of M" to
 * screen readers and returns early when they are missing, so rewriting the text
 * cost the announcement. Hiding the one span keeps the other two intact.
 */
.gform_wrapper:has(.gfrr-step-head) .gf_progressbar_title .gf_step_page_name {
  display: none;
}

/* The track's height, radius and both colours are CSS API — see
   `gf-field-pg-prog-bar-*` in oiflex_form_css_properties(). Only the padding
   and the shadow, which have no token, are left here. */
.gform_wrapper:has(.gfrr-step-head) .gf_progressbar {
  overflow: hidden;
  padding: 0;
  box-shadow: none;
}

.gform_wrapper:has(.gfrr-step-head) .gf_progressbar_percentage {
  transition: width 0.25s ease;
}

/* The mock shows the step counter alone, with no numeric percentage. */
.gform_wrapper:has(.gfrr-step-head) .gf_progressbar_percentage span {
  display: none;
}

/* ------------------------------------------------- step headings and copy */

.gfrr-step-head {
  margin: 0;
}

/* Figma 6796:3645: Open Sans 39/40.95, weight 700. */
.gform_wrapper:has(.gfrr-step-head) .gfrr-step-title {
  margin: 0 0 10px;
  font-size: 2.4375rem;
  line-height: 1.05;
  font-weight: 700;
  letter-spacing: normal;
  color: var(--gfrr-ink);
}

/* Figma 6796:3646: Open Sans 16/22.4, weight 400. The weight is stated because
   the Vamtam theme bolds body copy inside this band; without it the intro
   renders 700. */
.gform_wrapper:has(.gfrr-step-head) .gfrr-step-intro {
  margin: 0;
  color: var(--gfrr-muted);
  font-size: 1rem;
  line-height: 1.4;
  font-weight: 400;
}

/*
 * The step-1 divider. Figma `6796:3647` is a `DSDivider` frame at
 * `pad=32/0/32/0` around a one-pixel rule, so the 32 is space the divider owns
 * on both sides rather than a margin the rule collapses into a neighbour.
 *
 * ---------------------------------------------------------------------------
 * THE HOUSE PREFIX IS LOAD-BEARING HERE, NOT DECORATION — BOCO-24089 item 4
 * ---------------------------------------------------------------------------
 * This rule used to be a bare `.gfrr-rule` and its margin **never applied, at
 * any width**. Elementor ships `.elementor hr { background-color: transparent;
 * margin: 0 }`, which is (0,1,1) against a bare class's (0,1,0), so the
 * declared 32 lost and the rule sat flush against the intro — measured 0, read
 * back off `CSS.getMatchedStylesForNode` rather than guessed. Nothing failed;
 * the number was simply not the one in the file, which is why the gate now
 * asserts the *gap* and not the declaration.
 *
 * So the prefix takes this to (0,3,0) and the rule wins on specificity. Any
 * future edit that shortens the selector back to `.gfrr-rule` silently restores
 * the bug.
 *
 * `margin-bottom` stays 0 deliberately: the rule is its own `.gfield`, so the
 * form grid's row gap already supplies the space below it. That gap measures 40
 * against Figma's 32 here and 24 at 390 — a real delta, but one that belongs to
 * the grid rather than to this element, and moving it would move every field
 * seam on the form. Recorded in the *pipeline* repo's
 * `feedback/oi-flex-form-verification/README.md` — not a path in this repository;
 * that is where the gate for this ticket lives, because it spans both — rather
 * than patched from this side.
 */
.gform_wrapper:has(.gfrr-step-head) .gfrr-rule {
  border: 0;
  border-top: 1px solid var(--gfrr-rule);
  margin: 32px 0 0;
}

/*
 * ---------------------------------------------------------------------------
 * THE PHONE TYPE RAMP — BOCO-24089 item 3, and 560 is the file's own phone edge
 * ---------------------------------------------------------------------------
 * Figma draws this block twice and the two disagree on every size, so all of
 * these are stated rather than inherited. Left is the 390 frame, right the 1440
 * one the base rules above carry:
 *
 *   progress title  `6796:4207` 13/15.6 ls 0.7   vs `6796:3641` 14/16.8 ls 0.84
 *   step title      `6796:4214` 28/30.8 w700     vs `6796:3645` 39/40.95 w700
 *   step intro      `6796:4215` 15/22.5 w400     vs `6796:3646` 16/22.4  w400
 *   divider pad     `6796:4216` 24/24            vs `6796:3647` 32/32
 *
 * **The query is 560, not 767, and that is this file's convention rather than
 * Elementor's.** The two blocks that set this form's phone geometry — the
 * wrapper's `24px 0` and the panel's `24px 20px`, against `6796:4199` pad=28/20
 * and `6796:4203` pad=20 — are both at 560, so the type ramp joins them there.
 * The band *around* the form switches at Elementor's 767 instead, because that
 * is where its `_mobile` controls switch and a stylesheet cannot move it. Figma
 * draws no frame between 391 and 1024, so 561-767 is extrapolation either way:
 * what renders there is the phone band with desktop form type, which is the
 * same seam ledger item 245 already records for the padding.
 *
 * ---------------------------------------------------------------------------
 * WHAT THIS BLOCK REPLACED, AND WHY IT WAS INERT
 * ---------------------------------------------------------------------------
 * A bare `.gfrr-step-title { font-size: 1.625rem }` stood here and was wrong
 * three ways at once: (0,1,0) against the base rule's (0,3,0), so a media query
 * could not win it — the heading rendered its full desktop 39px at 390 — and
 * 1.625rem is 26px, which is neither the 28 Figma draws nor anything else in the
 * file. A media query does not add specificity; only the selector does.
 *
 * `letter-spacing` is deliberately NOT restated. The base rule's 0.06em gives
 * 0.78px at 13px against Figma's 0.7 — 0.08px. **Differences below one pixel are
 * not findings on this surface**; the rule is stated as an ADR in the pipeline
 * repo (`docs/adr/0003-one-pixel-geometry-tolerance.md`, not a path here) and is
 * restated rather than only cited, because a reader of this file cannot open it.
 * Pinning 0.7 would be churn dressed as precision.
 */
@media (max-width: 560px) {
  /* 13 x 1.2 is exactly the 15.6 the frame draws, so the leading needs nothing. */
  .gform_wrapper:has(.gfrr-step-head) .gf_progressbar_title {
    font-size: 0.8125rem;
  }

  /* 30.8/28 — the base 1.05 would give 29.4, which is 1.4 out and above the floor. */
  .gform_wrapper:has(.gfrr-step-head) .gfrr-step-title {
    font-size: 1.75rem;
    line-height: 1.1;
  }

  /* 22.5/15 — the base 1.4 would give 21, which is 1.5 out. */
  .gform_wrapper:has(.gfrr-step-head) .gfrr-step-intro {
    font-size: 0.9375rem;
    line-height: 1.5;
  }

  .gform_wrapper:has(.gfrr-step-head) .gfrr-rule {
    margin-top: 24px;
  }
}

/* ------------------------------------------------------- report context card */

/*
 * Step 1 presents the report as a fixed dark panel, because the report is
 * determined by the detail page the form sits on rather than chosen. The panel
 * is server-rendered from the post, one card, always present — see
 * inc/oi-flex-request-form-report.php and BOCO-24034 (D10/D11).
 *
 * Three rules used to live here and are gone with the six-card version, so that
 * nobody re-derives them from the class names: a `display: none` default with an
 * `.is-active` opt-in, a `:has()` rule collapsing the field's grid row while no
 * card matched, and `.gfrr-is-locked`, which hid the Report select once a value
 * was picked. All three existed to manage a *choice*. There is no choice now:
 * one card renders unconditionally, and the field it renders in is never empty.
 *
 * `gfrr-context-card`, not `gfrr-report-card`. A report card is the landing
 * page's tile selling one report among six; this confirms the one already
 * chosen. CONTEXT.md's glossary settles the name, and the old class was the
 * collision it was written to settle.
 */
.gfrr-context-card {
  background: var(--gfrr-ink);
  border-radius: var(--gfrr-radius-sm);
  padding: 20px;
}

.gfrr-context-card-name {
  margin: 0;
  font-size: 0.875rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--gfrr-on-ink);
}

.gfrr-context-card-summary {
  margin: 4px 0 0;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--gfrr-on-ink-muted);
}

/*
 * ---------------------------------------------------------------------------
 * THE FIXED-PERIOD STATEMENT — BOCO-24722 / D43, Figma frame `7335:629`.
 * ---------------------------------------------------------------------------
 * On Growth Opportunity Analysis and Supplier Trust Review the reporting period
 * select (field 6) is `visibility: hidden` and this line, written per render
 * into HTML field 24 by `oiflex_reporting_period_statement()`, occupies the row
 * D42 gave it: "Reporting period: Sep 2025 – Aug 2026 (the most recent 12
 * complete months)". On the other four reports field 24 is the hidden one and
 * every rule here matches nothing.
 *
 * **This is measured creative, not placeholder styling any more.** Every number
 * below is off `7335:629`, and two of them are the ones to leave alone:
 *
 * - **One colour, three weights.** All three runs are `--gfrr-muted` (#374a55,
 *   which the frame states outright). The contrast is 600 / 700 / 400 and
 *   nothing else. The review mused about the text around the span being "more
 *   subdued" and then drew it all one colour; a second colour here undoes a
 *   decision rather than finishing one.
 * - **The chip is on the label only, and it must not read as a control.** A
 *   bordered info box like the review panel's was considered and rejected for
 *   sitting directly under another such object, as was anything button-like.
 *   So: a tint, a radius, 2px/8px of padding, no border, no shadow, no hover,
 *   no cursor change.
 *
 * 16px/22.4 is the *field label's* size (`7335:658`), not the 14px body copy
 * this rule used to set — the line is a statement of fact about the order, not
 * a caption. **Both are what this element inherits anyway** — measured: with
 * every rule here removed the statement still computes 16px/22.4px — so the two
 * declarations pin the design rather than deliver it, and nothing visible
 * changes if they go. They stay because the value the page inherits is not a
 * value anyone chose for *this* line, and `verify-24722.mjs` red-proves them
 * against the `0.875rem`/`1.5` they replaced rather than against their own
 * removal; its header says which of its lines that argument applies to.
 */
.gfrr-period-statement {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 1rem;
  line-height: 1.4;
  color: var(--gfrr-muted);
  font-weight: 400;
}

/*
 * ---------------------------------------------------------------------------
 * THE ROW'S TWO GAPS ARE NOT EQUAL, AND THE GRID OFFERS ONLY ONE.
 * ---------------------------------------------------------------------------
 * 32 above and 28 below — `7335:645`'s bottom padding and `7335:653`'s top
 * padding, so in Figma the numbers belong to the *neighbours*. In the DOM there
 * is a single `row-gap` for every gap on the card, so the 4 and the 12 of
 * difference come out of this field's own margins.
 *
 * **It is declared on the field, not on the `<p>`.** A grid item establishes an
 * independent formatting context, so a margin on the paragraph inside would
 * produce the same picture — but `#field_8_24`'s box would stop coinciding with
 * the box a reader sees, and that box is what `verify-24721.mjs` measures for
 * "the period slot does not move". Keep the two the same thing.
 *
 * The offsets are **derived, not typed as -8px and -12px**: `--gf-form-gap-y` is
 * the property Gravity Forms' foundation stylesheet reads for
 * `.gform_fields { row-gap }` — measured, `40px` on this install at both 1440
 * and 390 — so the calc goes on meaning "32 above, 28 below" if Gravity Forms
 * or a theme layer ever moves the gap. The `40px` fallback is not decoration: a
 * `var()` that failed to resolve with no fallback makes the calc invalid, drops
 * the declaration, and silently restores 40/40.
 *
 * A negative margin on a grid item shrinks its auto-sized row track, so the
 * painted box rises by the top offset and hangs below the track by the bottom
 * one. That is what turns one 40px gap into 32 and 28.
 *
 * **Selected through the statement rather than through `gfrr-period-field`**,
 * which field 24 *shares with field 6* — a margin on that class would move the
 * select's row on the four selectable reports, where nothing asked for it. The
 * wrapper prefix is only for specificity, on the arithmetic the file's
 * *handing the controls back to GF* block sets out: `:has()` contributes its
 * argument's specificity, so this is `[0,4,0]` and clears anything Gravity
 * Forms declares on `.gfield`.
 *
 * Deliberately **not scoped by width.** Figma draws this frame at 1200 only, so
 * the same two numbers are the assumption at 390 — and 24721's note on `640`
 * says why a fresh query near Gravity Forms' breakpoint is the wrong instinct.
 */
.gform_wrapper:has(.gfrr-step-head) .gfield:has(> .gfrr-period-statement) {
  margin-block: calc(32px - var(--gf-form-gap-y, 40px)) calc(28px - var(--gf-form-gap-y, 40px));
}

/*
 * The label's chip (`7335:711`). `hug`s its text, which is what a flex item
 * does by default, so nothing sets a width.
 */
.gfrr-period-label {
  padding: 2px 8px;
  border-radius: 8px;
  background: var(--gfrr-chip-surface);
  font-weight: 600;
}

/*
 * The value (`7335:709`) — one Figma text node with a bold run over its first
 * nineteen characters, so one element with a `<strong>` inside it. `min-width:
 * 0` lets it wrap inside the flex row rather than forcing the row wider than
 * the card at 390.
 */
.gfrr-period-value {
  min-width: 0;
  font-weight: 400;
}

.gfrr-period-range {
  font-weight: 700;
}

/* ------------------------------------------- handing the controls back to GF */

/*
 * This block sets no design values. Every declaration points a property at the
 * Gravity Forms token that should already have been supplying it, and it exists
 * only because something else on the site takes those properties away.
 *
 * The Vamtam theme's Elementor kit styles bare form controls globally —
 * `.elementor-kit-18 input:not([type="button"]):not([type="submit"])` and its
 * `select` / `textarea` siblings. That selector is one attribute more specific
 * than Gravity Forms' own framework rule
 * (`.gform-theme--framework input[type]:where(…)`, where `:where()` contributes
 * nothing), so **the kit overrides Gravity Forms on every form on this site**,
 * not just this one. Font size, line height and border radius are three of the
 * four it takes; **the fourth is colour**, and that one is only half handled.
 * The block below (*the placeholder is painted by the kit*, BOCO-24546) takes
 * back the **placeholder** colour. It does not take back the colour of *entered*
 * text, and on the two single-line inputs the kit still wins that: field 11 and
 * field 12 type in `rgba(0, 0, 0, 0.8)` where `--gf-ctrl-color` asks for
 * `#071C2B`, because the kit reaches an `input` through `[0,3,1]` and a
 * `textarea` only through `[0,1,1]`. Left alone deliberately — 12.63:1 on the
 * white card against the intended ink's 17.36:1, and the two are 1.37:1 apart,
 * so nothing is visibly wrong and no reported defect is going unfixed. The rest
 * of the framework's properties survive.
 *
 * A CSS API property cannot reach a rule that declares the value directly, so
 * this is the one place — now two — the component has to out-specify rather
 * than configure. **Four** classes and a type is `[0,4,1]`, which beats the
 * kit's `[0,3,1]` outright: `:has()` contributes the specificity of its
 * argument, unlike `:where()`, so `.gform_wrapper:has(.gfrr-step-head)` counts
 * as two classes and not one. This comment said `[0,3,1]` and "ties the kit and
 * wins on order" until BOCO-24546 re-counted it — the rule was always safer
 * than its own note claimed, and the correction matters because the block below
 * relies on the same arithmetic to reach `[0,4,2]`.
 *
 * Order still favours us — this stylesheet is enqueued in the theme layer's
 * `theme` group and prints last — but nothing here depends on that. The kit
 * re-declares inside `(max-width: 1024px)` and `(max-width: 767px)` as well,
 * which is why this must win on specificity rather than by sitting in a
 * narrower media query.
 *
 * If the kit ever stops styling bare controls, delete this block: the tokens it
 * names are the ones Gravity Forms reads anyway, so removing it changes nothing.
 */
.gform_wrapper:has(.gfrr-step-head) .gfield .ginput_container input,
.gform_wrapper:has(.gfrr-step-head) .gfield .ginput_container select,
.gform_wrapper:has(.gfrr-step-head) .gfield .ginput_container textarea {
  font-size: var(--gf-ctrl-font-size);
  line-height: var(--gf-ctrl-line-height);
  border-radius: var(--gf-ctrl-radius);
}

/* ------------------------------- the placeholder is painted by the kit */

/*
 * BOCO-24546. Reported on step 2's "Additional details" as copy that reads like
 * text already typed into the field: *"it looks like it's literally something
 * that's written in there."*
 *
 * It was, in the only sense that matters to a reader. The kit paints entered
 * text and its placeholder **from one declaration**:
 *
 *   .elementor-kit-18 textarea,
 *   .elementor-kit-18 textarea::placeholder,
 *   .elementor-kit-18 input:not([type="button"]):not([type="submit"])::placeholder
 *     { color: var(--e-global-color-9d9c42a) }      // #000000CC
 *
 * So on this site a placeholder is the same colour as typed text *by
 * construction*, on every form, and no amount of muting the value fixes it.
 *
 * **Setting `--gf-ctrl-placeholder-color` alone is not the fix, though this
 * file's own rules say it should be.** Gravity Forms already ships a muted
 * default there, and measured on the rendered page the property resolved
 * correctly — `rgba(17, 35, 55, 0.7)` — while the placeholder painted
 * `rgba(0, 0, 0, 0.8)`. The framework's rule
 * (`.gform-theme--framework ::placeholder:where(…)`, `[0,1,1]`) loses to both kit
 * rules, and **not on the class count** — it matches the kit's `textarea` rule
 * there. It is one **type selector** short of `textarea::placeholder`
 * (`[0,1,2]`; the pseudo-element counts in the type bucket, so the bare
 * `textarea` is the difference), and short by two attributes and a type of the
 * `input` one (`[0,3,2]`, the two `:not([type=…])`). The value was never wrong;
 * the rule reading it was losing.
 *
 * **And there is no configuring around it, because the kit's declaration is not
 * reading a Gravity Forms property at all.** The only custom property that
 * reaches it is `--e-global-color-9d9c42a`, an Elementor global — and that same
 * declaration paints entered text, so muting it would grey out the customer's
 * own typing. That is not a trade-off worth weighing: it is the opposite of the
 * ticket's second acceptance criterion. A selector is the only instrument that
 * can separate the two.
 *
 * Hence the split, and it is deliberate rather than an oversight of the rule at
 * the top of this file: **the colour is still set in
 * `oiflex_form_css_properties()`**, so there is one home for it, and this rule
 * only supplies a selector specific enough to read it. `[0,4,2]` — the four
 * classes of the scope above plus a type and a pseudo-element — beats both kit
 * rules outright rather than on source order.
 *
 * `:is(input, textarea)` contributes only its type, so it costs nothing here
 * and keeps the two in one rule.
 *
 * WHAT THIS DOES NOT COVER, DELIBERATELY. A `<select>` has no `::placeholder`:
 * Gravity Forms renders its empty state as a real `<option>` (see the note on
 * `placeholder` in inc/oi-flex-request-form-scope.php), so "Select
 * granularity…" is selected-option text and still paints at full ink. Greying
 * it needs a different mechanism — `select:has(option[value=""]:checked)` —
 * and it is not what was reported. Left as its own question rather than folded
 * in here.
 *
 * SCOPE. This is one rule over **four controls**, not a fix for field 15. The
 * ticket names "Additional details" (field 15); the identical defect is on
 * "Comments" (field 7) and on the two single-line inputs, 11 (Work email) and
 * 12 (Company name). Seven fields carry a `placeholder` in the form definition,
 * but the other three are the selects excluded above. One declaration is the
 * right size for a defect the kit creates for every field at once; fixing only
 * the reported field would have left three.
 *
 * The two inputs are the pair that matters for the specificity claim, not the
 * textareas: the kit reaches an `input` through `[0,3,2]` — the two
 * `:not([type=…])` attributes — where a `textarea` is only `[0,1,2]`. A fix
 * specific enough for the reported field alone would have left 11 and 12
 * painting their placeholder in the entered-text colour, which is *exactly*
 * `rgba(0, 0, 0, 0.8)` on both, a measured 1.00:1 apart.
 */
.gform_wrapper:has(.gfrr-step-head) .gfield .ginput_container :is(input, textarea)::placeholder {
  color: var(--gf-ctrl-placeholder-color);
}

/* --------------------------------------------------------------- the select */

/*
 * Gravity Forms draws its own select chevron from `--gf-icon-ctrl-select`, a
 * data-URI whose colour is baked in, so the one property the CSS API cannot
 * express is a chevron that follows the brand ink. It is drawn here instead, as
 * a mask on the field's own wrapper: a data URI cannot resolve a custom
 * property, but a mask reads only the shape's alpha, so the paint can come from
 * background-color. Geometry from the mock (6796:3667): 12x8, inset 20 to match
 * the control's own inline padding, vertically centred.
 */
.gform_wrapper:has(.gfrr-step-head) .ginput_container_select {
  position: relative;
}

.gform_wrapper:has(.gfrr-step-head) .ginput_container_select::after {
  content: '';
  position: absolute;
  top: 50%;
  right: 20px;
  width: 12px;
  height: 8px;
  transform: translateY(-50%);
  pointer-events: none;
  background-color: var(--gfrr-ink);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center / 12px 8px;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center / 12px 8px;
}

/* Gravity Forms' own chevron would otherwise sit under this one. */
.gform_wrapper:has(.gfrr-step-head) .ginput_container_select select {
  background-image: none;
  padding-right: 44px;
}

/*
 * ---------------------------------------------------------------------------
 * THE SELECT'S TEXT SITS AT THE TOP OF THE CONTROL — BOCO-24089 ITEM 2
 * ---------------------------------------------------------------------------
 * Reported as "the text in the select fields is not properly aligned; it should
 * be vertically centred", and it was 23px out. Measured by reading real pixels
 * off an element screenshot at a device scale factor of 3, counting ink rows
 * across the text band only (from the control's inline padding to 44px short of
 * the right edge, so the chevron drawn above cannot be mistaken for a glyph):
 *
 *     1440   box 308x50   ink rows 17..65 of 153   top 5.67   bottom 29.00
 *      390   box 278x50   ink rows 17..65 of 153   top 5.67   bottom 29.00
 *
 * Identical at both widths, so this is not a breakpoint bug — the ticket files
 * it under mobile and it was never only mobile.
 *
 * WHY, AND WHY IT IS OURS TO FIX. Three declarations meet on this control and
 * each is individually right:
 *
 *   block-size:    var(--gf-local-height)      ← --gf-ctrl-size-md, 50px, ours
 *   padding-block: var(--gf-local-padding-y)   ← --gf-ctrl-padding-y, 0, GF's
 *   line-height:   var(--gf-ctrl-line-height)  ← 1.4 → 22.4px, ours
 *
 * A 22.4 line box in a 48px content box has 25.6px to spare, and Chrome puts
 * all of it underneath: a `select` renders its selected option in an anonymous
 * block that starts at the content box's top edge. An `input` does not behave
 * this way — the browser centres an input's inner text itself, whatever the
 * padding — which is why every other control on the form looks right and only
 * the two step-1 selects look wrong. It is also why this is not a theme-layer
 * property: `--gf-ctrl-padding-y` is shared with `input` and `textarea`, which
 * are already centred and would go 12.8px too tall.
 *
 * So the fix gives the select back exactly the padding that centres the line
 * box, and takes it from the same tokens rather than restating 50 and 22.4:
 * with `box-sizing: border-box` the content box is the height less the two
 * borders, and half of what the line box leaves over is the padding each side.
 * The control's rendered height does not change — 22.4 of content plus 25.6 of
 * padding plus 2 of border is the 50 it already was — so nothing else that
 * measures this box moves.
 *
 * `--gf-ctrl-line-height` is unitless (1.4), so it has to be multiplied by the
 * font size before it can be subtracted from a length; `--gf-ctrl-font-size` is
 * the same 1rem the block above hands the control.
 *
 * NOT `line-height: calc(var(--gf-ctrl-size) - 2px)`, which centres just as well
 * by half-leading and is one declaration shorter. It would contradict the block
 * above, whose entire purpose is to hand this property back to Gravity Forms'
 * own token after the Vamtam kit takes it — and a reader comparing the two
 * would have to work out which of them is the real line height. Padding is the
 * property actually missing.
 */
.gform_wrapper:has(.gfrr-step-head) .ginput_container_select select {
  padding-block: calc(
    (
      var(--gf-ctrl-size) - 2 * var(--gf-ctrl-border-width) -
        var(--gf-ctrl-line-height) * var(--gf-ctrl-font-size)
    ) / 2
  );
}

/* ------------------------------------------------------- character counters */

/*
 * Gravity Forms renders its own counter when a maximum character count is set.
 * The wording is not the "0 / 500" format in the mock and is not filterable
 * without PHP, so it is hidden and replaced. The underlying maxLength setting is
 * kept, because that is what enforces the limit in the browser and on the server.
 */
.gform_wrapper:has(.gfrr-step-head) .ginput_counter,
.gform_wrapper:has(.gfrr-step-head) .charleft {
  display: none;
}

/* Figma 6796:3688: 13/19.5, right aligned, 4 above. */
.gfrr-counter {
  display: block;
  margin-top: 4px;
  text-align: right;
  font-size: 0.8125rem;
  line-height: 1.5;
  font-weight: 400;
  color: var(--gfrr-muted);
  font-variant-numeric: tabular-nums;
}

.gfrr-counter.is-maxed {
  color: var(--gfrr-required);
}

/* --------------------------------------------------- the eligibility divert */

/*
 * BOCO-24045 / D30. Shown on step 2 when "Existing Onyx client?" is answered
 * **No**, by Gravity Forms' own conditional logic on field 20 — the mirror of
 * the rule that reveals the product question on Yes. The markup comes from
 * `oiflex_eligibility_panel()` in
 * finovate-child/inc/oi-flex-request-form-eligibility.php.
 *
 * ⚠️ **It is deliberately not styled as an error**, and that is the only real
 * design decision in this block. The customer has done nothing wrong — they
 * answered a question honestly and the product does not serve them — so it takes
 * the muted surface and the ink accent that `.gfrr-card` and `.gfrr-quote`
 * already use, not `--gfrr-required`. The red is reserved for "you need to fix
 * something", and there is nothing here for the customer to fix.
 *
 * The *refusal* to continue does show in red, through Gravity Forms' own
 * validation message on field 13, which is the correct place for it: that is the
 * thing the customer can act on by changing their answer.
 *
 * There is no mock. BOCO-24045 states outright that design input on this panel
 * is a styling review rather than something the build waits on, so this adds no
 * new visual idea — every value below is either an existing token or a figure
 * already used by a sibling component.
 */
.gfrr-eligibility {
  /*
   * ⚠️ **No left rule, and its absence is the decision — BOCO-24311 item 3.**
   * 24045 shipped `border-left: 3px solid var(--gfrr-ink)` to tell the panel
   * apart from the review page's `.gfrr-card` without a second surface colour.
   * Design read it as an alert bar: a coloured rule down one edge is the
   * callout/notice convention, and this panel is deliberately *not* an error
   * (see the block above). The muted surface already distinguishes it from
   * `.gfrr-card`, which sits on `--gfrr-field-surface` inside a 1px border, so
   * the rule was carrying no information the background did not.
   *
   * Restoring it is a design change, not a tidy-up. `verify-24311.mjs` asserts
   * the computed `border-left-width` is 0 — and that no `box-shadow` appears on
   * the panel or its field wrapper either, because the ticket says "shadow or
   * border" and only the border was ever there. Neither can come back unnoticed.
   */
  border-radius: 8px;
  background: var(--gfrr-muted-surface);
  padding: 16px 18px;
}

/*
 * ⚠️ **Every rule below is written `.gfrr-eligibility .gfrr-eligibility-*`, and
 * the repetition is load-bearing rather than clumsy.**
 *
 * The Elementor kit styles bare elements globally — `.elementor-kit-18 h3` and
 * `.elementor-kit-18 a` — and at (0,1,1) those beat a lone class at (0,1,0).
 * Measured, not guessed: with the single-class version of these rules the
 * heading rendered at **39px** rather than 16, because the kit's `h3` font-size
 * won, and the link's weight came out 400 rather than 600 for the same reason.
 * Doubling the class makes each selector (0,2,0), which wins.
 *
 * This is the same shape `.gfrr-confirm .gfrr-confirm-title` already uses a few
 * hundred lines down, for the same collision. The general rule for this
 * component is in the block at the top of the file: bare-element styling from
 * the kit reaches anything we render inside an Elementor page, so our own
 * markup needs a scoping ancestor whenever it uses a tag the kit has opinions
 * about.
 *
 * `font-weight` is stated on all three for a second, separate reason:
 * `vamtam-front-all` sets `p, h2, h3 { font-weight: inherit }`, so an unstated
 * weight is inherited from the Gravity Forms field wrapper — which is bold. The
 * body copy rendered bold until this line existed.
 */
.gfrr-eligibility .gfrr-eligibility-title {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--gfrr-ink);
}

.gfrr-eligibility .gfrr-eligibility-body {
  margin: 6px 0 0;
  font-size: 0.875rem;
  font-weight: 400;
  line-height: 1.55;
  color: var(--gfrr-muted);
}

/*
 * Underlined, not a button. A button here would compete with Continue for the
 * primary action on the step, and the primary action is still "change your
 * answer, or stop" — this is the way out, not the way on.
 *
 * The underline is stated rather than inherited: the Vamtam kit strips
 * `text-decoration` from links inside content areas, so an anchor that reads as
 * a link elsewhere can render as plain text here. Colour alone would fail
 * WCAG 1.4.1, which is why this is not left to the cascade.
 */
.gfrr-eligibility .gfrr-eligibility-link {
  display: inline-block;
  margin-top: 10px;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--gfrr-ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.gfrr-eligibility .gfrr-eligibility-link:hover {
  text-decoration-thickness: 2px;
}

/*
 * ⚠️ **Focus is styled separately from hover, and the first version was not.**
 * Both states shared one rule that only thickened the underline 1px → 2px, which
 * meant the panel's *only* interactive element had no focus indicator of its own
 * — it depended entirely on whatever outline the UA or the Elementor kit happened
 * to leave in place, on the link a keyboard user lands on immediately after being
 * refused. A 1px underline change is not a visible focus indicator (WCAG 2.4.7),
 * and it is a change to a property hover is already using.
 *
 * An explicit outline in the ink colour, offset so it reads against the panel's
 * muted surface rather than hugging the glyphs.
 */
.gfrr-eligibility .gfrr-eligibility-link:focus-visible {
  text-decoration-thickness: 2px;
  outline: 2px solid var(--gfrr-ink);
  outline-offset: 3px;
  border-radius: 2px;
}

/*
 * The arrow chip after the link — BOCO-24311 item 2.
 *
 * ---------------------------------------------------------------------------
 * EVERY NUMBER HERE IS MEASURED OFF THE LANDING PAGE, NOT CHOSEN
 * ---------------------------------------------------------------------------
 * The ticket names the landing page's *Talk to Sales* link as the authority.
 * Read from its rendered hero at 1440 rather than from the Figma node, because
 * what has to match is what a customer sees on the other page:
 *
 *   disc      20 x 20, border-radius 100px, background #071C2B
 *   glyph     8px, theme-icons, #72D0EB
 *   gap       8px between the end of the label and the disc's left edge
 *
 * Both colours are tokens here rather than literals: `--gfrr-ink` resolves to
 * `vamtam_accent_1` and `--gfrr-teal` to `vamtam_accent_2`, which are the two
 * globals the landing widget binds. So this tracks a rebrand with that link
 * instead of pinning two hexes somebody has to remember — the same argument the
 * `aria-disabled` cue below makes for its own colour.
 *
 * ---------------------------------------------------------------------------
 * ⚠️ `inline-flex` IS LOAD-BEARING, TWICE
 * ---------------------------------------------------------------------------
 * 1. **It keeps the underline off the disc.** The link above is deliberately
 *    underlined (WCAG 1.4.1 — see its own note), and text decoration propagates
 *    to in-flow inline descendants: a plain `<i>` would have the anchor's
 *    underline painted straight across the chip. `text-decoration: none` on the
 *    child cannot undo that — a propagated decoration is not inherited, it is
 *    drawn by the ancestor. An *atomic* inline-level box is the documented
 *    exception, and `inline-flex` makes one.
 * 2. It centres the 8px glyph in the 20px disc with `align-items`/`justify-
 *    content` rather than replicating the landing widget's 6px padding, which
 *    would have to be re-derived if the disc size ever changed.
 *
 * **What is deliberately NOT copied: `vamtam-btn-icon-wrap`.** The landing disc
 * is absolutely positioned at `inset-inline-start: calc(100% + var(--vamtam-gap))`
 * inside a `padding-inline-end` the Elementor button widget reserves through
 * `--vamtam-whitespace`. None of that machinery exists inside a Gravity Forms
 * HTML field, so the class would land a 20px disc at the anchor's left edge. A
 * plain `margin-left` reaches the same rendered position with nothing to inherit.
 *
 * `vertical-align: middle` because a 20px box beside 14px/1.4 text otherwise
 * sits on the baseline and grows the line box under it.
 */
.gfrr-eligibility .gfrr-eligibility-link-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  /*
   * ⚠️ **`margin-inline-start`, not `margin-left`** — `ar` is an active
   * language on this install and `oiflex_contact_href()` already asks WPML for
   * the current language's contact page, so an RTL render of this panel is a
   * supported state rather than a hypothetical. Measured with `margin-left`:
   * LTR gives the intended 8px between label and disc, RTL gives **0** — the
   * disc butts against the last letter and the 8px lands on its outer edge
   * instead. The logical property is 8px in both directions.
   *
   * It is also what the landing widget does: it positions its own disc with
   * `inset-inline-start`, and this rule's first draft copied the number while
   * dropping the property that made the number direction-safe.
   */
  margin-inline-start: 8px;
  vertical-align: middle;
  border-radius: 50%;
  background: var(--gfrr-ink);
  color: var(--gfrr-teal);
  /* The glyph. `font-family: 'theme-icons'` comes from the icon library's own
     stylesheet, at `!important`, so only the size is stated here. */
  font-size: 8px;
}

/* ------------------------------------------------------------- review page */

.gfrr-eyebrow {
  margin: 22px 0 10px;
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--gfrr-ink);
}

.gfrr-eyebrow:first-child {
  margin-top: 0;
}

.gfrr-card {
  border: 1px solid var(--gfrr-muted-surface);
  border-radius: var(--gfrr-radius-sm);
  padding: 18px 20px;
  margin: 0;
  background: var(--gfrr-field-surface);
}

.gfrr-card-title {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--gfrr-ink);
}

.gfrr-card-meta,
.gfrr-card-line {
  margin: 4px 0 0;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--gfrr-muted);
}

/* Quoted free text, used for report comments and additional details. */
.gfrr-quote {
  margin: 14px 0 0;
  background: var(--gfrr-muted-surface);
  border-radius: 8px;
  padding: 12px 14px;
  font-size: 0.875rem;
  line-height: 1.55;
  color: var(--gfrr-muted);
  white-space: pre-line;
  overflow-wrap: anywhere;
}

.gfrr-no-payment {
  margin: 20px 0 0;
  text-align: right;
  font-size: 0.8125rem;
  color: var(--gfrr-muted);
}

@media (max-width: 560px) {
  .gfrr-no-payment {
    text-align: left;
  }
}

/* ----------------------------------------------------------------- buttons */

/*
 * Back sits left, Continue and Submit sit right. `margin-right: auto` on the Back
 * button rather than `space-between` keeps a lone Continue on the right on page 1,
 * where there is no Back button to push it over.
 *
 * The buttons' own geometry — pill radius, height, padding, colours — is CSS
 * API, not here. This is placement only.
 */
/*
 * The one place a token is reassigned rather than set once on the wrapper.
 * Gravity Forms derives the secondary button's outline from the *input* border
 * colour — `--gf-ctrl-btn-border-color-secondary` and `--gf-ctrl-border-color`
 * are both written from the `inputBorderColor` style setting — and the design
 * separates them: fields get a faint rgba(0,0,0,.1) hairline (6796:3665), Back
 * gets a 2px ink outline (6796:3690).
 *
 * Declaring the token here rather than on the wrapper is what makes it stick.
 * Gravity Forms' own property block sits on the wrapper and is emitted after
 * ours, so it wins there; a value set on a closer ancestor of the button is not
 * competing with that block at all, it simply arrives nearer in the inheritance
 * chain.
 */
.gform_wrapper:has(.gfrr-step-head) .gform_page_footer,
.gform_wrapper:has(.gfrr-step-head) .gform_footer {
  --gf-ctrl-btn-border-color-secondary: var(--gfrr-ink);
  --gf-ctrl-btn-border-width-secondary: 2px;

  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 32px;
  border: 0;
  padding: 0;
}

.gform_wrapper:has(.gfrr-step-head) .gform_page_footer .gform_previous_button,
.gform_wrapper:has(.gfrr-step-head) .gform_footer .gform_previous_button {
  margin-right: auto;
}

.gform_wrapper:has(.gfrr-step-head) .gform_page_footer input[type="submit"],
.gform_wrapper:has(.gfrr-step-head) .gform_page_footer input[type="button"],
.gform_wrapper:has(.gfrr-step-head) .gform_footer input[type="submit"],
.gform_wrapper:has(.gfrr-step-head) .gform_footer input[type="button"] {
  width: auto;
  margin: 0;
}

/*
 * The "not yet" cue on Continue — BOCO-24039 / D15.
 *
 * `syncGate()` used to set the `disabled` attribute, which greyed the button out
 * for free because Gravity Forms styles a disabled button itself. It no longer
 * does: a disabled button is unreachable by keyboard and announces nothing, so
 * it now sets `aria-disabled` instead and lets the click through to Gravity
 * Forms' own validation. See the long note beside `syncGate()`.
 *
 * `aria-disabled` carries no styling of its own, so **without this rule the cue
 * disappears silently** — the form works, reads correctly to a screen reader, and
 * simply stops showing that the page is incomplete. Hence a rule, and hence a gate
 * that measures what the rule produces rather than that the rule exists.
 *
 * -------------------------------------------------------------------------
 * IT MUTES THE BACKGROUND, NOT THE WHOLE BUTTON — AND THAT IS THE POINT
 * -------------------------------------------------------------------------
 * The obvious cue, and the first thing shipped here, was Gravity Forms' own
 * disabled treatment: `--gf-local-opacity: var(--gf-ctrl-btn-opacity-disabled)`,
 * which is `0.5`. **That is a contrast failure on an operable control**, and it was
 * caught in review rather than by measuring — which is why the numbers are written
 * down here rather than left to be re-derived.
 *
 * `opacity` composites the element *and its text*. The primary button is `#FFFFFF`
 * on `--gfrr-ink` (`#071C2B`) over the white form card, so at half opacity the
 * label renders as white on `#838E95` — **3.35:1**, against the 4.5:1 WCAG 1.4.3
 * asks for.
 *
 * That was defensible while the button was genuinely `disabled`, because 1.4.3
 * exempts text in an *inactive* user-interface component. **Removing the `disabled`
 * attribute removed the exemption with it**: the button is now focusable, operable,
 * and something a keyboard user is expected to read and then press. So the
 * accessibility fix had quietly taken the label below the threshold. The cue and the
 * exemption were a pair, and the first pass replaced only one half of it.
 *
 * So the background is muted and **the label is left at full strength**. Blending
 * `--gfrr-ink` 65% with white gives `#5E6B75`, which carries white text at
 * **5.47:1**. The blend deliberately stops short of the most it could be: 60/40
 * (`#6A7780`) still passes at 4.60:1, so ending at 65/35 leaves the card colour and
 * the brand token room to move without silently crossing the line. `color-mix()`
 * derives it from the token so it tracks a rebrand rather than pinning a hex this
 * component would have to remember to update.
 *
 * **The fallback is an `@supports` block, and the reason is a trap worth stating.**
 * The obvious shape — a `--x: #5e6b75;` immediately before a `--x: color-mix(…);` —
 * does nothing at all. Custom property values are not validated at parse time (any
 * token stream is accepted), so the second declaration always wins, and if
 * `color-mix()` were unsupported the property would go invalid at *substitution*
 * time and take `background-color` to its initial value. That pattern guarantees
 * the opposite of what it looks like, and shipping it briefly meant a browser
 * without `color-mix()` would paint a **white button with a white label** — worse
 * than the wash this replaced.
 *
 * `@supports` gates the whole declaration instead of racing it, so the literal
 * genuinely holds where the function is missing. The two values are the same
 * colour: the literal is what `color-mix()` computes to at today's token, and the
 * function is there so the cue tracks a rebrand rather than pinning a hex somebody
 * has to remember.
 *
 * **This is a deliberate departure from the mock**, which draws the flat 50% wash.
 * Flagged on BOCO-24039 rather than changed quietly.
 *
 * -------------------------------------------------------------------------
 * The selector names `.gform-theme--framework` on purpose. Both colour
 * declarations are Gravity Forms' own local tokens, consumed by `background-color:
 * var(--gf-local-bg-color)` in the framework stylesheet — so if that theme is
 * switched off, they are never read and this cue is simply gone. Naming it makes
 * the dependency stated rather than accidental. An earlier version of this rule
 * claimed a literal fallback protected a non-framework render; it cannot, because
 * without the framework theme the consuming declaration does not exist to fall back
 * *to*. The gate measures rendered contrast, so a theme switch fails loudly instead
 * of dropping the cue.
 *
 * Setting the tokens rather than `background-color` directly is the same reasoning
 * as everywhere else in this file: GF's own rules compose off them, so hover and
 * focus keep working instead of having to be restated.
 *
 * `--gf-ctrl-btn-cursor-disabled` deliberately has no `var()`: GF does not define
 * one, and `default` is what a disabled button computed to here — measured, not
 * guessed.
 */
.gform-theme--framework.gform_wrapper:has(.gfrr-step-head) .gform_page_footer input[type="button"][aria-disabled="true"],
.gform-theme--framework.gform_wrapper:has(.gfrr-step-head) .gform_page_footer input[type="submit"][aria-disabled="true"],
.gform-theme--framework.gform_wrapper:has(.gfrr-step-head) .gform_footer input[type="button"][aria-disabled="true"],
.gform-theme--framework.gform_wrapper:has(.gfrr-step-head) .gform_footer input[type="submit"][aria-disabled="true"] {
  --gf-local-bg-color: #5e6b75;
  --gf-local-border-color: #5e6b75;
  --gf-local-cursor: default;
}

@supports (background-color: color-mix(in srgb, red 65%, white)) {
  .gform-theme--framework.gform_wrapper:has(.gfrr-step-head) .gform_page_footer input[type="button"][aria-disabled="true"],
  .gform-theme--framework.gform_wrapper:has(.gfrr-step-head) .gform_page_footer input[type="submit"][aria-disabled="true"],
  .gform-theme--framework.gform_wrapper:has(.gfrr-step-head) .gform_footer input[type="button"][aria-disabled="true"],
  .gform-theme--framework.gform_wrapper:has(.gfrr-step-head) .gform_footer input[type="submit"][aria-disabled="true"] {
    --gf-local-bg-color: color-mix(in srgb, var(--gfrr-ink) 65%, #ffffff);
    --gf-local-border-color: color-mix(in srgb, var(--gfrr-ink) 65%, #ffffff);
  }
}

/* ==========================================================================
   THE CONFIRMATION SCREEN — BOCO-24040 / BOCO-23591, D23. Figma 6796:5213.
   ==========================================================================

   ⚠️ NOTHING ABOVE THIS LINE APPLIES HERE, AND THAT IS NOT AN OVERSIGHT.

   Every other rule in this file is scoped `.gform_wrapper:has(.gfrr-step-head)`.
   Gravity Forms ends an AJAX submission with

       jQuery('#gform_wrapper_8').replaceWith(confirmation_content);

   — `replaceWith`, not `.html()` — so on this screen the wrapper is *gone* and
   `.gfrr-step-head`, which the form's own HTML fields emit, goes with it. Both
   halves of that selector leave together. So this block inherits no card, no
   grid, no padding and no white panel: it brings its own.

   **It does not bring its own palette, and that is the one exception.** The
   `--gfrr-*` tokens and `--gfrr-card-width` are shared, by naming
   `.gfrr-confirm` in that rule's selector list rather than restating it — so
   this screen is layout-independent but colour-coupled. The qualification is
   worth knowing before assuming any `var()` below resolves on its own: see the
   note beside `.gform_wrapper:has(.gfrr-step-head), .gfrr-confirm` at the top,
   which carries the one way that coupling could bite and why it is left.

   `.gfrr-status`, the status panel, is a child of the wrapper and is removed
   with it. That matches the design — no panel is drawn beside this screen — and
   it is also what releases D20a's widget lift, since all four of those rules are
   gated on `body:has(.gfrr-status…)`. Nothing here has to undo it.

   TWO PARENT-THEME DEFENCES ARE REPEATED FROM `.gfrr-step-title` ABOVE, because
   this screen renders inside the same Elementor band and meets the same two
   overrides: the theme puts negative letter-spacing on `h2`, and it bolds body
   copy inside this band. `letter-spacing: normal` and an explicit
   `font-weight: 400` are load-bearing, not defaults written out. */

.gfrr-confirm {
  /* The box the form used to occupy: Figma 6796:5216, a 1200 container with the
     same 48/40 padding as the form card, so the page does not shift when the
     screen swaps. The tokens are the form's own.

     ⚠️ `inline-size` IS LOAD-BEARING AND `max-width` ALONE IS NOT ENOUGH.
     The Elementor widget holding the form is a **flex item** of `.e-con-inner`,
     so it takes its width from its content rather than from its parent. The
     form sized it to 1200 by being a grid of definite tracks (728 + 32 + 360 +
     80 of padding); the confirmation replaces that content, and with only a
     `max-width` here its max-content contribution is whatever the copy happens
     to need — measured at **682**, which is the lede's 536 measure plus the
     card's padding and border. The card then renders 602 instead of 1040 and
     the screen is three-fifths of its drawn width.

     A definite `inline-size` restores the 1200 contribution, and
     `max-inline-size: 100%` is what keeps that from overflowing anything
     narrower. Both halves are needed; this is not a belt-and-braces pair. */
  inline-size: var(--gfrr-card-width);
  max-inline-size: 100%;
  margin-inline: auto;
  padding: var(--gfrr-card-pad-y) var(--gfrr-card-pad-x);
}

/* Figma 6796:5218: a 1040 card, radius 20, a one-pixel rule, 32 padding — and
   deliberately *not* the form panel's radius 24, 40/48 padding and drop shadow.
   The two cards are drawn differently in the mock and the difference is kept.

   The 72px block padding is 32 from this frame plus the 40 that 6796:5219
   ('StepConfirmation') adds inside it; one declaration rather than a nested
   wrapper that exists only to hold padding. */
.gfrr-confirm-card {
  max-width: 1040px;
  margin-inline: auto;
  padding: 72px 32px;
  background: var(--gfrr-field-surface);
  border: 1px solid var(--gfrr-rule);
  border-radius: 20px;
}

/* The success medallion. Figma 6796:5221: a 72px teal disc holding a 36px check
   box. `grid` + `place-items` rather than flex so the SVG centres on both axes
   with no line-height artefact, and `flex: none` is unnecessary because this is
   a block-level child rather than a flex item. */
.gfrr-confirm-icon {
  display: grid;
  place-items: center;
  inline-size: 72px;
  block-size: 72px;
  margin-inline: auto;
  border-radius: 50%;
  background: var(--gfrr-teal);
  /* The check inherits this through `stroke="currentColor"`; Figma 6796:5223
     strokes it in the ink colour. */
  color: var(--gfrr-ink);
}

.gfrr-confirm-icon svg {
  inline-size: 36px;
  block-size: 36px;
  display: block;
}

/* Figma 6796:5225: Open Sans 28/42, weight 700, centred, 24 above.

   ⚠️ THE `.gfrr-confirm` PREFIX IS WHAT MAKES THIS RULE APPLY AT ALL, and it is
   on exactly two families in this block — this one and the CTA. The reason is
   the same in both cases: they are the only two elements here whose **tag** the
   parent theme styles. A bare `.gfrr-confirm-title` is (0,1,0) and loses to the
   theme's own `h2` rule, which is (0,1,1); measured, the heading rendered 49px
   with -1.6px tracking, both of them the theme's. The prefix takes it to (0,2,0)
   and it wins. The `p`, `ul` and `span` rules around it need no prefix because
   nothing else claims them — verified by measurement, not assumed.

   `letter-spacing: normal` then undoes the theme's negative h2 tracking, which
   is a separate defence from the specificity: winning the cascade with no
   `letter-spacing` declaration would inherit nothing and leave the default. */
.gfrr-confirm .gfrr-confirm-title {
  margin: 24px 0 0;
  font-size: 1.75rem;
  line-height: 1.5;
  font-weight: 700;
  letter-spacing: normal;
  text-align: center;
  color: var(--gfrr-ink);
}

/* Figma 6796:5227: Open Sans 16/25.6, weight 400, centred, 12 above, and held
   to the 536px measure the mock sets rather than running the full card width.
   The weight is stated because the Vamtam theme bolds body copy in this band. */
.gfrr-confirm-lede {
  max-width: 536px;
  margin: 12px auto 0;
  font-size: 1rem;
  line-height: 1.6;
  font-weight: 400;
  text-align: center;
  color: var(--gfrr-muted);
}

/* Figma 6796:5229: a 420 card, radius 12, 24 padding, on the muted surface,
   centred under the lede with 32 above. */
.gfrr-confirm-next {
  max-width: 420px;
  margin: 32px auto 0;
  padding: 24px;
  border-radius: 12px;
  background: var(--gfrr-muted-surface);
}

/* Figma 6796:5231: Open Sans 14/21, weight 700, uppercase, 0.7 tracking. The
   tracking is stated in px because it is a measured value from the mock and not
   a ratio; 0.7/14 is 0.05em, which is what this is. */
.gfrr-confirm-next-title {
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.5;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gfrr-ink);
}

/* The three timeline rows are a list because they are a sequence, and a screen
   reader saying "list, 3 items" is the whole of what the visual sequence
   conveys. The reset is explicit: this renders inside an Elementor band whose
   kit styles `ul` and `li`, and D19 is the record of that arriving from a third
   direction. */
.gfrr-confirm-timeline {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Figma 6796:5232 and siblings: a 12 gap between the dot and the text, 12 above
   each row — including the first, which is what separates it from the heading.
   Figma draws a #E0E0E0 bottom stroke on each row and marks it *not visible*
   (`stroke=#E0E0E0:hidden`), so no divider is drawn. */
.gfrr-confirm-step {
  display: flex;
  gap: 12px;
  padding-top: 12px;
}

/* Figma 6796:5234: an 8px teal disc, dropped 6 from the row's top edge so it
   sits on the cap height of the term beside it rather than on its ascender.
   `flex: none` because the row is a flex container and a 8px item with no
   content would otherwise be shrunk to nothing by a long term. */
.gfrr-confirm-dot {
  flex: none;
  inline-size: 8px;
  block-size: 8px;
  margin-top: 6px;
  border-radius: 50%;
  background: var(--gfrr-teal);
}

/* Column, and `min-width: 0` so a long unbroken string wraps instead of forcing
   the row wider than the card. */
.gfrr-confirm-step-body {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* Figma 6796:5237: Open Sans 14/21, weight 600. */
.gfrr-confirm-term {
  font-size: 0.875rem;
  line-height: 1.5;
  font-weight: 600;
  color: var(--gfrr-ink);
}

/* Figma 6796:5239: Open Sans 13/19.5, weight 400. */
.gfrr-confirm-detail {
  font-size: 0.8125rem;
  line-height: 1.5;
  font-weight: 400;
  color: var(--gfrr-muted);
}

/* Figma 6796:5256: the button row, 32 above, centred. */
.gfrr-confirm-cta {
  margin: 32px 0 0;
  text-align: center;
}

/* Figma 6796:5257: a pill — `r=16777200` in the node JSON is Figma's sentinel
   for a fully rounded corner, not a pixel value — 263x56, ink, 14/28 padding,
   with Open Sans 16/24 weight 600 in white.
   `inline-flex` + `min-block-size` rather than a fixed height so the control
   grows with a translated label instead of clipping it, which is the whole
   reason the width is not pinned either. `text-decoration: none` is stated
   because this is an `<a>` inside body copy.

   ⚠️ PREFIXED FOR THE SAME REASON AS THE HEADING, and it failed harder: a bare
   class lost `font-weight`, `color` and `text-decoration` to the theme's link
   rules, so the label rendered 400-weight, underlined, and in the theme's link
   colour **against the ink background this rule had already applied** — a
   measured contrast of 1.00:1, which is to say invisible. The geometry all
   applied, so the button looked plausible in a screenshot and was unreadable.
   That is why the gate asserts the contrast ratio rather than the class. */
.gfrr-confirm .gfrr-confirm-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: 56px;
  padding: 14px 28px;
  border-radius: 100px;
  background: var(--gfrr-ink);
  color: var(--gfrr-on-ink);
  font-size: 1rem;
  line-height: 1.5;
  font-weight: 600;
  text-align: center;
  text-decoration: none;
}

/* `:visited` is in this list because the CTA points at a page the customer very
   plausibly has visited — it is where they came from — and the theme's visited
   colour would otherwise repaint the label on the ink. */
.gfrr-confirm .gfrr-confirm-button:hover,
.gfrr-confirm .gfrr-confirm-button:focus,
.gfrr-confirm .gfrr-confirm-button:visited {
  color: var(--gfrr-on-ink);
  text-decoration: none;
}

/*
 * The phone layout. Figma 6796:5263.
 *
 * 767 rather than 560, because this is the same breakpoint the status sheet
 * takes and it is Elementor's own active mobile breakpoint on this install
 * (`mobile = 767`); the 560 queries elsewhere in this file predate that and are
 * type-only tweaks.
 *
 * What changes is the type scale, the padding and the button's width. The copy
 * does not: the desktop and mobile frames carry the same ten strings, checked
 * node by node.
 */
@media (max-width: 767px) {
  /*
   * Figma 6796:5261 draws 24/10 around the card — and the 10 is **deliberately
   * dropped**, which is the one place this screen departs from the mock's
   * numbers.
   *
   * The mock's gutter is measured inside a 390-wide frame, and the band on the
   * page already pays it. **BOCO-24089 item 1 changed how much**: the band's own
   * 20 used to be joined by an unrecorded 16 on the grey card inside it, so this
   * comment described a 36px gutter and a `.gform_wrapper` 318 wide at x=36.
   * That 16 is gone and the form is now 350 at x=20 — which is the number every
   * frame draws, `6796:4193` for the copy column and `6796:5142` for the panel.
   *
   * The decision is unchanged and the reason for it is the same: honouring the
   * mock's 10 here as well would count the gutter twice, leaving the card
   * narrower than the form beside it — a visible step between the third step and
   * the confirmation, on the one transition where the customer is looking at
   * both. `verify-24040.mjs` asserts the two match by measuring the form rather
   * than by naming a width, so it followed the change without an edit.
   *
   * It was never only cosmetic. At the old 318, dropping to 298 left the card's
   * content box at 256 where "Explore Additional Reports" plus the CTA's 56px of
   * padding needs 263, so the label wrapped and the button rendered 72 tall
   * against a drawn 48. The margin is wider now — 350 gives 310 — but the shape
   * of the failure has not changed, so neither has the rule.
   *
   * The 24 above and below is the mock's and is kept.
   */
  .gfrr-confirm {
    padding: 24px 0;
  }

  /* Figma 6796:5263: 40/20/32/20, and the card is the full column width. */
  .gfrr-confirm-card {
    padding: 40px 20px 32px;
  }

  /* Figma 6796:5270: Open Sans 24/30.
     Prefixed to match the desktop rule — an unprefixed override at (0,1,0)
     would lose to its own (0,2,0) base rule and the heading would simply stay
     28px on a phone. A media query changes nothing about specificity. */
  .gfrr-confirm .gfrr-confirm-title {
    font-size: 1.5rem;
    line-height: 1.25;
  }

  /* Figma 6796:5272: Open Sans 15/24, held to a 308 measure. */
  .gfrr-confirm-lede {
    max-width: 308px;
    font-size: 0.9375rem;
    line-height: 1.6;
  }

  /* Figma 6796:5274: 20 padding, and no 420 cap — the card fills the column. */
  .gfrr-confirm-next {
    max-width: none;
    padding: 20px;
  }

  /* Figma 6796:5276: Open Sans 13/21, 0.65 tracking — 0.05em again. */
  .gfrr-confirm-next-title {
    font-size: 0.8125rem;
    line-height: 1.615;
  }

  /* Figma 6796:5296: 24 above rather than 32. */
  .gfrr-confirm-cta {
    margin-top: 24px;
  }

  /* Figma 6796:5297: full width, 48 tall, 12/28 padding. Prefixed for the same
     reason as the heading above. */
  .gfrr-confirm .gfrr-confirm-button {
    display: flex;
    inline-size: 100%;
    min-block-size: 48px;
    padding: 12px 28px;
  }
}
