/*
 * dfs-motion-peptide-floating-header.css — split from dfs-motion.css (@decision D159), 2026-08-13.
 * Loads via get_style_depends() with the core 'dfs-motion' handle as its
 * dependency, so core always precedes it and core<->widget document order
 * is preserved from the monolith era.
 */



/* ==========================================================================
   Site Menu widget
   ==========================================================================

   REWRITTEN 2026-08-12 against the reference's OWN COMPILED STYLESHEET and
   served HTML, not against sampled computed styles. @decision D69.

   The v1 rules this replaces were a plausible full-screen overlay menu and
   shared almost nothing with the mechanism they were meant to reproduce:
   v1 pinned a `position: fixed; inset: 0` overlay and faded it, where the
   reference expands a `grid-template-rows: 0fr -> 1fr` panel that hangs 8px
   under an inset bar inside the same fixed header. Nothing about the v1
   markup could have produced the measured behaviour.

   WHAT READING THEIR SOURCE SETTLED THAT SAMPLING HAD NOT

   `--spacing: .0625rem` is ONE PIXEL at a 16px root. Every `p-24`, `gap-8`,
   `pt-16` in their markup is therefore that many pixels. MEASURED-SPEC §3
   recorded a "spacing correction" saying these were 0.25rem units and that
   `py-128` meant 512px; that is wrong, and it was wrong in the document that
   every value here would otherwise have been scaled by.

   The logo roll distance falls straight out of it: the window is `h-24`
   (24px) and the two marks sit in a `gap-2` column, so the track slides
   24 + 2 = 26px. §6 measured "26px apart, track slides 26px" without being
   able to say why. Same mechanism drives the MENU/CLOSE label, which §6 had
   recorded as a visibility toggle — it is a translateY roll of a two-item
   column in an `h-[1em] w-[3.5em]` window.

   THE FLUID TYPE SCALE, exactly:

     --fluid-slope: calc( ( 100vw - 375px ) / 1225 )
     nav link:  clamp( 36px, 36px + 28 * slope, 64px )   (their --text-h2)
     bar label: clamp( 14px, 14px +  2 * slope, 16px )   (their --text-accent)

   This reconciles EXACTLY at three independent widths, which is what makes it
   safe to build on: §2's "56.617px" and "15.473px" both solve to 100vw=1277,
   and its "37.257px" solves to 430. Those readings are labelled 1860/540/390
   in the spec. The formula is right and the labels are not — so anything
   rebuilt from the labelled widths would have been fitted to a viewport the
   numbers never came from.

   Panel expand is `0fr -> 1fr` on a grid row with an `overflow: hidden` child.
   §6's "grid-template-rows: 0 -> 639px" was the RESOLVED value of `1fr` at
   one viewport; building to 639px would have frozen the panel at one height
   and clipped every other content length. */

.dfs-peptide-floating-header {
	/* ---------------------------------------------------------------
	 * Their design system, carried on our own property names.
	 *
	 * Declared on the wrapper, never on a child: custom properties
	 * inherit downward only, and the bar reads several of these from
	 * above. A `--dfs-sm-*` declared on the bar is invisible to the
	 * header that positions it.
	 * --------------------------------------------------------------- */

	--dfs-sm-sans: "aktiv-grotesk", ui-sans-serif, system-ui, -apple-system, sans-serif;
	--dfs-sm-mono: "Geist Mono", ui-monospace, "Fira Code", "SF Mono", monospace;

	/* The fluid slope. Unitless-looking but a LENGTH: 100vw minus the
	 * 375px floor, over the 1225px span up to their 1600px ceiling. */
	--dfs-sm-slope: calc( ( 100vw - 375px ) / 1225 );

	/*
	 * THE TYPE SYSTEM, READ FROM THEIR TOKENS — size, weight, line-height AND
	 * tracking, each declared per token rather than per utility class.
	 *
	 * Four of these were inferred before their `--text-*--*` variables were
	 * found, and four of the inferences were wrong in the same direction:
	 * plausible, close, and not theirs.
	 *
	 *   label tracking   -0.025em inferred, -0.02em declared. -0.025em is
	 *                    their `--tracking-tight` utility, which is a real
	 *                    token — just not the one this text uses.
	 *   contact leading  1.5 inferred, 1.43 declared.
	 *   caption size     taken from the label token; it has its OWN token,
	 *                    `--text-accent-sm`, a clamp two steps smaller.
	 *   backdrop blur    4px inferred from Tailwind's default `blur-sm`.
	 *                    They redefine `--blur-sm: 8px`.
	 *
	 * The last one is the instructive one: `backdrop-blur-sm` is a real
	 * Tailwind class with a documented default, so reasoning from the class
	 * name gave a confident answer that was half the real value. The class name
	 * is not the value; the token is.
	 */
	--dfs-sm-nav-size: clamp( 36px, calc( 36px + 28 * var( --dfs-sm-slope ) ), 64px );
	--dfs-sm-nav-leading: 1.125;
	--dfs-sm-nav-tracking: -0.05em;

	--dfs-sm-label-size: clamp( 14px, calc( 14px + 2 * var( --dfs-sm-slope ) ), 16px );
	--dfs-sm-label-tracking: -0.02em;

	--dfs-sm-caption-size: clamp( 12px, calc( 12px + 2 * var( --dfs-sm-slope ) ), 14px );

	--dfs-sm-body-size: 16px;
	--dfs-sm-body-sm-size: 14px;
	--dfs-sm-body-sm-leading: 1.43;

	/* Grid margin steps at their two breakpoints. 2rem at >=1024 is what
	 * makes the bar 1213px wide inside a 1277px viewport: 1277 - 64. */
	/*
	 * @decision D79  RESPONSIVE VALUES ARE SELECTED HERE, NOT EMITTED INLINE.
	 *
	 * An inline `--dfs-sm-margin: 32px` on the element beats every media query
	 * in this file, so a single inline value freezes the margin at its desktop
	 * setting on a phone — measured as our bar inset reading 32 against the
	 * reference's 16 at 390, a 100% miss on a value the stylesheet had right.
	 *
	 * So render() emits the three BREAKPOINT variables and the media queries
	 * pick between them. One source of truth per breakpoint, the control works
	 * at all three, and nothing inline can outrank a query it is not part of.
	 */
	/*
	 * BRAD'S HOUSE CONVENTION: 40px desktop, 20px mobile, replacing the
	 * reference's 2rem / 1.5rem / 1rem. A chosen deviation, not a drift — he
	 * sets the width with the Elementor section when a build needs otherwise.
	 */
	--dfs-sm-margin-mobile: 20px;
	--dfs-sm-margin-tablet: 20px;
	--dfs-sm-margin-desktop: 40px;
	--dfs-sm-margin: var( --dfs-sm-margin-mobile );

	--dfs-sm-panel-pad-mobile: 24px;
	--dfs-sm-panel-pad-desktop: 64px;

	/* NO CAP by default — the Elementor section sets the width when needed. */
	--dfs-sm-max-width: none;
	--dfs-sm-columns: 12;
	--dfs-sm-gutter: 16px;

	/*
	 * THE HOVER INDENT IS THE MARKER MAKING ROOM FOR ITSELF: 32px of square
	 * plus 32px of gap. On mobile there IS no marker, so the reference indents
	 * by about 9px instead of 64 — a 64px shift inside a 310px column reads as
	 * the link falling out of the list rather than responding to a touch.
	 */
	/*
	 * BREAKPOINT PARTS. D79, FOURTH OCCURRENCE — after the side margin, the
	 * marker size and the bar padding. The pattern is now unmistakable: ANY value
	 * that differs by breakpoint must be emitted by render() only as its
	 * '-desktop' / '-mobile' parts and resolved here, because a flat inline
	 * custom property outranks every media query in this file.
	 *
	 * Measured symptom each time: the control looks right in the panel and the
	 * property measures the desktop value on the phone rung. Here the mobile
	 * indent read 64px against the 8px it was set to.
	 */
	--dfs-sm-nav-indent-desktop: 64px;
	--dfs-sm-nav-indent: var( --dfs-sm-nav-indent-mobile );
	/* Halved at Brad's call — 16px beside 36px type read as too far. */
	/*
	 * @decision D154 — THE MOBILE INDENT MUST CLEAR THE MOBILE MARKER.
	 *
	 * Was 8px against a 16px marker, so the marker overlapped the active
	 * link's first letter by exactly 8px. Measured at 390x844: marker right
	 * edge 60, text left 52 — clearance MINUS 8. Desktop measured +32.
	 *
	 * The 8 was set as though this property were the GAP beside the marker.
	 * It is not: the control's own description says "How far the link steps
	 * right. This is the marker making room for itself", so it is the TOTAL
	 * step, and any value below the marker's width is an overlap by
	 * construction.
	 *
	 * 32 is not a new number either — desktop runs indent = 2x marker
	 * (64 against 32), which leaves exactly one marker-width of clearance.
	 * Mobile now runs the same ratio against its own 16px marker.
	 */
	--dfs-sm-nav-indent-mobile: 32px;
	/*
	 * BREAKPOINT VARIABLES, not one resolved value — D79 again, and it recurred
	 * within an hour of being written down. An inline '--dfs-sm-marker-size: 32px'
	 * from render() outranks every media query in this file, so the mobile rule
	 * below was inert and the marker stayed 32px beside 36px type.
	 *
	 * The tell is always the same: a control that looks right in the panel and
	 * measures wrong on one rung. Anything responsive has to be SELECTED here and
	 * only ever emitted as its per-breakpoint parts.
	 */
	--dfs-sm-marker-size-desktop: 32px;
	--dfs-sm-marker-size-mobile: 16px;
	--dfs-sm-marker-size: var( --dfs-sm-marker-size-mobile );
	--dfs-sm-marker-colour: var( --dfs-sm-brand );

	/* Per-element colour, each defaulting to the token above it so an untouched
	 * widget behaves exactly as before these existed. */
	--dfs-sm-bar-fg-top: var( --dfs-sm-bar-fg );
	--dfs-sm-bar-fg-scrolled: var( --dfs-sm-bar-fg );
	--dfs-sm-toggle-fg: var( --dfs-sm-bar-fg );
	--dfs-sm-toggle-fg-hover: #FFFFFF;
	--dfs-sm-toggle-opacity-hover: 1;
	--dfs-sm-cta-fg: #141314;
	--dfs-sm-cta-fill-hover: var( --dfs-sm-brand );
	--dfs-sm-cta-fg-hover: #FFFFFF;
	--dfs-sm-nav-fg: var( --dfs-sm-bar-fg );
	--dfs-sm-nav-fg-active: var( --dfs-sm-brand );
	--dfs-sm-contact-fg: var( --dfs-sm-bar-fg );
	--dfs-sm-contact-scale: 1.03;
	--dfs-sm-card-fg: var( --dfs-sm-bar-fg );
	--dfs-sm-card-fg-hover: var( --dfs-sm-brand );

	--dfs-sm-header-pad-top: 16px;
	--dfs-sm-bar-gap: 8px;          /* bar -> panel, their `gap-8` */
	--dfs-sm-bar-pad-block: 16px;   /* their `py-16` */

	/*
	 * THE BAR'S SIDE PADDING RAMPS WITH SCROLL — it is not a constant.
	 *
	 * This is what MEASURED-SPEC §6 records as "Bar transition: padding,
	 * background-color, color @ 0.5s cubic-bezier(0,0,0.2,1)" and then never
	 * says what the padding DOES. Their served markup answers it: the bar
	 * carries an inline `padding-left: 0px; padding-right: 0px` at the top of
	 * the page, written by their own script, and by the time the page is
	 * scrolled the logo has moved from x=32 to x=96 inside a bar whose own x is
	 * still 32. That is 64px of padding that was not there at rest.
	 *
	 * So the bar starts edge-to-edge with its container and draws its content
	 * inwards as the background fades up underneath it. Built as a static 16px
	 * the logo sat at 48 in both states — right in neither.
	 *
	 * The JS writes `--dfs-sm-bar-pad-inline` on the same ramp that drives the
	 * background, so the two move together the way the shared transition
	 * declaration says they should.
	 */
	/*
	 * THE BAR DOES NOT PULL ITS CONTENT INWARDS ON MOBILE.
	 *
	 * Brad: "as mobile menu goes down, the logo and menu are pulled in middle,
	 * take away that effect entirely ... they did it but it's poor."
	 *
	 * He is right on both counts. The reference does do it, and on a phone it is
	 * a bad idea: 64px of padding on each side of a 358px bar is 128px of a
	 * 358px width, so the logo and the toggle crowd toward the centre and the bar
	 * stops reading as a full-width header. On a 1213px desktop bar the same 64px
	 * is barely a tenth of the width and it reads as intended.
	 *
	 * So this is a deliberate deviation from the reference, at Brad's call, on
	 * one breakpoint only — recorded rather than quietly applied, because Rule 2
	 * is that a difference from the reference must be a decision and not a drift.
	 *
	 * Breakpoint PARTS, not one value: these are emitted inline by render(), and
	 * an inline custom property outranks every media query in this file. That is
	 * D79, which has now bitten three times — the side margin, the marker size,
	 * and this.
	 */
	/*
	 * THE SCROLL SHIFT IS BACK ON BOTH BREAKPOINTS, behind a switch each.
	 *
	 * It was removed from mobile outright, which was the wrong move and Brad
	 * named why: "i should have followed my own rule and kept original how it
	 * was and used toggle to change features not strip them away." That IS the
	 * house rule — every alternate state a control produces is a designed
	 * layout, never the default minus something — and stripping it left no way
	 * back to the reference.
	 *
	 * So the reference behaviour is the default on both, and each breakpoint has
	 * its own switch. 64px of inset reads as intended on a 1213px desktop bar
	 * and as crowding on a 358px phone, so they are genuinely separate choices.
	 */
	--dfs-sm-bar-pad-top: 0px;
	/*
	 * @decision D96  PARTS, NEVER A RESOLVED VALUE — D79.
	 *
	 * PHP emits `-desktop`/`-tablet`/`-mobile` inline and the breakpoints below
	 * choose between them. A single inline `--dfs-sm-bar-pad-scrolled` would
	 * outrank every media query here and freeze the desktop inset onto the
	 * phone, which is the defect that recurred four times in one session.
	 * Mobile-first, matching how `--dfs-sm-margin` is done twenty lines up.
	 */
	--dfs-sm-bar-pad-scrolled-mobile: 20px;
	--dfs-sm-bar-pad-scrolled-tablet: 20px;
	--dfs-sm-bar-pad-scrolled-desktop: 36px;
	--dfs-sm-bar-pad-scrolled: var( --dfs-sm-bar-pad-scrolled-mobile );
	--dfs-sm-bar-pad-open: var( --dfs-sm-panel-pad );

	/*
	 * THE BAR IS A ROUNDED PLATE, 30px by default. The reference uses 0 — §6
	 * records it — so this is a house deviation, and it is only visible on the
	 * INSET bar, which is why both widths exist rather than one replacing the
	 * other.
	 */
	--dfs-sm-bar-radius: 30px;

	/* Luxury set. Every one is off unless its class is present, so an untouched
	 * widget gains no rules and the default render is unchanged. */
	--dfs-sm-reveal-duration: 0.9s;
	--dfs-sm-reveal-stagger: 0.08s;
	--dfs-sm-drift-duration: 24s;
	--dfs-sm-panel-shadow: 0 24px 60px rgba( 0, 0, 0, 0.35 );
	--dfs-sm-sibling-blur: 3px;
	--dfs-sm-grain-opacity: 0.05;
	--dfs-sm-caption-delay: 0.18s;
	--dfs-sm-bar-pad-inline: var( --dfs-sm-bar-pad-top );
	--dfs-sm-logo-height: 24px;     /* their `h-24` */
	--dfs-sm-logo-gap: 2px;         /* their `gap-2` */
	--dfs-sm-panel-pad: var( --dfs-sm-panel-pad-mobile );
	/* Their `gap-y-32` class is overridden; the grid COMPUTES to 16px on both
	 * axes at every width. Measured, not read off the class name. */
	--dfs-sm-panel-row-gap: 16px;

	/* Colour tokens. Defaults are the reference's dark theme, which is what
	 * the bar sits in over the hero. The JS interpolates between the top and
	 * scrolled values rather than swapping a class, because the measured
	 * background is a continuous ramp of 1,176 distinct values. */
	--dfs-sm-bar-fg: #EEEEEE;
	--dfs-sm-surface: #333333;
	--dfs-sm-brand: #FB460D;
	--dfs-sm-muted: #818081;

	/*
	 * THE BAR'S TWO STATES ARE DECLARED, NOT INTERPOLATED.
	 *
	 * `--dfs-sm-bar-bg` is transparent here and becomes `--dfs-sm-surface` under
	 * `[data-dfs-scrolled="1"]`; the 0.5s transition on the bar does the rest.
	 * The JS sets one boolean attribute and reads no colours at all, so every
	 * value lives in exactly one place and an Elementor control overriding a
	 * token is picked up with no script involvement.
	 *
	 * The theme blocks below then swap `--dfs-sm-surface` and the foreground
	 * together, which is the second transition the reference fires — measured
	 * running for 450ms with scrollY frozen, which is how it was established
	 * that neither of these is scroll-linked.
	 */
	/*
	 * @decision D97  THE "BACKGROUND (INACTIVE)" PICKER HAS TO REACH SOMETHING.
	 *
	 * This was `transparent`, hardcoded, while `style_vars()` faithfully emitted
	 * `--dfs-sm-bar-bg-top` from the control on the Style > Bar > Inactive tab.
	 * Nothing anywhere consumed it — grep for `bar-bg-top` found two writes and
	 * zero reads. So the colour picker had been inert since it shipped, which is
	 * `marker_spin` exactly, and the feature sweep is what found it: the token
	 * changed to magenta and the bar's computed background stayed
	 * `rgba(0, 0, 0, 0)`.
	 *
	 * The shipped default is `rgba(0, 0, 0, 0)`, so the resolved pixel is
	 * unchanged for anyone who has not touched the control — this makes the
	 * control work without moving the default. The scrolled rules below still
	 * override with `--dfs-sm-surface`, which is the ramp the reference does.
	 */
	--dfs-sm-bar-bg: var( --dfs-sm-bar-bg-top, transparent );

	/* Motion. Their token names kept so a value can be traced back. */
	--dfs-sm-ease-in-out: cubic-bezier( 0.4, 0, 0.2, 1 );
	--dfs-sm-ease-in-out-quart: cubic-bezier( 0.76, 0, 0.24, 1 );
	--dfs-sm-ease-in-out-quint: cubic-bezier( 0.83, 0, 0.17, 1 );
	--dfs-sm-ease-out-back: cubic-bezier( 0.34, 1.56, 0.64, 1 );
	--dfs-sm-ease-out: cubic-bezier( 0, 0, 0.2, 1 );

	/* @decision D77  fitted by search against the 22-sample envelope */
	--dfs-sm-expand-delay: 0.15s;
	--dfs-sm-expand-duration: 0.66s;
	--dfs-sm-expand-ease: var( --dfs-sm-ease-in-out-quart );

	/*
	 * THE CLOSE IS NOT THE OPEN REVERSED. It does not animate at all.
	 *
	 * Measured, per frame: the panel holds at its full 678px for 437ms and is
	 * at 0 by 520ms, with nothing in between on any rung. The open envelope
	 * over the same span produces twenty distinct intermediate heights, so the
	 * recorder is plainly capable of seeing a close tween — there is not one.
	 *
	 * What fills those 440ms is the nav links retracting into their masks. The
	 * panel waits for them and then simply stops existing.
	 *
	 * A symmetric close is the default assumption and it looks wrong in a way
	 * that is hard to name: the panel appears to sag shut after the content has
	 * already gone.
	 */
	--dfs-sm-collapse-delay: 0.44s;
	--dfs-sm-collapse-duration: 0.08s;
	--dfs-sm-roll-duration: 0.5s;
	--dfs-sm-label-duration: 0.3s;
	--dfs-sm-burger-duration: 0.25s;
	--dfs-sm-colour-duration: 0.3s;
	--dfs-sm-bar-duration: 0.5s;
	--dfs-sm-hide-duration: 0.4s;
	--dfs-sm-backdrop-duration: 0.5s;

	/*
	 * @decision D293 — how long the panel's CONTENT takes to leave, derived
	 * from how long the SURFACE takes to leave rather than chosen.
	 *
	 * 0.36 of the surface's own out duration is 180ms at the shipped 500ms —
	 * D211's measured constant, so the default state does not move — and it
	 * stays proportionally ahead of the surface at every setting of
	 * `full_out_duration`, whose range starts at 0ms. A constant cannot do
	 * that: set the fill to vanish in 60ms and any fixed content fade is
	 * outrun, which is the defect re-entering through a visible control.
	 */
	--dfs-sm-content-out: calc( var( --dfs-sm-full-out-duration, var( --dfs-sm-backdrop-duration ) ) * 0.36 );

	--dfs-sm-header-y: 0%;

	position: fixed;
	inset-inline: 0;
	top: 0;
	z-index: 9999;
	display: flex;
	flex-direction: column;
	gap: var( --dfs-sm-bar-gap );
	padding-top: calc(
		var( --dfs-sm-header-pad-top )
		+ var( --dfs-sm-sticky-offset, 0px )
		+ var( --dfs-sm-admin-bar-offset, 0px )
	);

	font-family: var( --dfs-sm-sans );
	color: var( --dfs-sm-bar-fg );

	transform: translateY( var( --dfs-sm-header-y ) );
	transition: transform var( --dfs-sm-hide-duration ) var( --dfs-sm-ease-in-out-quart );
}


/*
 * @decision D140 — THE WORDPRESS ADMIN BAR. Fixes A9.
 *
 * Brad: "works perfecly no admnin bar but with admin bar doesnt respect the
 * top padding ect and is way too close."
 *
 * WordPress pushes the page down for the admin bar with
 * `html { margin-top: 32px !important; }`. This header is `position: fixed;
 * top: 0`, and a fixed element resolves against the viewport, not against
 * `html` — so that margin cannot move it. The bar keeps sitting at y=0 with
 * the admin bar painted over its top padding, which is why the padding reads
 * as "not respected" rather than as a header in the wrong place.
 *
 * BRAD'S CONSTRAINT DECIDES THE SHAPE: "do not risk breaking non admin bar
 * view to fix admin bar." So the offset is a VARIABLE that defaults to 0px
 * and is only ever set under `body.admin-bar` — a class WordPress adds only
 * when it actually renders the bar. The logged-out path cannot reach these
 * rules; the selector does not match. `calc( ... + 0px )` is an identity, so
 * the default padding is arithmetically unchanged, and that was confirmed by
 * re-running all nine rungs and diffing the geometry rather than asserting it.
 *
 * It is a SEPARATE variable from `--dfs-sm-sticky-offset` on purpose. That one
 * belongs to D68's user-facing sticky-offset slider; if both wrote the same
 * property the slider and the admin bar would silently overwrite each other.
 * Composing them in the calc lets a user offset apply ON TOP of the admin bar
 * rather than instead of it.
 *
 * The three heights are WordPress's own breakpoints, not chosen numbers:
 * 32px above 782px, 46px at 782 and below, and at 600px and below the admin
 * bar switches to `position: absolute` and scrolls away — so the offset must
 * go back to zero there or the header would keep a permanent gap under
 * nothing once the page is scrolled.
 */
body.admin-bar .dfs-peptide-floating-header {
	--dfs-sm-admin-bar-offset: 32px;
}


@media screen and ( max-width: 782px ) {
	body.admin-bar .dfs-peptide-floating-header {
		--dfs-sm-admin-bar-offset: 46px;
	}
}


@media screen and ( max-width: 600px ) {
	body.admin-bar .dfs-peptide-floating-header {
		--dfs-sm-admin-bar-offset: 0px;
	}
}


/* Showing SPRINGS, hiding SLIDES FLAT — two different curves, which a single
 * averaged reading of the header had flattened into one "spring". Their rules
 * are `.4s ease-out-back` in and `.4s ease-in-out-quart` out. */
.dfs-peptide-floating-header[data-dfs-hidden="0"] {
	transition-timing-function: var( --dfs-sm-ease-out-back );
}


/*
 * STICKY OFF — the bar sits in the page and scrolls away with it.
 *
 * Its own section in Content rather than a Style switch, because this removes a
 * BEHAVIOUR: no fixed position, no background appearing on scroll, no hiding.
 * Every option that only makes sense while sticky is conditioned on it, so an
 * unsticky header does not present a panel of controls that cannot do anything
 * — a visible control must actually do something.
 *
 * The panel still opens. It is the navigation, not an enhancement.
 */
.dfs-peptide-floating-header--static {
	position: relative;
	transform: none;
	transition: none;
}


.dfs-peptide-floating-header--static .dfs-peptide-floating-header__backdrop { display: none; }


/*
 * @decision D169 — STICKY PER DEVICE. Each class parks the bar on ONE side of
 * the 1024 breakpoint (bar_shift's line) while the other keeps the full
 * sticky behaviour. `position: absolute`, NOT relative: by the time these
 * classes matter the portal (D71) has moved the root to the end of <body>,
 * where a relative bar renders at the foot of the page — absolute at top: 0
 * (which the base rule already sets) puts it over the page head and lets it
 * scroll away naturally. The `:not(--editor)` keeps the canvas on the
 * relative layout the editor needs; `.dfs-peptide-floating-header.` doubles specificity to
 * (0,3,0) so the hide-on-scroll transform (`[data-dfs-hidden]`, 0,2,0) can
 * never slide a parked bar.
 */
@media ( min-width: 1024px ) {
	.dfs-peptide-floating-header.dfs-peptide-floating-header--static-desktop:not( .dfs-peptide-floating-header--editor ) {
		position: absolute;
		transform: none;
		transition: none;
	}

	.dfs-peptide-floating-header--static-desktop .dfs-peptide-floating-header__backdrop { display: none; }
}

@media ( max-width: 1023px ) {
	.dfs-peptide-floating-header.dfs-peptide-floating-header--static-mobile:not( .dfs-peptide-floating-header--editor ) {
		position: absolute;
		transform: none;
		transition: none;
	}

	.dfs-peptide-floating-header--static-mobile .dfs-peptide-floating-header__backdrop { display: none; }
}


/*
 * THE TRAVELLING MARKER, OFF.
 *
 * The indent goes with it. The indent exists so the links make room for the
 * square — 32px of marker plus a matching gap — so links that still step right
 * for a marker that is not there read as broken alignment rather than as
 * emphasis. render() emits '--dfs-sm-nav-indent: 0px' in this state, and this
 * rule is the belt to that braces.
 */
.dfs-peptide-floating-header--no-marker .dfs-peptide-floating-header__nav-marker { display: none; }


.dfs-peptide-floating-header--no-marker .dfs-peptide-floating-header__nav a,
.dfs-peptide-floating-header--no-marker .dfs-peptide-floating-header__nav a:hover,
.dfs-peptide-floating-header--no-marker .dfs-peptide-floating-header__nav a[aria-current="page"] {
	--dfs-sm-nav-x: 0px;
}


/*
 * NO BUTTON — the bar becomes two columns and the menu takes an alignment.
 *
 * A designed two-column layout, not the three-column grid with an empty cell:
 * '1fr auto' with the menu justified where the control says, and the same
 * inset the logo has on the other side.
 */
@media ( min-width: 1024px ) {
	.dfs-peptide-floating-header--no-cta .dfs-peptide-floating-header__row { grid-template-columns: 1fr auto; }
	.dfs-peptide-floating-header--no-cta.dfs-peptide-floating-header--menu-start .dfs-peptide-floating-header__row { grid-template-columns: auto 1fr; }
	.dfs-peptide-floating-header--no-cta.dfs-peptide-floating-header--menu-start .dfs-peptide-floating-header__toggle { justify-self: start; }
	.dfs-peptide-floating-header--no-cta.dfs-peptide-floating-header--menu-center .dfs-peptide-floating-header__toggle { justify-self: center; }
	.dfs-peptide-floating-header--no-cta.dfs-peptide-floating-header--menu-end .dfs-peptide-floating-header__toggle { justify-self: end; }
}


/*
 * SWAPPED — menu right, button centre. Order alone would move the cells but
 * leave each one justified for the position it used to be in.
 */
@media ( min-width: 1024px ) {
	.dfs-peptide-floating-header--swapped .dfs-peptide-floating-header__toggle { order: 3; justify-self: end; }
	.dfs-peptide-floating-header--swapped .dfs-peptide-floating-header__cta    { order: 2; justify-self: center; }
}


/*
 * PANEL COLUMNS REMOVED.
 *
 * Each surviving combination is placed deliberately. Two columns centre as a
 * pair rather than clinging to columns 1 and 5 of a twelve-column grid; a lone
 * nav takes the alignment its control asks for. The house rule is that every
 * alternate state a control can produce is a designed layout, never the default
 * minus something.
 */
@media ( min-width: 1024px ) {
	.dfs-peptide-floating-header--two-col .dfs-peptide-floating-header__nav     { grid-column: 2 / span 4; }
	/*
	 * @decision D215 — WITH THE CARDS HIDDEN THE CONTACT COLUMN GROWS to the
	 * panel's edge (Brad, Mode 2 + rich lines: "the contact column doesn't
	 * grow any to fill area"). `7 / -1` instead of the old `span 3`, with a
	 * 40px right inset here and 20px below the breakpoint — the same "every
	 * alternate state is a designed layout" rule this block already states.
	 * The Width slider (Contact Column, shown when cards are off) writes a
	 * per-post `span N` that beats this default, so a hand always wins.
	 */
	.dfs-peptide-floating-header--two-col .dfs-peptide-floating-header__contact { grid-column: 7 / var( --dfs-sm-contact-end, -1 ); padding-right: 40px; }

	/*
	 * WITH NO CONTACT COLUMN the card row takes everything from column 6, which
	 * is what makes a third card fit. It is not "two cards, wider" — the row is
	 * re-placed and the repeater's cap moves from 2 to 3, so the extra column
	 * becomes a real slot rather than empty space.
	 */
	.dfs-peptide-floating-header--two-col .dfs-peptide-floating-header__cards   { grid-column: 6 / span 7; }

	.dfs-peptide-floating-header--nav-only .dfs-peptide-floating-header__nav { grid-column: 1 / span 12; }
	.dfs-peptide-floating-header--nav-only.dfs-peptide-floating-header--panel-center .dfs-peptide-floating-header__nav { align-items: center; }
	.dfs-peptide-floating-header--nav-only.dfs-peptide-floating-header--panel-end .dfs-peptide-floating-header__nav { align-items: flex-end; }
}

/* D215 — below the breakpoint the grown column keeps a 20px right inset. */
@media ( max-width: 1023px ) {
	.dfs-peptide-floating-header--two-col .dfs-peptide-floating-header__contact { padding-right: 20px; }
}


.dfs-peptide-floating-header--nav-only.dfs-peptide-floating-header--panel-center .dfs-peptide-floating-header__nav { align-items: center; }

.dfs-peptide-floating-header--nav-only.dfs-peptide-floating-header--panel-end .dfs-peptide-floating-header__nav { align-items: flex-end; }


/*
 * A centred or right-aligned nav must not also indent on hover — the indent
 * assumes a left edge to step away from, and against a centre alignment it
 * reads as the list jittering.
 */
.dfs-peptide-floating-header--nav-only.dfs-peptide-floating-header--panel-center .dfs-peptide-floating-header__nav a,
.dfs-peptide-floating-header--nav-only.dfs-peptide-floating-header--panel-end .dfs-peptide-floating-header__nav a,
.dfs-peptide-floating-header--nav-only.dfs-peptide-floating-header--panel-center .dfs-peptide-floating-header__nav a:hover,
.dfs-peptide-floating-header--nav-only.dfs-peptide-floating-header--panel-end .dfs-peptide-floating-header__nav a:hover {
	--dfs-sm-nav-x: 0px;
}


.dfs-peptide-floating-header--nav-only.dfs-peptide-floating-header--panel-center .dfs-peptide-floating-header__nav-marker,
.dfs-peptide-floating-header--nav-only.dfs-peptide-floating-header--panel-end .dfs-peptide-floating-header__nav-marker { display: none; }


/*
 * THEMES, NOT A RUNTIME SAMPLER.
 *
 * MEASURED-SPEC §3 records this correctly and it is worth restating where the
 * code is: the bar does not read the pixels underneath it. `--color-background`
 * on the reference resolves to three different values because there are three
 * scoped theme blocks, and the header carries a `data-theme` attribute that
 * selects one. The JS here decides WHICH theme by hit-testing the bar's
 * midline against the elements a selector names; the colours themselves are
 * entirely declarative, which is why an Elementor control can override them.
 *
 * Their token sets, copied exactly.
 */
.dfs-peptide-floating-header[data-dfs-theme="dark"] {
	--dfs-sm-bar-fg: #EEEEEE;
	--dfs-sm-surface: #333333;
	--dfs-sm-muted: #818081;
	--dfs-sm-brand: #FD551D;
	--dfs-sm-bar-bg-top: rgba( 51, 51, 51, 0 );
	--dfs-sm-bar-bg-end: #333333;
	--dfs-sm-bar-fg-top: #EEEEEE;
	--dfs-sm-bar-fg-end: #EEEEEE;
}


.dfs-peptide-floating-header[data-dfs-theme="light"] {
	--dfs-sm-bar-fg: #141314;
	--dfs-sm-surface: #D5D5D5;
	--dfs-sm-muted: #696869;
	--dfs-sm-brand: #FB460D;
	--dfs-sm-bar-bg-top: rgba( 213, 213, 213, 0 );
	--dfs-sm-bar-bg-end: #D5D5D5;
	--dfs-sm-bar-fg-top: #141314;
	--dfs-sm-bar-fg-end: #141314;
}


/*
 * @decision D258 — COMPLETE TRANSPARENCY. The bar never paints (render()
 * holds --dfs-sm-bar-surface at the top background, the D173 trick), and
 * while the midline crosses a LIGHT section the whole closed-bar set swaps
 * to the Over Light palette. Scoped on the BAR, not the root: the adapt
 * tokens arrive as root-inline vars, and a redefinition on the consumer's
 * nearer ancestor beats any root-inline control override without a single
 * !important. The OPEN state is excluded — bar and panel read as one sheet
 * in the menu's own configured colours (his tan open state stays his).
 * The logo swap needs no rule here: it rides the existing theme sync.
 *
 * SUPERSEDED BY @decision D297 below, which keeps every word of the above
 * true and replaces the six parallel token writes with ONE ink variable.
 */

/*
 * @decision D297 — ONE INK VARIABLE. Every colour the closed adaptive bar
 * wears — burger, nav text, contact ink, logo tint, CTA fill and CTA text —
 * derives from `--dfs-sm-ink` plus a CTA pair. Flipping the bar between a
 * dark section and a light one is now ONE redefinition instead of six
 * parallel ones, which is what makes both the close-state assertion (D293)
 * and the adaptive gate (D302) cheap: an instrument can ask "what is the
 * ink" rather than sampling six properties and hoping they agree.
 *
 * The six consumers still resolve through the SAME token names as before,
 * so nothing downstream of this block changed — the ink layer sits between
 * the control overrides and the consumers, not in place of either.
 *
 * @decision D298 — THE DROP-IN DEFAULTS. Adaptive mode ships white ink over
 * dark sections with a WHITE CTA carrying BLACK text — correct over any dark
 * hero with zero configuration, which is Brad's whole complaint about the
 * mode (BUG-4: "too much config, no proof it works — must drop in working").
 * Before this, the CTA over a dark hero wore `accent_colour` (the brand
 * orange #FB460D) with near-black text, so the untouched drop-in was a brand
 * chip rather than the neutral white pill the reference tier uses.
 *
 * This IS a deliberate default change, and it is scoped as narrowly as it
 * can be: it applies ONLY when `bar_visibility: adaptive`, which no saved
 * instance on struttcentral uses (verified in `_elementor_data` — the key is
 * absent everywhere, so every instance resolves to 'scroll'). The three
 * `adapt_*_dark` controls restore the previous look in three clicks.
 */
.dfs-peptide-floating-header--adaptive:not( .is-open ) .dfs-peptide-floating-header__bar {
	--dfs-sm-ink:          var( --dfs-sm-adapt-ink-dark, #FFFFFF );
	--dfs-sm-ink-cta-fill: var( --dfs-sm-adapt-cta-fill-dark, #FFFFFF );
	--dfs-sm-ink-cta-fg:   var( --dfs-sm-adapt-cta-text-dark, #000000 );

	/*
	 * @decision D422 — THE HOVER PAIR IS ADAPTIVE TOO.
	 *
	 * Before this, `--dfs-sm-cta-fill-hover` was written ONCE, inline on the
	 * root, from the global `cta_fill_hover` control — so the button's resting
	 * colours swapped with the section underneath and its HOVER colours did not.
	 * MEASURED on clarityfilms 2026-08-29, both themes, identical:
	 *   --dfs-sm-adapt-cta-fill / -dark  #C1683D  (adaptive, correct)
	 *   --dfs-sm-cta-fill-hover          #0B1017  (global, same in both)
	 * Brad: "it doesnt seem to have a hover slider so i cant keep it same
	 * colour during hover its going #0D111F."
	 *
	 * Redefining the token HERE, on the bar, beats the inline root value for
	 * every descendant of the bar — the CTA included — without `!important`.
	 * That is the same mechanism `--dfs-sm-cta-fill` two lines below already
	 * relies on, not a new trick.
	 *
	 * PHP always emits these two, falling back to the global hover control when
	 * the new adaptive controls are blank, so an untouched widget renders
	 * byte-identically to 4.67.2. The literals here are the stylesheet's own
	 * last resort for markup rendered before the controls existed.
	 */
	--dfs-sm-ink-cta-fill-hover: var( --dfs-sm-adapt-cta-fill-hover-dark, #0B1017 );
	--dfs-sm-ink-cta-fg-hover:   var( --dfs-sm-adapt-cta-text-hover-dark, #FFFFFF );

	/*
	 * The ink's hover. Falls back to the ink itself, so a blank control means
	 * 'do not change colour under the cursor' rather than 'jump to whatever
	 * the stylesheet happened to hard-code'.
	 */
	--dfs-sm-ink-hover: var( --dfs-sm-adapt-ink-hover-dark, var( --dfs-sm-ink ) );

	--dfs-sm-bar-fg:          var( --dfs-sm-ink );
	--dfs-sm-bar-fg-top:      var( --dfs-sm-ink );
	--dfs-sm-bar-fg-scrolled: var( --dfs-sm-ink );
	--dfs-sm-toggle-fg:       var( --dfs-sm-ink );
	/*
	 * css:199 hard-codes this to #FFFFFF and css:1296 applies it !important,
	 * so over a light section the burger rested dark and hovered WHITE.
	 * Redefining the token on the bar beats the root value for every
	 * descendant without needing !important of its own — the same mechanism
	 * --dfs-sm-cta-fill below already relies on.
	 */
	--dfs-sm-toggle-fg-hover: var( --dfs-sm-ink-hover );
	--dfs-sm-contact-fg:      var( --dfs-sm-ink );
	--dfs-sm-cta-fill:        var( --dfs-sm-ink-cta-fill );
	--dfs-sm-cta-fg:          var( --dfs-sm-ink-cta-fg );
	--dfs-sm-cta-fill-hover:  var( --dfs-sm-ink-cta-fill-hover );
	--dfs-sm-cta-fg-hover:    var( --dfs-sm-ink-cta-fg-hover );
}


/*
 * The light side redefines ONLY the ink — the seven consumer lines above are
 * inherited from the block they sit inside, so a future consumer is added in
 * one place rather than two. D258's three controls survive verbatim as this
 * side's overrides, with their original defaults, so no instance configured
 * against D258 moves by a single hex digit.
 */
.dfs-peptide-floating-header--adaptive[data-dfs-theme="light"]:not( .is-open ) .dfs-peptide-floating-header__bar {
	--dfs-sm-ink:          var( --dfs-sm-adapt-fg, #524739 );
	--dfs-sm-ink-cta-fill: var( --dfs-sm-adapt-cta-fill, #0B1017 );
	--dfs-sm-ink-cta-fg:   var( --dfs-sm-adapt-cta-text, #FFFFFF );

	/* D422 — the light side's hover pair. The seven consumer lines in the
	 * block above are inherited, exactly as the comment there describes, so
	 * only the ink is redefined here. */
	--dfs-sm-ink-cta-fill-hover: var( --dfs-sm-adapt-cta-fill-hover, #0B1017 );
	--dfs-sm-ink-cta-fg-hover:   var( --dfs-sm-adapt-cta-text-hover, #FFFFFF );

	/* the light side's ink hover; the consumer lines are inherited from the
	   block above, exactly as that block's comment describes */
	--dfs-sm-ink-hover: var( --dfs-sm-adapt-ink-hover, var( --dfs-sm-ink ) );
}


/*
 * THE SOCIAL ROW IN THE CONTACT COLUMN.
 *
 * Colour is inherited rather than declared. The icons live inside the
 * contact column, which already has contact_colour, contact_hover_colour and
 * the adaptive over-dark / over-light ink; a hardcoded colour here would put
 * them outside all of it and they would be the one thing in the panel that
 * ignored the theme.
 */
/*
 * ANCHORED INSIDE .dfs-peptide-floating-header__contact-group ON PURPOSE, at (0,3,0).
 *
 * The social list renders inside a contact group, so it was picking up the
 * RICH-TEXT list styling that lives there (@decision D210):
 *   .dfs-peptide-floating-header__contact-group ul, …   padding-left: 1.1em   (0,2,0)
 *   .dfs-peptide-floating-header__contact-line, …       display: block        (0,2,0)
 * both of which beat the (0,1,0) rules these replace. MEASURED: every other
 * left edge in the column sat at 841px and the icons started at 858 — a 17px
 * prose indent on a row of icons.
 *
 * Beaten by specificity rather than !important, so the rich lists keep the
 * indent they are supposed to have and nothing needs a second exception later.
 */
.dfs-peptide-floating-header__contact-group ul.dfs-peptide-floating-header__contact-social {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	/* flush left with the lines above by default — Brad's "aligned left to
	   match the column content above" */
	justify-content: var( --dfs-sm-social-just, flex-start );
	gap: var( --dfs-sm-social-gap, 18px );
	margin: var( --dfs-sm-social-top, 0px ) 0 0;
	padding: 0;
	list-style: none;
}

.dfs-peptide-floating-header__contact-social-item {
	display: block;
	margin: 0;
}

.dfs-peptide-floating-header__contact-group a.dfs-peptide-floating-header__contact-social-link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/*
	 * Blank ink keeps the inherited colour, which is the point: the icons take the contact
	 * column's colour and its adaptive over-dark / over-light treatment with it.
	 * A control only overrides that when the author sets one.
	 */
	color: var( --dfs-sm-social-ink, inherit );
	background-color: var( --dfs-sm-social-bg, transparent );
	padding: var( --dfs-sm-social-pad, 0px );
	border-radius: var( --dfs-sm-social-radius, 0px );
	font-size: var( --dfs-sm-social-size, 22px );
	line-height: 1;
	text-decoration: none;
	transition: color 0.3s ease, background-color 0.3s ease;
}

.dfs-peptide-floating-header__contact-group a.dfs-peptide-floating-header__contact-social-link:hover,
.dfs-peptide-floating-header__contact-group a.dfs-peptide-floating-header__contact-social-link:focus-visible {
	/* falls back to the resting value, so a hover colour is optional */
	color: var( --dfs-sm-social-ink-hover, var( --dfs-sm-social-ink, inherit ) );
	background-color: var( --dfs-sm-social-bg-hover, var( --dfs-sm-social-bg, transparent ) );
}

/*
 * An uploaded SVG is rendered inline by Elementor and would otherwise paint
 * its own authored colour, so it is told to take the menu's ink like the
 * font icons already do.
 */
.dfs-peptide-floating-header__contact-social-link svg {
	width: 1em;
	height: 1em;
	fill: currentColor;
}

/*
 * NAV LINKS, THE CONTACT LINE AND THE MARK.
 *
 * Unlike the burger these have no hover token to redefine — the global
 * a:hover rule (css:1414-1432) sets background, border and text-decoration
 * and never touches color, so they simply held their resting ink. This gives
 * them the hover ink, which with a blank control IS the resting ink, so
 * nothing moves until somebody fills the control in.
 *
 * The CTA is excluded: it has had its own adaptive hover pair since D422 and
 * must not be overpainted by the menu ink.
 */
.dfs-peptide-floating-header--adaptive.dfs-peptide-floating-header--ink-hover:not( .is-open ) .dfs-peptide-floating-header__bar a:hover:not( .dfs-peptide-floating-header__cta ):not( .dfs-peptide-floating-header__panel-cta ),
.dfs-peptide-floating-header--adaptive.dfs-peptide-floating-header--ink-hover:not( .is-open ) .dfs-peptide-floating-header__bar a:focus-visible:not( .dfs-peptide-floating-header__cta ):not( .dfs-peptide-floating-header__panel-cta ) {
	color: var( --dfs-sm-ink-hover );
}

/*
 * THE OPEN STATE'S HOVER TEXT.
 *
 * Five classes, (0,5,0), because Elementor emits cta_text_open at (0,4,0)
 * — `.dfs-peptide-floating-header.dfs-peptide-floating-header--ID.is-open .dfs-peptide-floating-header__cta-label` — and
 * the generic hover rule is only (0,3,0), so the RESTING colour was
 * outranking the hover one. MEASURED: hover text stayed rgb(20,19,20) with
 * the hover control set to #FF0000.
 *
 * The fallback chain is the important half: the open hover colour, then the
 * open RESTING colour, then the global hover. That middle link is what keeps
 * today's behaviour for anyone who set an open text colour and no hover.
 */
.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__cta:hover .dfs-peptide-floating-header__cta-label,
.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__cta:hover .dfs-peptide-floating-header__cta-chip {
	color: var( --dfs-sm-cta-fg-hover-open, var( --dfs-sm-cta-fg-open, var( --dfs-sm-cta-fg-hover ) ) );
}

/*
 * WHAT SHOWS THROUGH THE GAP.
 *
 * A strip sitting exactly in the space the shortened backdrop leaves, so a
 * tint or a blur can be applied to the revealed page without touching the
 * menu above it. Inert at the default: zero height when there is no gap,
 * and transparent with no blur unless one is chosen.
 */
.dfs-peptide-floating-header__backdrop::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	top: 100%;
	height: var( --dfs-sm-panel-gap, 0px );
	background-color: var( --dfs-sm-gap-tint, transparent );
	backdrop-filter: blur( var( --dfs-sm-gap-blur, 0px ) );
	-webkit-backdrop-filter: blur( var( --dfs-sm-gap-blur, 0px ) );
	pointer-events: none;
}

/*
 * THE COLUMNS THAT ARE NOT THE NAV.
 *
 * The END is extended to the grid's last line and the content justified
 * inside it. Extending the end rather than moving the start keeps 'left'
 * exactly where the column already sits, and holds whatever span the nav
 * has — MEASURED live at 6 columns while this stylesheet's own base rule
 * says 4, so a hard-coded start column would be wrong on one of them.
 */
/*
 * ALIGNING THE OTHER COLUMNS — driven by custom properties, not by a class.
 *
 * It used to be a .dfs-peptide-floating-header--extra-VALUE class written into the markup by
 * render(). A class is one value for every width BY CONSTRUCTION, so the
 * alignment could be set on desktop and never turned off on a phone — Brad:
 * "on mobile it cantbe disabeld though so aligns thecontent right".
 *
 * The control now emits these two properties through its selectors, so
 * Elementor writes them per breakpoint and the site's own breakpoints decide.
 *
 * BOTH FALLBACKS ARE THE UNALIGNED LAYOUT, so a menu whose control was never
 * touched renders exactly as before: no column-end override, no justify-self.
 */
.dfs-peptide-floating-header__contact,
.dfs-peptide-floating-header__cards {
	grid-column-end: var( --dfs-sm-extra-col, auto );
	justify-self: var( --dfs-sm-extra-just, stretch );
}

/*
 * @decision D299 — THE BLEND VARIANT IS NOT BUILT, AND THE REASON IS
 * MEASURED. No CSS lives here on purpose; this comment is the finding.
 *
 * `mix-blend-mode: difference` on white ink would invert per-pixel against
 * whatever is beneath — mid-scroll-perfect, zero configuration, no classifier
 * at all. It was built, and it did not work, and the way it failed is worth
 * keeping because it will look tempting again.
 *
 * `mix-blend-mode` composites against the backdrop WITHIN the element's own
 * stacking context, and `.dfs-peptide-floating-header` creates one three times over: the
 * hide-on-scroll `transform`, `z-index: 9999`, and the `position: fixed`
 * beside that z-index (the last of those also arrives from the D71 portal
 * shim that render() emits inline, which outranks any rule in this file). So
 * the ink blended against the header's own empty backdrop and painted
 * NOTHING.
 *
 * MEASURED, NOT REASONED. `_probe-blend.mjs` screenshots the bar over the
 * image section with the variant on and off and byte-compares the crops: the
 * first build produced IDENTICAL PIXELS — 122,022 bytes both ways — while
 * `getComputedStyle(...).mixBlendMode` returned 'difference' and gate #9's
 * row went green. That is an instrument agreeing with itself: the property is
 * set whether or not it does anything, so asserting the property asserts
 * nothing at all. Exactly the class of near-miss this house has paid for
 * before.
 *
 * WHY IT WAS CUT RATHER THAN FORCED. Unwinding the header's stacking context
 * is not a tweak: `.dfs-peptide-floating-header__backdrop` sits at `z-index: -1` and the
 * comment above it states plainly that it relies on the header being its own
 * context to stay under the bar while remaining above the page. Remove the
 * context and the full-screen backdrop drops BEHIND page content in every
 * full-screen mode — trading a curated variant for a broken flagship. Making
 * blend work properly means re-founding that arrangement (a non-isolating
 * header, the backdrop re-parented, hide-on-scroll driven by something other
 * than a transform) and that is its own cut with its own gates.
 *
 * The control was removed with the CSS. A visible control that does nothing
 * is the one thing this widget is not allowed to ship.
 */


/*
 * @decision D300 — A LOGO WITHOUT A SWITCH PAIR.
 *
 * "Logo Colour: Adapt automatically" renders ONE mark and recolours it, so
 * the second-logo field stops being a requirement for a header that inverts.
 * Two paths, because a logo arrives as one of two things:
 *
 *   - an SVG, which `inline_svg_mark()` already inlines for the D126 draw-in.
 *     Inlined, `fill: currentColor` makes it take the ink for free — which is
 *     exactly how the reference gets its own mark's theme-following.
 *   - a bitmap, which cannot be recoloured. `brightness(0) invert(1)` forces
 *     it to pure white for the dark side; the light side drops the invert so
 *     it forces to pure black. Both are single-composite filters, no repaint
 *     cost, and both are honest about what they do: a bitmap logo becomes a
 *     SILHOUETTE. That is stated in the panel, and it is why "keep my logo's
 *     own colours" exists.
 *
 * The pair path is untouched and remains the DEFAULT, because flipping the
 * default would silently re-render the logo of every saved menu — and the
 * shipped pair (bundled off-white + brown marks) already inverts correctly,
 * so the untouched drop-in was never the broken case.
 */
.dfs-peptide-floating-header--logo-ink .dfs-peptide-floating-header__logo-mark svg,
.dfs-peptide-floating-header--logo-ink svg.dfs-peptide-floating-header__logo-mark {
	fill: currentColor;
	color: var( --dfs-sm-ink, var( --dfs-sm-bar-fg ) );
}


/*
 * @decision D427 — THE LOGO GETS ITS OWN COLOUR WHILE THE PANEL IS OPEN.
 *
 * Brad, 2026-08-29: "DFS Core Menu has a way to set logo to swap, but on core
 * menu open it needs to be always the dark brown logo and its staying white
 * one, no setting to configure that."
 *
 * MEASURED on clarityfilms, logo SVG `color`:
 *     closed  rgb(255,255,255)
 *     open    rgb(238,238,238)   <- #EEEEEE, still white, over a #D8D3C2 panel
 *
 * WHY IT GOES WHITE, and it is not the logo swap failing. `data-dfs-logo` does
 * flip 0 -> 1 on open, so syncLogo() is working. The colour is the problem: the
 * rule above reads `--dfs-sm-ink`, and the block that SETS that token is scoped
 * `.dfs-peptide-floating-header--adaptive:not( .is-open )`. The moment the panel opens that
 * block stops matching, `--dfs-sm-ink` is unset, and the chain falls through to
 * `--dfs-sm-bar-fg` — the bar's own light ink, on a light panel.
 *
 * The burger already had `toggle_colour_open` for exactly this situation. The
 * logo had nothing; grepped every `_open` control on the widget to confirm
 * before adding one.
 *
 * INERT WHEN BLANK. The fallback chain here is the SAME chain the rule above
 * uses, so an empty control renders byte-identically to 4.68.0. Only a value
 * changes anything.
 */
.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__logo-mark svg,
.dfs-peptide-floating-header.is-open svg.dfs-peptide-floating-header__logo-mark,
.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__logo svg {
	color: var( --dfs-sm-logo-open, var( --dfs-sm-ink, var( --dfs-sm-bar-fg ) ) );
	fill: currentColor;
}

.dfs-peptide-floating-header--logo-ink svg.dfs-peptide-floating-header__logo-mark path,
.dfs-peptide-floating-header--logo-ink svg.dfs-peptide-floating-header__logo-mark polygon,
.dfs-peptide-floating-header--logo-ink svg.dfs-peptide-floating-header__logo-mark rect,
.dfs-peptide-floating-header--logo-ink svg.dfs-peptide-floating-header__logo-mark circle {
	fill: currentColor;
}

/* The bitmap path. `filter` cannot read a custom property, so the two states
 * are two declarations rather than one parameterised rule. */
.dfs-peptide-floating-header--logo-ink img.dfs-peptide-floating-header__logo-mark {
	filter: brightness( 0 ) invert( 1 );
	transition: filter var( --dfs-sm-roll-duration ) var( --dfs-sm-ease-out );
}

.dfs-peptide-floating-header--logo-ink[data-dfs-theme="light"] img.dfs-peptide-floating-header__logo-mark {
	filter: brightness( 0 );
}


/*
 * NAV LINKS RISE STAGGERED, and the stagger index is emitted per item by
 * render() as `--dfs-sm-i`. The delay is deliberately built on the same
 * `--dfs-sm-expand-delay` the panel uses, so the first link starts as the
 * panel finishes its own delay rather than at an unrelated constant that
 * would drift the moment the expand timing is tuned.
 */
.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__nav a,
.dfs-peptide-floating-header--editor.is-preview-open .dfs-peptide-floating-header__nav a {
	transition-delay: calc( var( --dfs-sm-expand-delay ) + var( --dfs-sm-i, 0 ) * 0.06s );
}


/* Closing drops them all at once — a staggered exit reads as the panel
 * struggling to close rather than as choreography. */
.dfs-peptide-floating-header__nav a { transition-delay: 0s; }


@media ( min-width: 768px ) {
	.dfs-peptide-floating-header {
		--dfs-sm-margin: var( --dfs-sm-margin-tablet );
		--dfs-sm-bar-pad-scrolled: var( --dfs-sm-bar-pad-scrolled-tablet );
	}
}


@media ( min-width: 1024px ) {
	.dfs-peptide-floating-header {
		--dfs-sm-marker-size: var( --dfs-sm-marker-size-desktop );
		/*
		 * A HARDCODED `--dfs-sm-nav-indent: 64px` USED TO SIT AT THE END OF THIS
		 * BLOCK, one line after the variable it overrode, so the Indent control
		 * was inert on every desktop viewport — it wrote
		 * `--dfs-sm-nav-indent-desktop` faithfully and this threw it away. Same
		 * bug class as D79 and the same symptom: the control looks right in the
		 * panel and measures the shipped value on the page. 64 was the shipped
		 * default anyway, which is exactly why nobody saw it.
		 */
		--dfs-sm-nav-indent: var( --dfs-sm-nav-indent-desktop );
		--dfs-sm-margin: var( --dfs-sm-margin-desktop );
		--dfs-sm-panel-pad: var( --dfs-sm-panel-pad-desktop );
		--dfs-sm-bar-pad-scrolled: var( --dfs-sm-bar-pad-scrolled-desktop );
	}
}


/*
 * THE BAR'S SCROLLED AND OPEN STATES.
 *
 * Three padding values, measured off their bar's own inline style at three
 * moments: 0 at the top of the page, 16 once scrolled, 64 while the panel is
 * open. The last one is not decoration — 64 is the panel's own padding, so the
 * logo lines up with the nav column beneath it the moment the menu opens, and
 * drifts 48px out of alignment if the state is missed.
 */
.dfs-peptide-floating-header[data-dfs-scrolled="1"] {
	/*
	 * @decision D166 — the scrolled bar has its OWN colour token, falling
	 * back to the menu surface so a blank control keeps today's render.
	 * Note `.is-open` below stays on the surface directly: while the menu is
	 * up, bar and panel must read as one sheet, whatever the closed bar wears.
	 */
	--dfs-sm-bar-bg: var( --dfs-sm-bar-surface, var( --dfs-sm-surface ) );
	--dfs-sm-bar-fg: var( --dfs-sm-bar-fg-scrolled );
	--dfs-sm-bar-pad-inline: var( --dfs-sm-bar-pad-scrolled );
}


.dfs-peptide-floating-header.is-open {
	--dfs-sm-bar-bg: var( --dfs-sm-surface );
	--dfs-sm-bar-pad-inline: var( --dfs-sm-bar-pad-open );
}


/*
 * THE SHIFT, SWITCHED OFF — per breakpoint.
 *
 * Pinned back to the REST value rather than the declaration being removed: the
 * state rules above still exist, so something has to outrank them at the same
 * specificity. A wrapper class plus the state selector does that without
 * reaching for !important.
 */
/*
 * @decision D149 — THE TOGGLE DISABLES THE SCROLL TRIGGER ONLY.
 *
 * `--dfs-sm-bar-pad-inline` has TWO triggers, and they are not the same
 * feature wearing one name. Scrolling draws the content in as the background
 * arrives; OPENING pins the logo to the panel's own padding so it lines up
 * with the nav column beneath it. The open value is `--dfs-sm-bar-pad-open`,
 * which IS `--dfs-sm-panel-pad` — that alignment is structural, not decorative,
 * and the bar drifts 48px out of register the moment it is skipped.
 *
 * So "Draw Content In On Scroll = off" now suppresses the SCROLLED rule and
 * leaves the OPEN rule standing. Previously both selectors were listed
 * together and the toggle killed the alignment too.
 *
 * The open rule is restated here rather than left to `.dfs-peptide-floating-header.is-open`
 * above, because specificity makes that insufficient: while the menu is open
 * AND the page is scrolled both states match, and
 * `.dfs-peptide-floating-header--no-shift-desktop[data-dfs-scrolled="1"]` is (0,2,0) sitting
 * LATER in document order than `.dfs-peptide-floating-header.is-open` at the same (0,2,0).
 * It would win the tie and pin the padding flat. Restating it at (0,2,0) after
 * the scrolled rule is what puts the open state back on top.
 */
@media ( min-width: 1024px ) {
	.dfs-peptide-floating-header--no-shift-desktop[data-dfs-scrolled="1"] {
		--dfs-sm-bar-pad-inline: var( --dfs-sm-bar-pad-top );
	}

	.dfs-peptide-floating-header--no-shift-desktop.is-open {
		--dfs-sm-bar-pad-inline: var( --dfs-sm-bar-pad-open );
	}
}


@media ( max-width: 1023px ) {
	.dfs-peptide-floating-header--no-shift-mobile[data-dfs-scrolled="1"] {
		--dfs-sm-bar-pad-inline: var( --dfs-sm-bar-pad-top );
	}

	.dfs-peptide-floating-header--no-shift-mobile.is-open {
		--dfs-sm-bar-pad-inline: var( --dfs-sm-bar-pad-open );
	}
}


/* Their `.grid-container`, verbatim. Both the bar and the panel are one, which
 * is the whole of the "inset" the spec described as `left: 25px` — that was
 * the resolved centring offset at one viewport, not a declared inset. */
.dfs-peptide-floating-header__bar-wrap,
.dfs-peptide-floating-header__panel-wrap {
	width: calc( 100% - var( --dfs-sm-margin ) * 2 );
	max-width: var( --dfs-sm-max-width );
	margin-inline: auto;
}


/*
 * FULL WIDTH — the background reaches both edges, the content still sits at
 * the margin. An ALTERNATIVE to the inset bar, not a replacement for it.
 *
 * Brad, after I had twice swapped one behaviour for the other: "i should have
 * followed my own rule and kept original how it was and used toggle to change
 * features not strip them away." That is the house rule already written down —
 * every alternate state a control can produce is a DESIGNED layout, never the
 * default minus something — and both of my previous passes broke it, once by
 * removing the scroll shift and once by removing the inset.
 *
 * So both exist. The default is the reference: inset, which is also the only
 * one where a corner radius is visible.
 */
.dfs-peptide-floating-header--bar-full .dfs-peptide-floating-header__bar-wrap,
.dfs-peptide-floating-header--bar-full .dfs-peptide-floating-header__panel-wrap {
	width: 100%;
	max-width: none;
}


.dfs-peptide-floating-header--bar-full .dfs-peptide-floating-header__bar { border-radius: 0; }


.dfs-peptide-floating-header--bar-full .dfs-peptide-floating-header__bar,
.dfs-peptide-floating-header--bar-full .dfs-peptide-floating-header__panel-inner {
	padding-inline: var( --dfs-sm-margin );
}


/* With a full-bleed bar the scroll shift is measured FROM the margin, not from
 * zero, or the content would jump to the screen edge as the background arrives. */
.dfs-peptide-floating-header--bar-full[data-dfs-scrolled="1"] .dfs-peptide-floating-header__bar {
	padding-inline: calc( var( --dfs-sm-margin ) + var( --dfs-sm-bar-pad-scrolled ) );
}


.dfs-peptide-floating-header--bar-full.is-open .dfs-peptide-floating-header__bar {
	padding-inline: calc( var( --dfs-sm-margin ) + var( --dfs-sm-panel-pad ) );
}


/*
 * @decision D149, the full-bleed leg. Same two triggers, same correction.
 *
 * This one could NOT be left to the generic `.is-open` rule even in principle:
 * the suppression selector here is (0,4,0) and
 * `.dfs-peptide-floating-header--bar-full.is-open .dfs-peptide-floating-header__bar` is (0,3,0), so it loses
 * on specificity rather than on order and no amount of moving it would help.
 * The open value is restated at matching (0,4,0).
 */
.dfs-peptide-floating-header--bar-full.dfs-peptide-floating-header--no-shift-desktop[data-dfs-scrolled="1"] .dfs-peptide-floating-header__bar {
	padding-inline: var( --dfs-sm-margin );
}


.dfs-peptide-floating-header--bar-full.dfs-peptide-floating-header--no-shift-desktop.is-open .dfs-peptide-floating-header__bar {
	padding-inline: calc( var( --dfs-sm-margin ) + var( --dfs-sm-panel-pad ) );
}


@media ( max-width: 1023px ) {
	.dfs-peptide-floating-header--bar-full.dfs-peptide-floating-header--no-shift-mobile[data-dfs-scrolled="1"] .dfs-peptide-floating-header__bar {
		padding-inline: var( --dfs-sm-margin );
	}

	.dfs-peptide-floating-header--bar-full.dfs-peptide-floating-header--no-shift-mobile.is-open .dfs-peptide-floating-header__bar {
		padding-inline: calc( var( --dfs-sm-margin ) + var( --dfs-sm-panel-pad ) );
	}
}


.dfs-peptide-floating-header__bar {
	border-radius: var( --dfs-sm-bar-radius );
	padding-block: var( --dfs-sm-bar-pad-block );
	padding-inline: var( --dfs-sm-bar-pad-inline );
	background-color: var( --dfs-sm-bar-bg );
	color: var( --dfs-sm-bar-fg );
	transition:
		padding var( --dfs-sm-bar-duration ) var( --dfs-sm-ease-out ),
		background-color var( --dfs-sm-bar-duration ) var( --dfs-sm-ease-out ),
		color var( --dfs-sm-bar-duration ) var( --dfs-sm-ease-out );
}


.dfs-peptide-floating-header__row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	align-items: center;
}


@media ( min-width: 1024px ) {
	.dfs-peptide-floating-header__row { grid-template-columns: 1fr auto 1fr; }
}


/* --------------------------------------------------------------- the logo */

/*
 * A 24px WINDOW WITH BOTH MARKS PERMANENTLY INSIDE IT.
 *
 * Not a swap, not a crossfade: the two marks are stacked in a column with a
 * 2px gap and the whole track slides 26px, so the second mark arrives as the
 * first leaves. Both are always in the DOM, which is why the reference shows
 * 205 interpolated states on an element that looks like a toggle.
 *
 * `overflow: hidden` on the anchor is what makes it a window. Without it the
 * inactive mark is simply visible below the active one.
 */
.dfs-peptide-floating-header__logo {
	position: relative;
	color: inherit;
	display: block;
	height: var( --dfs-sm-logo-height );
	flex-shrink: 0;
	overflow: hidden;
	justify-self: start;
	line-height: 0;
}


.dfs-peptide-floating-header__logo-track {
	display: flex;
	flex-direction: column;
	gap: var( --dfs-sm-logo-gap );
	transform: translateY( 0 );
	transition: transform var( --dfs-sm-roll-duration ) var( --dfs-sm-ease-out );
}


/*
 * THE LOGO FOLLOWS WHAT IS UNDERNEATH IT, not just whether the menu is open.
 *
 * Brad asked whether the header can detect the content below and pick the white
 * or dark mark. It can, and the machinery was already here — the section-aware
 * theme that swaps the bar's own colours knows exactly this. The logo was the
 * one thing in the bar still keyed to the open state instead.
 *
 * The reference gets this for free because its mark is an inline SVG filled
 * with 'var(--color-foreground)', so it simply inherits the theme. Ours are two
 * bitmaps, which cannot be recoloured — but they can be rolled between, and the
 * roll is already built. So the same signal drives it.
 *
 * The rule is now "match the foreground of whatever you are sitting on":
 * over a dark section the off-white mark, over a light section the brown one.
 * That covers the open panel too, because an open panel paints the surface
 * colour of the current theme.
 *
 * 'data-dfs-logo' rather than reading the theme directly, so the swap-on-open
 * mode remains expressible: the JS decides which signal feeds the attribute.
 */
.dfs-peptide-floating-header[data-dfs-logo="1"] .dfs-peptide-floating-header__logo-track {
	transform: translateY( calc( -1 * ( var( --dfs-sm-logo-height ) + var( --dfs-sm-logo-gap ) ) ) );
}


/*
 * D300 — THE ROLL IS NEUTRALISED WHEN THERE IS ONLY ONE MARK.
 *
 * `syncLogo()` sets `data-dfs-logo="1"` from the theme, which is correct for
 * the pair and catastrophic for a single mark: the track would slide the only
 * logo on the page out of its own window and the bar would show an empty
 * space. Fixed HERE rather than by teaching the JS about the mode, because a
 * rule cannot be defeated by a future caller setting the attribute for its
 * own reasons — and the attribute is still wanted, since the light/dark half
 * of it is exactly what the bitmap filter above keys on.
 */
.dfs-peptide-floating-header--logo-ink .dfs-peptide-floating-header__logo-track {
	transform: none !important;
}


/*
 * !important, AND THE REASON IS MEASURED.
 *
 * On the live site this rule was losing: the token read 24px, the window
 * measured 24px, and the <img> computed to 424x100 — its intrinsic size. The
 * height was resolving to 'auto', so a theme-level 'img { height: auto }'
 * reset was winning, and the bar showed the top 24px of a 100px logo: an arc
 * and a row of dashes.
 *
 * Every theme ships that reset — it is in the WordPress block library, in
 * Hello Elementor and in most optimiser plugins — and it is written to apply to
 * every image on the site. This is the same shield as the button reset, for the
 * same reason: a logo rendering at four times its box is broken, not a styling
 * preference, and the value being forced is still our own token.
 */
.dfs-peptide-floating-header__logo-mark {
	display: block;
	height: var( --dfs-sm-logo-height ) !important;
	width: auto !important;
	max-width: none !important;
	flex-shrink: 0;
}


/* ------------------------------------------------------------- the toggle */

.dfs-peptide-floating-header__toggle {
	display: flex;
	align-items: center;
	gap: 8px;
	justify-self: end;
	margin: 0;
	padding: 0;
	background: none;
	border: 0;
	cursor: pointer;
	color: inherit;
	font-family: var( --dfs-sm-mono );
	font-size: var( --dfs-sm-label-size );
	font-weight: 400;
	line-height: 1.5;
	letter-spacing: var( --dfs-sm-label-tracking );
	text-transform: uppercase;
	transition: opacity var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out );
}


/*
 * THE TOGGLE HOVERS TO A COLOUR, not to a washed-out grey.
 *
 * The reference uses 'opacity: 0.7', which over a dark bar reads as the control
 * switching OFF rather than responding — Brad: "middle menu hover is a grey".
 * Their CTA hovers to white, so the two now match and the bar reads as one set
 * of controls. Both the colour and the opacity are configurable; setting the
 * opacity back to 0.7 restores the reference exactly.
 */
.dfs-peptide-floating-header__toggle { color: var( --dfs-sm-toggle-fg ); }


/*
 * 5.207.0-co66 — :hover only where the device really hovers. On a phone a tap leaves the
 * toggle in :hover until the next tap lands elsewhere, so after Close the toggle stayed in
 * the hover colour (white on a white page, measured on the co64 test pages). Keyboard
 * focus keeps both on every device.
 */
.dfs-peptide-floating-header__toggle:focus-visible {
	opacity: var( --dfs-sm-toggle-opacity-hover );
}

@media ( hover: hover ) {
	.dfs-peptide-floating-header__toggle:hover { opacity: var( --dfs-sm-toggle-opacity-hover ); }

	.dfs-peptide-floating-header:not( .is-open ) .dfs-peptide-floating-header__toggle:hover { color: var( --dfs-sm-toggle-fg-hover ) !important; }
}

/*
 * @decision D177 — THE HOVER COLOUR ONLY APPLIES WHILE THE MENU IS CLOSED.
 *
 * On touch, a tap leaves the element in :hover until the next touch lands
 * elsewhere — so with the menu OPEN the hover colour (!important, needed to
 * beat hostile themes per D83) was permanently beating the Menu Open colour:
 * Brad's brown open-toggle worked on desktop (the pointer drifts off after
 * the click) and stayed white on every phone. Scoping the colour to
 * :not(.is-open) hands the open state to the Menu Open control on every
 * device; hover feedback while open remains through the opacity line above.
 */
.dfs-peptide-floating-header:not( .is-open ) .dfs-peptide-floating-header__toggle:focus-visible {
	color: var( --dfs-sm-toggle-fg-hover ) !important;
}


/*
 * @decision D83  THE THEME PAINTS OUR BUTTON, AND ONLY THE LIVE PAGE COULD SHOW IT.
 *
 * Measured on the live install: hovering the MENU toggle gave it
 * background-color rgb(204, 51, 102) and color rgb(255, 255, 255). Neither is
 * in this stylesheet. They come from the site's own button styles, which target
 * button:hover and beat a class selector for anything we did not set.
 *
 * @decision D84  the fixture now carries a hostile host stylesheet so this
 * class of collision fails locally. It scored 98/100 across eleven rungs with
 * this bug present, because a
 * fixture has no WordPress theme. It is the exact hole the fixture's own header
 * documents — "does NOT prove theme colour inheritance" — and the reason a live
 * install has to come EARLY rather than after the score looks good.
 *
 * !important is deliberate here and almost nowhere else. Theme button rules are
 * written to win, they differ per site, and a header that turns magenta on one
 * client's theme is not a styling preference. The colour that SHOULD paint is
 * still read from our own custom property, so a control still governs it.
 */
/*
 * The shield refuses the host's background and border but NO LONGER pins the
 * colour: the toggle now has its own inactive and active colours, and a blanket
 * 'color: var(--dfs-sm-bar-fg) !important' here outranked both. A defence that
 * overrides the thing it defends is the same fault the padding reset had.
 */
.dfs-peptide-floating-header__toggle,
.dfs-peptide-floating-header__toggle:hover,
.dfs-peptide-floating-header__toggle:focus,
.dfs-peptide-floating-header__toggle:active,
.dfs-peptide-floating-header__toggle:focus-visible {
	background: none !important;
	background-color: transparent !important;
	border: 0 !important;
	box-shadow: none !important;
	text-decoration: none !important;
}


/*
 * ONE SHIELD OVER EVERY INTERACTIVE ELEMENT, rather than a list per element.
 *
 * The first version named elements individually and missed two: the CTA kept the
 * theme's link blue, and the nav links took its `a:hover` background — a
 * translucent magenta wash behind 64px type. Both were found by inspect.mjs on
 * its first two runs against the hostile fixture.
 *
 * The lesson is that an enumerated defence has to be complete to work, and the
 * thing being defended against is a rule written by someone else that we have
 * never seen. A descendant selector cannot be incomplete in the same way: any
 * anchor or button inside this widget is ours, and none of them should ever
 * carry a host's background, border, shadow or underline.
 *
 * COLOUR IS DELIBERATELY NOT IN THIS BLOCK. Colour is the one property the
 * widget genuinely varies — accent on hover, brand on the current page, muted in
 * the contact column — so blanket-forcing it would break the widget's own
 * design instead of protecting it. It is set per element, with !important, only
 * where a host is known to compete.
 */
/*
 * THE PANEL CTA IS EXEMPT — @decision D153.
 *
 * It is the one anchor in this widget that is SUPPOSED to have a background, a
 * radius and a fill. The shield above is `!important` at (0,2,0) and the panel
 * CTA's own rule is normal weight, so without this exemption the button
 * rendered with `background-color: rgba(0, 0, 0, 0)` and a 0px radius while
 * measuring the correct 48px height — visible geometry, invisible skin.
 *
 * That symptom was first misread as an undefined `--dfs-sm-brand` and "fixed"
 * by adding var() fallbacks. The variable was never undefined: it measured
 * `#FB460D` at the element the whole time. Dumping every matching rule is what
 * found the real cause, which is A1/A2/A4 exactly — the widget's own
 * `!important` beating a rule that could never have outranked it.
 *
 * EXEMPTING IS THE FIX, NOT `!important` ON THE BUTTON. Elementor's generated
 * CSS from the panel CTA's own colour controls never carries `!important`, so
 * shielding the button by raising ITS weight would leave the colour pickers
 * inert — the same defect one level along. Removing it from the shield leaves
 * every declaration at normal weight, where the controls can beat it.
 */
/*
 * @decision D160 — the exemption list, now two entries and GATED.
 *
 * `card-media` joined on 2026-08-13, found by measuring, not by report: with
 * a 27px radius set on both cards of one widget, the LINKED card (an <a>)
 * computed 0px while its unlinked twin (<span>) computed 27px — the border/
 * radius/shadow group controls compile to Elementor per-post CSS at normal
 * weight, and the shield's !important had been eating them on anchors since
 * both shipped. The <span> variant never passed through the shield, which is
 * why the defect only existed on cards that carried a link.
 *
 * `check-shield.mjs` now fails the build when a class the markup puts on an
 * anchor is styled with a shielded property — from the stylesheet OR from a
 * PHP `selectors` map — without an exemption here. Do not add an exemption
 * without that gate seeing it; do not add a skinned anchor without adding its
 * exemption.
 */
/*
 * @decision D286 — THE SHIELD READS THE DECORATION FROM A VARIABLE THE
 * CONTROLS CAN WRITE. Same shape as D138's fill fix, applied to
 * text-decoration.
 *
 * The 1,003-feature editor sweep proved `nav_typography_text_decoration`
 * inert everywhere including the front end: the typography group's
 * sub-field compiles at normal weight onto the anchor itself, and this
 * block's `text-decoration: none !important` always won. Same class of
 * defect on `contact_body_typography` — its `.dfs-peptide-floating-header__contact-line`
 * class lands on <p>, <a> AND <li>, so decoration applied to paragraphs
 * and silently lost on anchors: inconsistent rows, worse than uniformly
 * dead. Every other typography group targets a child span, which ADDS its
 * own decoration and never met the shield.
 *
 * The var is set per element by the controls (fields_options writes
 * `--dfs-sm-td` on exactly the elements each control owns), so the
 * fallback `none` keeps refusing the host theme's underline everywhere a
 * control has not spoken — the defence is unchanged where it matters.
 */
.dfs-peptide-floating-header a:not( .dfs-peptide-floating-header__panel-cta ):not( .dfs-peptide-floating-header__card-media ),
.dfs-peptide-floating-header a:hover:not( .dfs-peptide-floating-header__panel-cta ):not( .dfs-peptide-floating-header__card-media ),
.dfs-peptide-floating-header a:focus:not( .dfs-peptide-floating-header__panel-cta ):not( .dfs-peptide-floating-header__card-media ),
.dfs-peptide-floating-header a:active:not( .dfs-peptide-floating-header__panel-cta ):not( .dfs-peptide-floating-header__card-media ),
.dfs-peptide-floating-header a:visited:not( .dfs-peptide-floating-header__panel-cta ):not( .dfs-peptide-floating-header__card-media ),
.dfs-peptide-floating-header button,
.dfs-peptide-floating-header button:hover,
.dfs-peptide-floating-header button:focus,
.dfs-peptide-floating-header button:active {
	background: none !important;
	background-color: transparent !important;
	border: 0 !important;
	border-radius: 0 !important;
	box-shadow: none !important;
	text-decoration: var( --dfs-sm-td, none ) !important;
	outline: none !important;
}


/*
 * PADDING IS RESET ON BUTTONS ONLY, and this cost a regression to learn.
 *
 * The shield above originally carried `padding: 0`. `.dfs-peptide-floating-header a:hover` is
 * (0,2,1) and `.dfs-peptide-floating-header__nav a` is (0,1,1), so on hover the shield outranked
 * the nav link's own `padding-block: 4px` and every link lost 8px of height the
 * moment the pointer touched it — measured as `paddingTop: 4px -> 0px`.
 *
 * A defence that outranks the thing it defends is not a defence. Buttons are
 * where a theme's padding actually lands (8px 14px here), so that is where the
 * reset belongs.
 */
.dfs-peptide-floating-header button,
.dfs-peptide-floating-header button:hover,
.dfs-peptide-floating-header button:focus,
.dfs-peptide-floating-header button:active {
	padding: 0 !important;
	margin: 0 !important;
}


/* The CTA is the one element that WANTS a background — its own brand fill,
 * painted on the inner chips rather than the anchor, which is why the shield
 * above can safely clear the anchor itself.
 *
 * @decision D138 — THE SHIELD READS THE FILL VARIABLE TOO. Fixes A1 and A2.
 *
 * D137 gave the fill rule below `var( --dfs-sm-cta-fill, var( --dfs-sm-brand ) )`
 * and had the controls write that variable. It was emitted correctly, matched
 * the element, and still lost — because THIS rule is `!important` and that one
 * is not, and `!important` beats normal weight regardless of specificity or
 * source order. Three attempts were spent hunting for a selector that could
 * win. None can: Elementor's `selectors` CSS never carries `!important`, so
 * no selector it generates can ever beat this line.
 *
 * So the variable has to be read INSIDE the declaration that already wins.
 * The fallback keeps the default render byte-identical — `--dfs-sm-cta-fill`
 * is unset at defaults, so this still resolves to the brand — and the shield
 * keeps refusing a theme's background exactly as before, because the
 * `!important` is untouched.
 *
 * This is the same lesson as D125, applied to the rule that actually holds the
 * property rather than the one next to it. */
.dfs-peptide-floating-header__cta-label,
.dfs-peptide-floating-header__cta-chip {
	background-color: var( --dfs-sm-cta-fill, var( --dfs-sm-brand ) ) !important;
}


/* The CTA keeps its own brand fill, so only the theme's border, shadow and
 * padding are refused — never its background, which is ours. */
.dfs-peptide-floating-header__cta,
.dfs-peptide-floating-header__cta:hover,
.dfs-peptide-floating-header__cta:focus,
.dfs-peptide-floating-header__cta:active {
	/* COLOUR TOO. The first version of this reset covered background, border,
	 * shadow and padding and left `color` alone, so the theme's link blue
	 * (#0b57d0) painted the CTA label — caught by inspect.mjs on its very first
	 * run against the hostile fixture, which is the entire argument for the
	 * hostile fixture. */
	color: var( --dfs-sm-bar-fg ) !important;
	background: none !important;
	border: 0 !important;
	box-shadow: none !important;
	text-decoration: none !important;
	padding: 0 !important;
}


@media ( min-width: 1024px ) {
	.dfs-peptide-floating-header__toggle { justify-self: center; }
}


/* Same window-and-track mechanism as the logo, at 1em. */
/*
 * @decision D141 — WIDTH COMES FROM THE LABELS. Fixes A6.
 *
 * 3.5em was measured for the stock "MENU"/"CLOSE" pair and then hardcoded, so
 * any shorter label left dead space inside the window — and because the flex
 * `gap` is measured from the window's edge rather than the text's, that dead
 * space was added to every `toggle_gap` value. That sum is the ~20px floor
 * Brad could not get under, and it is why the control measured as emitting
 * correctly while the spacing refused to close.
 *
 * render() now emits `--dfs-sm-label-window-w` from the longer of the two
 * labels. The 3.5em fallback is kept deliberately: a cached page that still
 * has the old markup renders exactly as it did before.
 */
/*
 * ⬥ THE ch ESTIMATE MUST NOT DRIVE LAYOUT BEFORE JS HAS MEASURED.
 * render() emits --dfs-sm-label-window-w as calc( Nch + ... ), and `ch` is the
 * advance of the "0" glyph — it equals one character only in a MONOSPACE font.
 * --dfs-sm-mono is one, but a typography control can set a proportional family
 * (the `fullscreen` preset sets Inter), where every capital is wider than "0".
 * MEASURED on clarityfilms 2026-09-09: 4ch resolved to 33.81px against a real
 * label width of 46-48px, so `overflow: hidden` ate the last character and
 * "MENU" painted as "MEN"; the JS correction below then animated the window
 * open, wiping the "U" in over 300ms. Reported as a lazy-load; it was this.
 *
 * So until the measurement lands the window is max-content, which cannot clip,
 * and carries no transition — the switch to the measured px value is therefore
 * instant, not a visible wipe (a transition cannot start when the before-style
 * declares no transition-property). The closed label is measured alone: sizing
 * max-content to the longer of the two labels would move the burger when the
 * exact value arrives.
 */
.dfs-peptide-floating-header__label-window {
	position: relative;
	display: block;
	height: 1em;
	width: max-content;
	overflow: hidden;
	text-align: left;
}


/* Out of flow, so it adds nothing to max-content above. visibility keeps it
 * measurable — the JS reads its scrollWidth for the open-state width. */
.dfs-peptide-floating-header__label-window:not( .is-measured ) .dfs-peptide-floating-header__label:last-child {
	position: absolute;
	visibility: hidden;
}


.dfs-peptide-floating-header__label-window.is-measured {
	width: var( --dfs-sm-label-window-w, 3.5em );
	transition: width var( --dfs-sm-label-duration ) var( --dfs-sm-ease-out );
}


/* The open label is usually a different length from the closed one, so the
 * window resizes with the slide instead of staying wide enough for both. This
 * is what actually removes the floor — sizing to the longer label left the
 * difference between "CLOSE" and "MENU" sitting against the burger. */
.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__label-window.is-measured {
	width: var( --dfs-sm-label-window-w-open, var( --dfs-sm-label-window-w, 3.5em ) );
}


.dfs-peptide-floating-header__label-track {
	display: flex;
	flex-direction: column;
	gap: 2px;
	transform: translateY( 0 );
	transition: transform var( --dfs-sm-label-duration ) var( --dfs-sm-ease-out );
}


.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__label-track {
	transform: translateY( calc( -1em - 2px ) );
}


.dfs-peptide-floating-header__label {
	display: block;
	height: 1em;
	line-height: 1;
}


/*
 * TWO BARS, 16x2, OFFSET +/-3px, ROTATING TO +/-45deg.
 *
 * The transform order is `rotate() translateY()` — rotate FIRST. At rest that
 * reads as a plain 3px offset; at 45deg the offset travels along the rotated
 * axis, which is what closes the two bars into a centred X instead of leaving
 * them 6px apart and crossed. Writing `translateY() rotate()` produces a
 * visibly different, slightly wrong X, and both orders look identical at rest.
 */
.dfs-peptide-floating-header__burger {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	height: 16px;
	width: 16px;
	flex-shrink: 0;
}


.dfs-peptide-floating-header__burger span {
	position: absolute;
	height: 2px;
	width: 100%;
	transform-origin: center;
	background-color: currentColor;
	transition: transform var( --dfs-sm-burger-duration ) var( --dfs-sm-ease-out );
}


/*
 * @decision D122 — THE BAR SEPARATION IS DERIVED, not a constant.
 *
 * These were hardcoded at -3px/+3px, which is correct for the 16px box and
 * wrong for every other size. Adding a burger Size control on top of a fixed
 * offset would have produced a control whose every non-default value is a
 * DEGRADED layout — two bars bunched in the middle of a large box — and a
 * control that can only make things worse is one that should not ship.
 *
 * 3/16 = 0.1875, so the fallback here reproduces the previous constant exactly
 * at the default size and the default render cannot move.
 */
/*
 * @decision D178 — the explicit Space Between Lines slider outranks the
 * size-derived offset. `--dfs-sm-burger-gap-offset` is only ever written by
 * render() on the ROOT (so it inherits through), while burger_size's derived
 * offset is per-post CSS on the burger element itself — a wrapped var chain
 * is what lets the hand-set value win without an !important anywhere.
 */
.dfs-peptide-floating-header__burger span:first-child { transform: rotate( 0deg ) translateY( calc( -1 * var( --dfs-sm-burger-gap-offset, var( --dfs-sm-burger-offset, 3px ) ) ) ); }

.dfs-peptide-floating-header__burger span:last-child  { transform: rotate( 0deg ) translateY( var( --dfs-sm-burger-gap-offset, var( --dfs-sm-burger-offset, 3px ) ) ); }


/* @decision D179 — rounded line ends, opt-in; square stays the default. */
.dfs-peptide-floating-header--burger-round .dfs-peptide-floating-header__burger span { border-radius: 999px; }


.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__burger span:first-child { transform: rotate( 45deg ) translateY( 0 ); }

.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__burger span:last-child  { transform: rotate( -45deg ) translateY( 0 ); }


/*
 * @decision D176 — THE THREE-LINE BURGER. The morph above keys on
 * first-child/last-child, so the middle span joins without touching it: the
 * outer pair still folds into the X and the middle fades out. It sits at the
 * box centre (no translateY), which is where the two outer lines meet.
 */
.dfs-peptide-floating-header--burger-3 .dfs-peptide-floating-header__burger span:nth-child( 2 ) {
	transform: none;
	transition:
		opacity var( --dfs-sm-burger-duration ) var( --dfs-sm-ease-out ),
		transform var( --dfs-sm-burger-duration ) var( --dfs-sm-ease-out );
}

.dfs-peptide-floating-header--burger-3.is-open .dfs-peptide-floating-header__burger span:nth-child( 2 ) { opacity: 0; }


/*
 * @decision D175 — ICON-ONLY TOGGLE ON MOBILE. Only the label window hides;
 * the burger stays, and display:none removes the window from the flex row so
 * the toggle gap collapses with it — no stray offset.
 */
@media ( max-width: 1023px ) {
	.dfs-peptide-floating-header--no-label-mobile .dfs-peptide-floating-header__label-window { display: none; }
}


/* ===================================================== 3.41.0 ADDITIONS ==
 * D184 numbering · D185 clock · D188 image reveal · D189 bloom/wipe reveals.
 */

/* -- D184: editorial numbering. The marker never renders in this mode, so
 *    the indent machinery has nothing to step around. -- */
.dfs-peptide-floating-header--nav-numbers .dfs-peptide-floating-header__nav { --dfs-sm-nav-indent-desktop: 0px; --dfs-sm-nav-indent-mobile: 0px; }
.dfs-peptide-floating-header__nav-num {
	display: inline-block;
	vertical-align: super;
	margin-right: 14px;
	font-size: 13px;
	font-weight: 600;
	letter-spacing: 0.08em;
	color: var( --dfs-sm-muted, #818081 );
}

/* -- D185: the living clock line, foot of the open menu. -- */
.dfs-peptide-floating-header__clock {
	margin: 24px 0 0;
	font-size: 13px;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var( --dfs-sm-muted, #818081 );
}
.dfs-peptide-floating-header__clock-sep { margin: 0 8px; }
.dfs-peptide-floating-header__clock-time { color: var( --dfs-sm-contact-fg, #EEEEEE ); }

/* -- D188: the reveal layer sits between the panel surface and its content;
 *    the panel establishes the context and the clip stacks above. -- */
.dfs-peptide-floating-header--imgrev .dfs-peptide-floating-header__panel { position: relative; }
.dfs-peptide-floating-header--imgrev .dfs-peptide-floating-header__panel-clip { position: relative; z-index: 1; }
.dfs-peptide-floating-header__imgrev { position: absolute; inset: 0; overflow: hidden; }
.dfs-peptide-floating-header__imgrev img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	opacity: 0;
	transform: scale( 1.04 );
	transition:
		opacity 0.5s var( --dfs-sm-ease-out ),
		transform 1.2s var( --dfs-sm-ease-out );
}
.dfs-peptide-floating-header__imgrev img.is-show { opacity: 1; transform: scale( 1 ); }
/* The dim that keeps the type readable over any image. */
.dfs-peptide-floating-header__imgrev::after {
	content: '';
	position: absolute;
	inset: 0;
	background: rgba( 0, 0, 0, 0.45 );
}

/* -- D189: reveal appearances. The clip lives on the panel CLIP element so
 *    the fitted grid-rows expand underneath is untouched; is-preview-open
 *    keeps the editor canvas honest (D174's lesson). -- */
.dfs-peptide-floating-header--appear-bloom .dfs-peptide-floating-header__panel-clip {
	clip-path: circle( 0% at 50% 40px );
	transition: clip-path calc( var( --dfs-sm-expand-duration, 0.66s ) * 1.2 ) var( --dfs-sm-ease-out );
}
.dfs-peptide-floating-header--appear-bloom.is-open .dfs-peptide-floating-header__panel-clip,
.dfs-peptide-floating-header--editor.dfs-peptide-floating-header--appear-bloom.is-preview-open .dfs-peptide-floating-header__panel-clip {
	clip-path: circle( 160% at 50% 40px );
}

/* -- D369: the ease became a VARIABLE so mode 7 can set its measured curve
 *    without moving D189's own mode. The fallback IS the shipped value, so an
 *    appear-wipe instance that does not set the variable renders byte-identically
 *    to 3.74.0 — the Stage 2 promise. Measured off the stepload reference: the
 *    wipe runs on cubic-bezier(0.76,0,0.24,1) (= --dfs-sm-ease-in-out-quart) over
 *    785ms, best of 11 candidates at all three viewports. -- */
.dfs-peptide-floating-header--appear-wipe .dfs-peptide-floating-header__panel-clip {
	clip-path: inset( 0 0 100% 0 );
	transition: clip-path var( --dfs-sm-expand-duration, 0.66s )
		var( --dfs-sm-appear-ease, var( --dfs-sm-ease-out ) );
}
.dfs-peptide-floating-header--appear-wipe.is-open .dfs-peptide-floating-header__panel-clip,
.dfs-peptide-floating-header--editor.dfs-peptide-floating-header--appear-wipe.is-preview-open .dfs-peptide-floating-header__panel-clip {
	clip-path: inset( 0 );
}

@media ( prefers-reduced-motion: reduce ) and (dfs-reduced-motion-honoured) {
	.dfs-peptide-floating-header--appear-bloom .dfs-peptide-floating-header__panel-clip,
	.dfs-peptide-floating-header--appear-wipe .dfs-peptide-floating-header__panel-clip {
		clip-path: none;
		transition: none;
	}
	.dfs-peptide-floating-header__imgrev img { transition: none; transform: none; }
}


/* ===================================================== 3.43.0 ADDITIONS ==
 * D192 cursor system + magnetic · D194 ambient video · D195 cascade.
 */

/* -- D195: the cascade. Delay on OPEN only; closing stays uniform. -- */
.dfs-peptide-floating-header--nav-stagger.is-open .dfs-peptide-floating-header__nav a {
	transition-delay: calc( var( --dfs-sm-i, 0 ) * 70ms ), 0s;
}

/* -- D194: the ambient loop fills the shared reveal layer, always visible,
 *    beneath the reveal images and the dim. -- */
.dfs-peptide-floating-header__ambient {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* -- D192: the cursor container and its parts. Everything is fixed,
 *    pointer-events:none, and only paints while `.is-live`. The native
 *    cursor stays visible except under the full replacement styles. -- */
.dfs-peptide-floating-header__cursor {
	position: fixed;
	inset: 0;
	z-index: 10000;
	pointer-events: none;
	opacity: 0;
	transition: opacity 0.25s ease;
}
.dfs-peptide-floating-header__cursor.is-live { opacity: 1; }

.dfs-peptide-floating-header__cursor-dot,
.dfs-peptide-floating-header__cursor-ring,
.dfs-peptide-floating-header__cursor-big,
.dfs-peptide-floating-header__cursor-ghost {
	position: fixed;
	top: 0;
	left: 0;
	border-radius: 50%;
	will-change: transform;
}
.dfs-peptide-floating-header__cursor-dot {
	width: 10px;
	height: 10px;
	margin: -5px 0 0 -5px;
	background: var( --dfs-sm-brand, #FB460D );
}
.dfs-peptide-floating-header__cursor-ring {
	width: 38px;
	height: 38px;
	margin: -19px 0 0 -19px;
	border: 1.5px solid var( --dfs-sm-brand, #FB460D );
	transition: width 0.25s ease, height 0.25s ease, border-radius 0.25s ease;
	display: flex;
	align-items: center;
	justify-content: center;
}
.dfs-peptide-floating-header__cursor.is-over .dfs-peptide-floating-header__cursor-ring { width: 56px; height: 56px; margin: -28px 0 0 -28px; }
.dfs-peptide-floating-header__cursor--snap .dfs-peptide-floating-header__cursor-ring { border-radius: 999px; margin: 0; translate: -50% -50%; }
.dfs-peptide-floating-header__cursor--label .dfs-peptide-floating-header__cursor-ring i {
	font-style: normal;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.08em;
	color: var( --dfs-sm-brand, #FB460D );
}
.dfs-peptide-floating-header__cursor--label.is-over .dfs-peptide-floating-header__cursor-ring { background: var( --dfs-sm-surface, #333 ); border-color: transparent; }

.dfs-peptide-floating-header__cursor--invert .dfs-peptide-floating-header__cursor-big {
	width: 88px;
	height: 88px;
	margin: -44px 0 0 -44px;
	background: #fff;
	mix-blend-mode: difference;
}
.dfs-peptide-floating-header__cursor--spotlight .dfs-peptide-floating-header__cursor-big {
	width: 340px;
	height: 340px;
	margin: -170px 0 0 -170px;
	background: radial-gradient( circle, rgba( 255, 255, 255, 0.10 ) 0%, transparent 65% );
}

.dfs-peptide-floating-header__cursor-ghost {
	width: 8px;
	height: 8px;
	margin: -4px 0 0 -4px;
	background: var( --dfs-sm-brand, #FB460D );
	opacity: 0.5;
}
.dfs-peptide-floating-header__cursor--blob .dfs-peptide-floating-header__cursor-ghost {
	width: 22px;
	height: 22px;
	margin: -11px 0 0 -11px;
	opacity: 0.85;
	filter: blur( 4px );
}

.dfs-peptide-floating-header__cursor-hline,
.dfs-peptide-floating-header__cursor-vline {
	position: fixed;
	background: var( --dfs-sm-brand, #FB460D );
	opacity: 0.6;
	will-change: transform;
}
.dfs-peptide-floating-header__cursor-hline { top: 0; left: 0; width: 100vw; height: 1px; }
.dfs-peptide-floating-header__cursor-vline { top: 0; left: 0; width: 1px; height: 100vh; }

.dfs-peptide-floating-header__cursor-ripple {
	position: fixed;
	width: 12px;
	height: 12px;
	margin: -6px 0 0 -6px;
	border: 1.5px solid var( --dfs-sm-brand, #FB460D );
	border-radius: 50%;
	animation: dfs-sm-ripple 0.65s var( --dfs-sm-ease-out ) forwards;
}
@keyframes dfs-sm-ripple {
	to { width: 120px; height: 120px; margin: -60px 0 0 -60px; opacity: 0; }
}

/* The full replacements hide the native cursor inside the open menu. */
.dfs-peptide-floating-header[data-dfs-cursor="dot"].is-open,
.dfs-peptide-floating-header[data-dfs-cursor="ring"].is-open,
.dfs-peptide-floating-header[data-dfs-cursor="snap"].is-open,
.dfs-peptide-floating-header[data-dfs-cursor="label"].is-open,
.dfs-peptide-floating-header[data-dfs-cursor="comet"].is-open,
.dfs-peptide-floating-header[data-dfs-cursor="blob"].is-open {
	cursor: none;
}

@media ( prefers-reduced-motion: reduce ) and (dfs-reduced-motion-honoured) {
	.dfs-peptide-floating-header__cursor { display: none; }
	.dfs-peptide-floating-header--nav-stagger.is-open .dfs-peptide-floating-header__nav a { transition-delay: 0s; }
}


/*
 * THE PANEL CTA — B7 / @decision D153.
 *
 * Deliberately NOT sharing `.dfs-peptide-floating-header__cta-label`. That class carries an
 * `!important` fill shield built to survive a hostile theme in the BAR, and
 * inheriting it here would make every panel-CTA colour control fight a shield
 * aimed at a different element — the A1/A2/A4 lesson, where the widget's own
 * `!important` beat Elementor's generated CSS and three attempts went into
 * finding a better selector that could never have worked.
 *
 * So the defaults are declared at NORMAL weight and every one of them is
 * overridable by the Elementor controls without a specificity fight.
 *
 * `margin-top: auto` is what puts it at the foot of the contact column. The
 * status block uses the same mechanism, so when both are present the status
 * block keeps its place and the CTA sits under it.
 */
.dfs-peptide-floating-header__panel-cta {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	align-self: flex-start;
	margin-top: 24px;
	padding: 12px 24px;
	border-radius: 999px;
	/*
	 * FALLBACKS ARE LOAD-BEARING, not belt-and-braces.
	 *
	 * The first build of this rule was `var( --dfs-sm-brand )` with no
	 * fallback, and the live sweep read the button's background as
	 * `rgba(0, 0, 0, 0)` while its height measured 48px — so the rule was
	 * matching and only the COLOUR was dropping out. `--dfs-sm-brand` is
	 * declared on the theme blocks (`[data-dfs-theme="dark"|"light"]`) and
	 * emitted by `accent_colour` only when that control is set; on a menu with
	 * neither, the var is undefined, `background-color: var( --undefined )` is
	 * invalid at computed-value time, and the declaration is thrown away
	 * entirely rather than falling back to anything.
	 *
	 * An invisible button is indistinguishable from a broken one, and this is
	 * the same shape as `--dfs-sm-bar-bg-top` (D97): faithfully referenced,
	 * resolving to nothing. Every var here now names its own fallback.
	 */
	background-color: var( --dfs-sm-brand, #FB460D );
	color: var( --dfs-sm-surface, #D5D5D5 );
	font-family: var( --dfs-sm-mono );
	font-size: var( --dfs-sm-label-size );
	letter-spacing: var( --dfs-sm-label-tracking );
	text-transform: uppercase;
	text-decoration: none;
	line-height: 1.5;
	transition:
		background-color var( --dfs-sm-bar-duration ) var( --dfs-sm-ease-out ),
		color var( --dfs-sm-bar-duration ) var( --dfs-sm-ease-out );
}


.dfs-peptide-floating-header__panel-cta:hover,
.dfs-peptide-floating-header__panel-cta:focus-visible {
	background-color: var( --dfs-sm-bar-fg, #141314 );
	color: var( --dfs-sm-surface, #D5D5D5 );
}


/* ---------------------------------------------------------------- the CTA */

/*
 * Hidden below 1024 in the reference, so it is `display: none` here rather
 * than shrunk — a CTA that wraps under a 56px bar is not the mobile design,
 * it is the desktop design failing.
 */
.dfs-peptide-floating-header__cta {
	display: none;
	justify-self: end;
	min-width: 0;
	flex-shrink: 0;
	white-space: nowrap;
	text-decoration: none;
	font-family: var( --dfs-sm-mono );
	font-size: var( --dfs-sm-label-size );
	font-weight: 500;
	line-height: 1.5;
	letter-spacing: -0.025em;
	text-transform: uppercase;
	outline: none;
}


@media ( min-width: 1024px ) {
	.dfs-peptide-floating-header__cta { display: inline-flex; }
}


.dfs-peptide-floating-header__cta-inner {
	position: relative;
	display: flex;
	width: 100%;
	align-items: center;
	gap: 6px;
}


/*
 * THE LABEL GOES WHITE ON HOVER, which is what theirs does and ours did not.
 * The fill stays brand; only the text changes. Colour transitions on its own
 * 0.3s rather than riding the 0.7s chip movement, or the text lags the motion.
 */
/*
 * @decision D137 — THE FILL READS A DEDICATED VARIABLE, with the brand as its
 * fallback.
 *
 * The pill and its chip share one fill, and it was hard-wired to
 * `--dfs-sm-brand`. That made the Menu Open fill (D120) unreachable: a control
 * can only win here by setting `background-color` at higher specificity, and
 * this rule is also the thing the hover state overrides with `!important`, so
 * the property is contested from two directions at once.
 *
 * `--dfs-sm-cta-fill` gives the controls somewhere to write that the cascade
 * cannot argue with — an inline custom property on the root beats every
 * stylesheet rule, and the fallback keeps the default render byte-identical
 * when nothing sets it. Same lesson as D125: when `!important` or a variable
 * already owns a property, route through the variable rather than fighting it.
 */
.dfs-peptide-floating-header__cta-label,
.dfs-peptide-floating-header__cta-chip {
	background-color: var( --dfs-sm-cta-fill, var( --dfs-sm-brand ) );
	color: var( --dfs-sm-cta-fg );
	transition: transform 0.7s var( --dfs-sm-ease-in-out-quint ),
		background-color var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out ),
		color var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out );
}


.dfs-peptide-floating-header__cta:hover .dfs-peptide-floating-header__cta-label,
.dfs-peptide-floating-header__cta:hover .dfs-peptide-floating-header__cta-chip,
.dfs-peptide-floating-header__cta:focus-visible .dfs-peptide-floating-header__cta-label,
.dfs-peptide-floating-header__cta:focus-visible .dfs-peptide-floating-header__cta-chip {
	/*
	 * The open state's own properties are asked for FIRST, with the existing
	 * ones as the fallback. They are only ever set while the menu is open, so
	 * a closed bar resolves exactly as it always did, and a widget that never
	 * sets them is untouched — an unset custom property falls straight
	 * through var().
	 *
	 * This is a different QUESTION rather than a louder answer: the existing
	 * property is written inline on the root and cannot be out-specified.
	 */
	background-color: var( --dfs-sm-cta-fill-hover-open, var( --dfs-sm-cta-fill-hover ) ) !important;
	color: var( --dfs-sm-cta-fg-hover-open, var( --dfs-sm-cta-fg-hover ) );
}


/*
 * NO MINI BUTTON — the label fills the button instead of leaving a gap where
 * the chip used to slide. Without this the label keeps its
 * 'translateX(-40px - 6px)' and sits offset from an edge that no longer exists.
 */
.dfs-peptide-floating-header__cta-inner:not( :has( .dfs-peptide-floating-header__cta-chip ) ) .dfs-peptide-floating-header__cta-label {
	transform: none !important;
	padding-inline: 16px;
}


.dfs-peptide-floating-header__cta-label {
	display: flex;
	flex: 1 1 auto;
	align-items: center;
	justify-content: center;
	height: 32px;
	padding-inline: 8px;
	transform: translateX( calc( -32px - 6px ) );
}


.dfs-peptide-floating-header__cta-chip {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	flex-shrink: 0;
}


.dfs-peptide-floating-header__cta-chip svg { width: 0.75em; height: 0.75em; display: block; }


.dfs-peptide-floating-header__cta-chip--in {
	transform-origin: left;
	transform: rotate( -45deg ) scale( 0 );
}


.dfs-peptide-floating-header__cta-chip--out {
	position: absolute;
	right: 0;
	z-index: 10;
	transform-origin: right;
	transform: rotate( 0deg ) scale( 1 );
}


.dfs-peptide-floating-header__cta:hover .dfs-peptide-floating-header__cta-chip--in  { transform: rotate( 0deg ) scale( 1 ); }

.dfs-peptide-floating-header__cta:hover .dfs-peptide-floating-header__cta-label     { transform: translateX( 0 ); }

.dfs-peptide-floating-header__cta:hover .dfs-peptide-floating-header__cta-chip--out { transform: rotate( -45deg ) scale( 0 ); }


@media ( min-width: 1024px ) {
	.dfs-peptide-floating-header__cta-label {
		height: 40px;
		padding-inline: 12px;
		transform: translateX( calc( -40px - 6px ) );
	}
	.dfs-peptide-floating-header__cta-chip { width: 40px; height: 40px; }
	.dfs-peptide-floating-header__cta:hover .dfs-peptide-floating-header__cta-label { transform: translateX( 0 ); }
}


/* -------------------------------------------------------------- the panel */

/*
 * `grid-template-rows: 0fr -> 1fr` WITH AN `overflow: hidden` CHILD.
 *
 * The panel measures itself. A fixed pixel height — which is what §6's
 * "0 -> 639px" would have produced — is correct only for the exact content
 * that was on screen the day it was measured, and clips silently for anything
 * longer. `1fr` on a grid row resolves to the content height at every width.
 *
 * The child MUST be the overflow: hidden element and MUST NOT have its own
 * height, or the row has nothing to collapse.
 */
/*
 * TWO TRANSITIONS, ONE PER DIRECTION.
 *
 * The transition that runs is the one declared on the state being transitioned
 * TO, so the base rule below governs CLOSING and the `.is-open` rule governs
 * OPENING. That is the only way to express an asymmetric pair in CSS, and this
 * pair is strongly asymmetric — 810ms of eased travel out, an instant snap
 * back after a wait.
 *
 * Fitted, not chosen. Searching every easing the reference declares against its
 * own 22-sample envelope returns `ease-in-out-quart` at 150ms delay and 660ms
 * duration, with an RMS error of 0.31% of full travel. The runner-up is quint
 * at 0.59%, which is why §3's guess was close; the hand fit this session used
 * (`ease-in-out`, 350/525) comes fifth at 1.74%, five times worse than the
 * search and entirely plausible-looking on a five-point summary.
 */
.dfs-peptide-floating-header__panel {
	display: grid;
	grid-template-rows: 0fr;
	background-color: var( --dfs-sm-surface );
	transition:
		grid-template-rows var( --dfs-sm-collapse-duration ) linear var( --dfs-sm-collapse-delay ),
		background-color var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out );
}


.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__panel {
	grid-template-rows: 1fr;
	transition:
		grid-template-rows var( --dfs-sm-expand-duration ) var( --dfs-sm-expand-ease ) var( --dfs-sm-expand-delay ),
		background-color var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out );
}


/*
 * @decision D85  THE PANEL NEVER GROWS PAST THE WINDOW — a deliberate improvement, not a
 * reproduction.
 *
 * The reference does not solve this. At Brad's real 1277x502 window its own
 * panel runs 233px below the fold with no way to reach the bottom, because the
 * page underneath is scroll-locked while the menu is open. Ours ran 111px over.
 * Reproducing that faithfully would be reproducing a fault.
 *
 * The cap ends the panel at the window edge and lets it scroll internally when
 * the content will not fit. On a tall window nothing changes at all —
 * max-height only ever binds when the content would otherwise be unreachable.
 */
/*
 * A DESIGNED EXIT, as an option.
 *
 * The reference does NOT do this — its close is a hold then a snap, measured at
 * 678px for 437ms and zero by 520ms with nothing in between on any rung. So
 * this is a house addition and it ships OFF, which keeps the reference
 * reproducible by default.
 *
 * The order is cards, then nav, then the panel itself: each thing leaves before
 * the thing that contains it. Reversing the entrance is the obvious approach
 * and it reads as a rewind — leaving in a DIFFERENT order from arriving is what
 * makes it read as choreography rather than as playback.
 *
 * Only the closing direction is touched. The `.is-open` rules restore whatever
 * the entrance delays were, so turning this on cannot change how the panel
 * opens.
 */
.dfs-peptide-floating-header--exit .dfs-peptide-floating-header__card-media,
.dfs-peptide-floating-header--exit .dfs-peptide-floating-header__card-caption { transition-delay: 0s; }


.dfs-peptide-floating-header--exit .dfs-peptide-floating-header__nav a { transition-delay: 0.12s; }


.dfs-peptide-floating-header--exit .dfs-peptide-floating-header__panel {
	transition-delay: 0.34s;
	transition-duration: 0.3s;
}


.dfs-peptide-floating-header__panel-clip {
	overflow: hidden;
	max-height: calc( 100vh - var( --dfs-sm-header-pad-top ) * 2 - var( --dfs-sm-bar-height, 72px ) - var( --dfs-sm-bar-gap ) );
}


.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__panel-clip {
	overflow-y: auto;
	overscroll-behavior: contain;
}


/* Brad 2026-10-15: "always hide scrollbar on it". The panel can still scroll
 * (wheel, touch, keys); only the bar is not drawn. */
.dfs-peptide-floating-header__panel-clip { scrollbar-width: none; }
.dfs-peptide-floating-header__panel-clip::-webkit-scrollbar { display: none; }


.dfs-peptide-floating-header__panel-inner { padding: var( --dfs-sm-panel-pad ); }


/*
 * @decision D146 — HIDE THE PAGE SCROLLBAR WHILE THE PANEL IS OPEN, WITHOUT
 * TAKING SCROLLING AWAY.
 *
 * Brad: "my menu still has a scrollbar, anyway to completly hide scroll bar
 * ( even if ther is scrolling possibler )" — and then the important half:
 * "my resolution is also shorter than most which is why it does that i assume
 * and not in your test resolution." Exactly right, and it is the reason the
 * A11 test passed while the defect was real: that test measured the PAGE
 * scrollbar at 1920x940, where the content fits and there is none. A gate only
 * ever sees the viewport shapes it looks at.
 *
 * This is deliberately NOT the scroll lock. `scroll_lock` stops the page moving
 * at all, which is right for a full-page fill and wrong for a dropdown panel
 * someone may still want to scroll behind. Hiding the bar while leaving the
 * scrolling is the thing that was actually asked for.
 *
 * THE PADDING IS NOT OPTIONAL. Removing the scrollbar frees its gutter, so the
 * page would jump sideways by its width the moment the menu opens and jump back
 * on close. The JS measures the real width into `--dfs-sm-sbw` before hiding it
 * and that is given back as padding, so the layout does not move. A fixed 15px
 * would be wrong on overlay-scrollbar platforms, where the true width is 0.
 */
html.dfs-sm-no-scrollbar {
	scrollbar-width: none;          /* Firefox */
	-ms-overflow-style: none;       /* legacy Edge */
	padding-right: var( --dfs-sm-sbw, 0px );
}


html.dfs-sm-no-scrollbar::-webkit-scrollbar {
	width: 0;
	height: 0;
	display: none;                  /* Chrome, Safari, Edge */
}


/*
 * @decision D127 — LOGO HOVER, one class per effect. Every rule is gated
 * behind a class the widget only emits when the toggle is on, so an untouched
 * logo has no transition and no transform.
 */
/*
 * @decision D130 — THE ANIMATED ELEMENT IS `.dfs-peptide-floating-header__logo`, NOT THE TRACK.
 *
 * Fixes a defect shipped in 3.25.0. Brad: "if logo is given a hover grow
 * animation, when it grows it clips".
 *
 * `.dfs-peptide-floating-header__logo` is `overflow: hidden` and that is LOAD-BEARING — the
 * track holds both the inactive and active logo stacked and slides between
 * them, so the clip box is the only thing stopping both from showing at once.
 * Scaling the track therefore scaled the content INSIDE a fixed clip box and
 * the overflow was cut off. Making the box `overflow: visible` would have
 * exposed the second logo, which is why that is not the fix.
 *
 * Transforming the clip box itself scales the box and its content together, so
 * nothing is cut and the swap still works.
 */
.dfs-peptide-floating-header--logo-hover-grow .dfs-peptide-floating-header__logo,
.dfs-peptide-floating-header--logo-hover-shrink .dfs-peptide-floating-header__logo,
.dfs-peptide-floating-header--logo-hover-tilt .dfs-peptide-floating-header__logo,
.dfs-peptide-floating-header--logo-hover-float .dfs-peptide-floating-header__logo,
.dfs-peptide-floating-header--logo-hover-fade .dfs-peptide-floating-header__logo {
	transform-origin: left center;
	transition:
		transform var( --dfs-sm-logo-hover-duration, 0.35s ) var( --dfs-sm-ease-out ),
		opacity var( --dfs-sm-logo-hover-duration, 0.35s ) var( --dfs-sm-ease-out );
}


/* `transform-origin: left center` keeps the logo LEFT ALIGNED as it scales —
 * a centre origin walks it toward the viewport edge, which is the same
 * complaint the width slider produced. */
.dfs-peptide-floating-header--logo-hover-grow .dfs-peptide-floating-header__logo:hover   { transform: scale( 1.06 ); }

.dfs-peptide-floating-header--logo-hover-shrink .dfs-peptide-floating-header__logo:hover { transform: scale( 0.94 ); }

.dfs-peptide-floating-header--logo-hover-tilt .dfs-peptide-floating-header__logo:hover   { transform: rotate( -3deg ); }

.dfs-peptide-floating-header--logo-hover-float .dfs-peptide-floating-header__logo:hover  { transform: translateY( -3px ); }

.dfs-peptide-floating-header--logo-hover-fade .dfs-peptide-floating-header__logo:hover   { opacity: 0.7; }


/*
 * @decision D128 — LOGO ENTRANCE, ON PAGE LOAD.
 *
 * A keyframe animation rather than a transition, precisely because there is no
 * state change to transition FROM: the header is fixed and portalled (D71), so
 * it is painted in its final position on the first frame and never enters the
 * viewport. `backwards` fill matters — with a delay set, the logo must hold the
 * FROM state during the wait rather than flashing at full opacity first.
 */
@keyframes dfs-sm-logo-fade  { from { opacity: 0; } to { opacity: 1; } }

@keyframes dfs-sm-logo-rise  { from { opacity: 0; transform: translateY( 12px ); }  to { opacity: 1; transform: none; } }

@keyframes dfs-sm-logo-drop  { from { opacity: 0; transform: translateY( -12px ); } to { opacity: 1; transform: none; } }

@keyframes dfs-sm-logo-scale { from { opacity: 0; transform: scale( 0.9 ); }        to { opacity: 1; transform: none; } }


/* @decision D130 — entrance animates the clip box too, same reason. */
.dfs-peptide-floating-header--logo-in-fade .dfs-peptide-floating-header__logo,
.dfs-peptide-floating-header--logo-in-rise .dfs-peptide-floating-header__logo,
.dfs-peptide-floating-header--logo-in-drop .dfs-peptide-floating-header__logo,
.dfs-peptide-floating-header--logo-in-scale .dfs-peptide-floating-header__logo {
	transform-origin: left center;
	animation-duration: var( --dfs-sm-logo-entrance-duration, 0.6s );
	animation-delay: var( --dfs-sm-logo-entrance-delay, 0s );
	animation-timing-function: var( --dfs-sm-ease-out );
	animation-fill-mode: backwards;
	animation-iteration-count: 1;
}


.dfs-peptide-floating-header--logo-in-fade .dfs-peptide-floating-header__logo  { animation-name: dfs-sm-logo-fade; }

.dfs-peptide-floating-header--logo-in-rise .dfs-peptide-floating-header__logo  { animation-name: dfs-sm-logo-rise; }

.dfs-peptide-floating-header--logo-in-drop .dfs-peptide-floating-header__logo  { animation-name: dfs-sm-logo-drop; }

.dfs-peptide-floating-header--logo-in-scale .dfs-peptide-floating-header__logo { animation-name: dfs-sm-logo-scale; }


/*
 * @decision D126 — SVG DRAW-IN. Only reachable when the logo was actually
 * INLINED — these select inside `svg`, and a logo that failed any of the
 * sanitizer's checks is still an `<img>`, which none of this can match. So a
 * rejected file degrades to a normal logo rather than to an invisible one.
 */
@keyframes dfs-sm-logo-draw { to { stroke-dashoffset: 0; } }

@keyframes dfs-sm-logo-fill { to { fill-opacity: 1; } }


.dfs-peptide-floating-header--logo-draw .dfs-peptide-floating-header__logo-mark path,
.dfs-peptide-floating-header--logo-draw .dfs-peptide-floating-header__logo-mark polygon,
.dfs-peptide-floating-header--logo-draw .dfs-peptide-floating-header__logo-mark circle,
.dfs-peptide-floating-header--logo-draw .dfs-peptide-floating-header__logo-mark rect {
	stroke: currentColor;
	stroke-width: 1;
	stroke-dasharray: 1000;
	stroke-dashoffset: 1000;
	fill-opacity: 0;
	animation:
		dfs-sm-logo-draw var( --dfs-sm-logo-draw-duration, 1.6s ) var( --dfs-sm-ease-out ) forwards,
		dfs-sm-logo-fill var( --dfs-sm-logo-draw-duration, 1.6s ) var( --dfs-sm-ease-out ) forwards;
	animation-delay: var( --dfs-sm-logo-entrance-delay, 0s );
}


/*
 * REDUCED MOTION. All three families resolve to their FINISHED state, never to
 * nothing — a logo that does not draw in must still be a visible logo, which is
 * not what `fill-opacity: 0` would leave behind.
 */
@media ( prefers-reduced-motion: reduce ) and (dfs-reduced-motion-honoured) {
	.dfs-peptide-floating-header--logo-hover-grow .dfs-peptide-floating-header__logo-track,
	.dfs-peptide-floating-header--logo-hover-shrink .dfs-peptide-floating-header__logo-track,
	.dfs-peptide-floating-header--logo-hover-tilt .dfs-peptide-floating-header__logo-track,
	.dfs-peptide-floating-header--logo-hover-float .dfs-peptide-floating-header__logo-track,
	.dfs-peptide-floating-header--logo-hover-fade .dfs-peptide-floating-header__logo-track,
	.dfs-peptide-floating-header--logo-in-fade .dfs-peptide-floating-header__logo-track,
	.dfs-peptide-floating-header--logo-in-rise .dfs-peptide-floating-header__logo-track,
	.dfs-peptide-floating-header--logo-in-drop .dfs-peptide-floating-header__logo-track,
	.dfs-peptide-floating-header--logo-in-scale .dfs-peptide-floating-header__logo-track {
		animation: none;
		transition: none;
		transform: none;
		opacity: 1;
	}

	.dfs-peptide-floating-header--logo-draw .dfs-peptide-floating-header__logo-mark path,
	.dfs-peptide-floating-header--logo-draw .dfs-peptide-floating-header__logo-mark polygon,
	.dfs-peptide-floating-header--logo-draw .dfs-peptide-floating-header__logo-mark circle,
	.dfs-peptide-floating-header--logo-draw .dfs-peptide-floating-header__logo-mark rect {
		animation: none;
		stroke: none;
		stroke-dashoffset: 0;
		fill-opacity: 1;
	}
}


/*
 * @decision D123 — FULL-SCREEN PANEL BACKGROUND.
 *
 * `.dfs-peptide-floating-header__panel-wrap` is the width constraint; the surface colour is
 * painted on its child `.dfs-peptide-floating-header__panel`. So releasing the wrap to full
 * width is all it takes for the background to reach both edges.
 *
 * THE SECOND RULE IS THE ONE THAT MATTERS. The inner is handed the EXACT box
 * the wrap just gave up — same width calc, same max-width, same auto margins —
 * so the content it holds lands on the identical pixel. Compensating with
 * `padding-inline: margin + panel-pad` instead LOOKS equivalent and is wrong
 * the moment `--dfs-sm-max-width` binds, because the old box was centred
 * inside max-width rather than inset from the viewport.
 *
 * `max-width: none` is a valid value, which is what makes this safe when
 * `max_width` is 0 — `min()` would not have been.
 */
/*
 * @decision D131 — FULL SCREEN MEANS THE WHOLE VIEWPORT. Replaces the first
 * implementation, which was wrong.
 *
 * 3.25.0 read "edge to edge" as horizontal only and widened
 * `.dfs-peptide-floating-header__panel-wrap`. Brad: "the entire screen should change to the
 * menu panels color, with menu sitting on top ... important note the logo,
 * menu button and button on right all sit above the menus fullscreen color."
 *
 * THE BACKDROP IS ALREADY THAT SURFACE. `.dfs-peptide-floating-header__backdrop` is fixed,
 * covers the viewport, and is deliberately z-ordered UNDER the bar and the
 * panel while sitting above all page content. So the fill goes there and every
 * requirement falls out for free: the whole screen takes the panel colour, and
 * the logo, menu button and CTA are already painted above it.
 *
 * Nothing about the content box changes — the wrap keeps its width and the
 * inner keeps its padding — so "the content stays exactly where it is" is not
 * something this has to reproduce, it is something it never disturbs. That is
 * strictly better than the widen-and-compensate version it replaces.
 */
/*
 * @decision D144 — THE FULL-SCREEN SURFACE IS PAINTED ON THE MODIFIER, NEVER ON
 * `.is-open`. Fixes A11.
 *
 * Brad: "when you click close its buggy visually, it shows a flash of the
 * background bar, the panel box ect under it... it seems to just paste a full
 * color over rest of page which is what causes glitchy closing." That reading
 * of the mechanism is exactly right.
 *
 * These declarations used to hang off `.is-open`. Only `opacity` is in the
 * backdrop's transition list, so on close — the moment `.is-open` is removed —
 * `background-color` snapped from the panel surface back to the translucent
 * `rgba(0,0,0,0.3)` default AND `backdrop-filter: blur()` snapped back ON,
 * instantly, while the opacity fade and the panel's row-collapse were both
 * still running. The panel did the same thing in reverse, snapping from
 * `transparent` back to painting its own rectangle. Those three snaps ARE the
 * flash of "the background bar, the panel box, ect".
 *
 * D124 already wrote this rule down for the appearance options: "The closed
 * state is defined on the inner rather than on `.is-open`, or the panel would
 * animate IN correctly and snap OUT with no transition." The full-screen
 * surface was built the other way and reproduced the defect.
 *
 * So the paint moves to the MODIFIER class, which is present in both states.
 * Nothing about the surface changes when the menu opens or closes — only
 * `opacity` moves, and it is the one property that has always been animated.
 * There is no longer any property that CAN snap, which is a structural fix
 * rather than a retuned duration.
 */
.dfs-peptide-floating-header--panel-full .dfs-peptide-floating-header__backdrop {
	background-color: var( --dfs-sm-surface );
	-webkit-backdrop-filter: none;
	backdrop-filter: none;

	/*
	 * FULL-PAGE MEANS THE VIEWPORT, NOT THE HEADER'S BOX. The backdrop is
	 * `fixed; inset: 0`, but the root carries a transform (the hide-on-scroll
	 * travel), and a transformed ancestor is the containing block for fixed
	 * descendants — so `inset: 0` resolved to the HEADER's ~800px box and the
	 * page showed beneath the fill (found live under preset 2, D165).
	 * Viewport units do not resolve against the containing block, so sizing
	 * beats positioning here. The root sits at the viewport's top-left, so
	 * 0/0 inside its box IS the viewport corner.
	 */
	width: 100vw;
	/*
	 * The gap comes off HERE because this element is what actually covers the
	 * page — MEASURED with the panel open, the backdrop is the full viewport
	 * and opaque while the panel's own background is rgba(0,0,0,0). Default 0,
	 * so the height is unchanged until somebody asks for a gap.
	 */
	height: calc( 100vh - var( --dfs-sm-panel-gap, 0px ) );
	height: calc( 100lvh - var( --dfs-sm-panel-gap, 0px ) );

	/*
	 * SEPARATE CURVES IN AND OUT. A single symmetric fade is what makes an
	 * overlay read as a flat colour being pasted on and pulled off; easing out
	 * more slowly than in is most of what reads as "expensive". Both default to
	 * the existing backdrop timing, so an untouched widget renders exactly as
	 * it did.
	 */
	transition: opacity
		var( --dfs-sm-full-out-duration, var( --dfs-sm-backdrop-duration ) )
		var( --dfs-sm-full-out-ease, var( --dfs-sm-ease-out ) );
}


.dfs-peptide-floating-header--panel-full.is-open .dfs-peptide-floating-header__backdrop,
.dfs-peptide-floating-header--panel-full.is-preview-open .dfs-peptide-floating-header__backdrop {
	opacity: 1;
	transition: opacity
		var( --dfs-sm-full-in-duration, var( --dfs-sm-backdrop-duration ) )
		var( --dfs-sm-full-in-ease, var( --dfs-sm-ease-out ) );
}


/*
 * The panel's own surface would otherwise paint a second, differently sized
 * rectangle on top of the full-screen one — visible wherever a radius or the
 * container inset exposes an edge.
 *
 * Also on the modifier, not on `.is-open`, for the same reason: gated on the
 * open state it snapped back to its own colour mid-close. The panel is
 * collapsed to a zero-height grid row while closed, so a transparent
 * background there is invisible either way.
 */
.dfs-peptide-floating-header--panel-full .dfs-peptide-floating-header__panel {
	background-color: transparent;
}

/*
 * @decision D211 — THE CONTACT COLUMN MAY NOT OUTLIVE THE SURFACE (Brad,
 * Mode 2: the text "lingers after close, floating and cheap").
 *
 * Measured on the fixture, 33ms frames: on close the backdrop is at 0.05 by
 * ~360ms while the panel holds its full height to ~450ms and the contact
 * column sits at FULL opacity over the naked page until the row-collapse
 * clips it past 520ms. The nav links spend that hold retracting into their
 * masks — the contact column is the only thing left un-choreographed, which
 * is exactly why it reads as floating.
 *
 * Only the full-surface modes can show this (default mode closes behind an
 * opaque panel), so the fix is scoped to the modifier — and per D144's law
 * the closed state lives ON the modifier, so nothing can snap. Close: one
 * uniform 0.18s fade, no stagger (D195's law), gone before the backdrop
 * reaches half. Open: the same curve and delay the panel itself uses, so
 * the entrance is untouched. No new controls.
 */
.dfs-peptide-floating-header--panel-full .dfs-peptide-floating-header__contact {
	opacity: 0;
	transition: opacity 0.18s var( --dfs-sm-ease-out );
}

.dfs-peptide-floating-header--panel-full.is-open .dfs-peptide-floating-header__contact,
.dfs-peptide-floating-header--panel-full.is-preview-open .dfs-peptide-floating-header__contact {
	opacity: 1;
	transition: opacity var( --dfs-sm-expand-duration ) var( --dfs-sm-ease-out ) var( --dfs-sm-expand-delay );
}

/*
 * @decision D293 — NOTHING THE MENU OWNS MAY OUTLIVE ITS SURFACE. Brad's hard
 * close-state rule: "no part of the menu may be left visible after close,
 * ever." Generalises D211 from one child to the whole content layer.
 *
 * D211 fixed the contact column and its own measurement recorded, without
 * acting on it, that "the nav links spend that hold retracting into their
 * masks" — read at the time as choreographed, and it is not. The retract is a
 * 0.55s transform, so a link is still mid-travel and at FULL opacity long
 * after the surface has gone, and the cards were never in the exit at all.
 *
 * MEASURED, fixture and canary, 1920x940, panel-full, per animation frame:
 * the fill is under 1% of its peak by ~406ms while the two card images and
 * their captions still paint at opacity 1 for a further 66ms — 116ms with the
 * exit choreography on, where the nav links join them. That window of
 * photographs and nav text floating over the naked page IS the linger.
 *
 * ONE RULE, NOT ONE PER CHILD. `__panel-inner > *` is the whole content layer
 * — the grid (nav, contact, cards) and the clock line — so a child added
 * later cannot be forgotten the way the cards were. Deliberately NOT
 * `__panel-inner` itself: @decision D124's appearance options already own
 * that element's opacity, and two rules on one property at equal specificity
 * is decided by source order, which is not a mechanism anyone should have to
 * remember. On the children the two compose instead: an appear mode keeps its
 * designed fade and this multiplies into it.
 *
 * ASYMMETRIC BY CONSTRUCTION, per the panel's own two-transitions law — the
 * transition that runs is the one declared on the state being transitioned TO.
 * The base rule governs CLOSING and carries the fade; the open rule governs
 * OPENING and carries `0s`, so every child arrives exactly as it did before
 * this rule existed and gains no second opacity ramp. Opacity is exactly 1
 * while open and 0 while closed, so no stacking context persists in either
 * resting state.
 *
 * Scoped to the full-surface modes because only they can show it: in the
 * default mode the panel paints its own opaque surface and IS the last thing
 * to leave, which the gate confirms rather than assumes (`default` and `exit`
 * are green on the same instrument that reds the three fullscreen modes).
 */
.dfs-peptide-floating-header--panel-full .dfs-peptide-floating-header__panel-inner > * {
	opacity: 0;
	transition: opacity var( --dfs-sm-content-out ) var( --dfs-sm-ease-out );
}

.dfs-peptide-floating-header--panel-full.is-open .dfs-peptide-floating-header__panel-inner > *,
.dfs-peptide-floating-header--panel-full.is-preview-open .dfs-peptide-floating-header__panel-inner > * {
	opacity: 1;
	transition: opacity 0s;
}

/*
 * @decision D174 — THE EDITOR CANVAS PAINTS THE FULL-SCREEN SURFACE ON THE
 * PANEL. On the front end the fill is the BACKDROP; the editor hides
 * backdrops by design (`--editor .dfs-peptide-floating-header__backdrop{display:none}`),
 * and panel-full makes the panel itself transparent — so the open menu in
 * the canvas had NO surface at all: preset 2's nav floated over the template
 * in the wrong colours while live was perfect (Brad, 2026-08-13: "colors
 * shown wrong, overall buggier editor look and live looks fine"). Editor
 * only; the front end keeps the backdrop mechanism untouched.
 */
.dfs-peptide-floating-header--editor.dfs-peptide-floating-header--panel-full .dfs-peptide-floating-header__panel {
	background-color: var( --dfs-sm-surface );
}

/*
 * …and while the canvas panel is OPEN, the whole root wears the surface too.
 * Without this the bar renders as a surface-coloured pill floating on the
 * template background with a gap above the panel — two seams the live page
 * does not have, because there the full-viewport backdrop sits behind both.
 * Root background is the closest a relative, in-flow canvas widget can get
 * to a fixed full-bleed fill. Covers both open mechanisms: the preview
 * switch (is-preview-open) and a toggle click in the canvas (is-open).
 */
.dfs-peptide-floating-header--editor.dfs-peptide-floating-header--panel-full.is-preview-open,
.dfs-peptide-floating-header--editor.dfs-peptide-floating-header--panel-full.is-open {
	background-color: var( --dfs-sm-surface );
}


/*
 * @decision D124 — APPEARANCE OPTIONS, LAYERED OVER THE FITTED EXPAND.
 *
 * Each animates a property the grid-rows transition does not touch, so the D77
 * curve keeps running underneath exactly as measured. The closed state is
 * defined on the inner rather than on `.is-open`, or the panel would animate IN
 * correctly and snap OUT with no transition.
 */
.dfs-peptide-floating-header--appear-fade .dfs-peptide-floating-header__panel-inner,
.dfs-peptide-floating-header--appear-rise .dfs-peptide-floating-header__panel-inner,
.dfs-peptide-floating-header--appear-scale .dfs-peptide-floating-header__panel-inner {
	opacity: 0;
	transition:
		opacity var( --dfs-sm-expand-duration ) var( --dfs-sm-ease-out ) var( --dfs-sm-expand-delay ),
		transform var( --dfs-sm-expand-duration ) var( --dfs-sm-expand-ease ) var( --dfs-sm-expand-delay );
}


.dfs-peptide-floating-header--appear-rise .dfs-peptide-floating-header__panel-inner  { transform: translateY( 16px ); }

.dfs-peptide-floating-header--appear-scale .dfs-peptide-floating-header__panel-inner { transform: scale( 0.98 ); transform-origin: center top; }


.dfs-peptide-floating-header--appear-fade.is-open .dfs-peptide-floating-header__panel-inner,
.dfs-peptide-floating-header--appear-rise.is-open .dfs-peptide-floating-header__panel-inner,
.dfs-peptide-floating-header--appear-scale.is-open .dfs-peptide-floating-header__panel-inner,
.dfs-peptide-floating-header--appear-fade.is-preview-open .dfs-peptide-floating-header__panel-inner,
.dfs-peptide-floating-header--appear-rise.is-preview-open .dfs-peptide-floating-header__panel-inner,
.dfs-peptide-floating-header--appear-scale.is-preview-open .dfs-peptide-floating-header__panel-inner {
	opacity: 1;
	transform: none;
}


@media ( prefers-reduced-motion: reduce ) and (dfs-reduced-motion-honoured) {
	.dfs-peptide-floating-header--appear-fade .dfs-peptide-floating-header__panel-inner,
	.dfs-peptide-floating-header--appear-rise .dfs-peptide-floating-header__panel-inner,
	.dfs-peptide-floating-header--appear-scale .dfs-peptide-floating-header__panel-inner {
		transition: none;
		opacity: 1;
		transform: none;
	}
}


.dfs-peptide-floating-header__grid {
	display: grid;
	grid-template-columns: repeat( var( --dfs-sm-columns ), minmax( 0, 1fr ) );
	gap: var( --dfs-sm-gutter );
	row-gap: var( --dfs-sm-panel-row-gap );
}


.dfs-peptide-floating-header__nav,
.dfs-peptide-floating-header__contact { grid-column: span 12; }


/*
 * BOTH CARDS SHARE ONE WRAPPER, and it is hidden below the breakpoint.
 *
 * Their container is `grid-span-5 grid-start-8 hidden gap-16 opacity-0 lg:flex`
 * — columns 8 to 12, a flex ROW with two `flex-1` children, and `display: none`
 * under 1024. Placing the two cards individually at `7/span 3` and `10/span 3`
 * put them in the wrong columns AND left them visible on mobile, where their
 * panel is 639.6px and ours came out 1369.5px.
 */
.dfs-peptide-floating-header__cards {
	display: none;
	grid-column: 8 / span 5;
	gap: var( --dfs-sm-gutter );
}


@media ( min-width: 1024px ) {
	.dfs-peptide-floating-header__nav     { grid-column: span 4; }
	.dfs-peptide-floating-header__contact { grid-column: 5 / span 2; }
	.dfs-peptide-floating-header__cards   { display: flex; }
}


.dfs-peptide-floating-header__card { flex: 1 1 0; min-width: 0; }


/*
 * THE STATUS BLOCK — their "ACCEPTING PROJECTS / JOIN THE WAITLIST / ONLY 3
 * SPOTS LEFT", each with an 8x8 brand square beside it.
 *
 * margin-top: auto pins it to the bottom of the contact column, which is why it
 * sits level with the foot of the image cards on desktop and simply follows the
 * list on mobile. Those 8x8 squares are the brand-coloured elements visible in
 * their header at rest — not the nav marker, which is 32x32.
 */
.dfs-peptide-floating-header__status {
	margin-top: auto;
	display: flex;
	flex-direction: column;
	gap: 8px;
}


.dfs-peptide-floating-header__status-item {
	display: flex;
	align-items: flex-start;
	gap: 8px;
	margin: 0;
	font-size: var( --dfs-sm-caption-size );
	letter-spacing: var( --dfs-sm-label-tracking );
	text-transform: uppercase;
}


.dfs-peptide-floating-header__status-dot {
	display: inline-block;
	width: 8px;
	height: 8px;
	flex-shrink: 0;
	margin-top: 0.45em;
	background-color: var( --dfs-sm-brand );
}


.dfs-peptide-floating-header__nav {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 4px;
}


/*
 * @decision D86  THE TRAVELLING MARKER — Brad's "block moving around".
 *
 * A 32x32 brand square, position absolute left 0, that slides to whichever link
 * is hovered and rests beside the current page when nothing is. Measured on the
 * reference at 1440:
 *
 *   at rest, Home active   translateY 21.9
 *   hovering Pricing       translateY 187.0, rotate 0
 *   hovering Contact       translateY 347.7, rotate 90   <- matrix(0,1,-1,0,..)
 *
 * It ROTATES as it travels, a quarter turn per move. That is the same trick the
 * Scroll Steps marker uses, and the same one that went unmeasured there for a
 * whole build because the recorder sampled only its top offset.
 *
 * Hidden until the panel opens, or it sits in the corner of a closed header.
 */
.dfs-peptide-floating-header__nav-marker {
	position: absolute;
	left: 0;
	top: 0;
	width: var( --dfs-sm-marker-size );
	height: var( --dfs-sm-marker-size );
	background-color: var( --dfs-sm-marker-colour );
	pointer-events: none;
	opacity: 0;
	transform: translateY( 0 ) rotate( 0deg );
	transition:
		transform 0.5s var( --dfs-sm-ease-in-out-quart ),
		opacity var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out );
}


.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__nav-marker,
.dfs-peptide-floating-header--editor.is-preview-open .dfs-peptide-floating-header__nav-marker { opacity: 1; }


/*
 * THE MARKER STAYS ON MOBILE, and hiding it was wrong.
 *
 * It was hidden below 1024 on the reasoning that a phone has no hover, so a
 * square that can only sit on the current page is decoration. Brad pointed out
 * what that reasoning missed: on a phone the marker IS the current-page
 * indicator, and it is the only one — which is exactly why the reference's own
 * mobile menu shows its active item styled as though hovered.
 *
 * It scales down instead, because 32px beside 36px type is the whole cap height
 * of the word next to it.
 */
@media ( max-width: 1023px ) {
	.dfs-peptide-floating-header__nav a { --dfs-sm-nav-x: 0px; }
	.dfs-peptide-floating-header__nav a:hover,
	.dfs-peptide-floating-header__nav a[aria-current="page"] { --dfs-sm-nav-x: var( --dfs-sm-nav-indent ); }
}


/*
 * PER-LINK MASK. Each link sits in its own clipped box with a sliding inner
 * div, so the links can rise into view staggered when the panel opens.
 *
 * `overflow-y: clip` with `overflow-x: visible` — NOT `overflow: hidden`.
 * Hidden on both axes creates a scroll container that crops the focus wipe
 * where it extends past the text box. `clip` on one axis only is the reason
 * the reference can mask vertically and still paint the underline.
 */
.dfs-peptide-floating-header__nav-item {
	overflow-y: clip;
	overflow-x: visible;
	width: 100%;
}


/*
 * @decision D82  THE TRANSFORM IS ON THE LINK ITSELF, not on the wrapper.
 *
 * Their served markup puts an empty `<div style="transform: none">` around
 * each link, which is what this was built against — but a runtime probe 250ms
 * into an open shows the wrapper still at `none` and the `<a>` carrying
 * `matrix(1, 0, 0, 1, 0, 88)`. The markup describes where the transform COULD
 * go; only the running page says where it does.
 *
 * 88px at desktop against an 80px link, 53.76px at mobile against a 48.87px
 * link — 110% of the link's own height in both cases, which is why a percentage
 * is right here and a pixel constant would drift with the fluid type.
 */
/*
 * TWO AXES ON ONE TRANSFORM. The reveal drives Y, the hover drives X, and both
 * have to live in the same transform declaration or the later rule silently
 * wins and one of them stops working.
 */
.dfs-peptide-floating-header__nav a {
	--dfs-sm-nav-x: 0px;
	--dfs-sm-nav-y: 110%;
	transform: translate( var( --dfs-sm-nav-x ), var( --dfs-sm-nav-y ) );
	transition: transform 0.55s var( --dfs-sm-ease-in-out-quint ),
		color var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out );
}


.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__nav a { --dfs-sm-nav-y: 0%; }


.dfs-peptide-floating-header__nav a {
	display: block;
	padding-block: 4px;
	text-decoration: none;
	color: inherit;
	font-size: var( --dfs-sm-nav-size );
	font-weight: 500;
	line-height: var( --dfs-sm-nav-leading );
	letter-spacing: var( --dfs-sm-nav-tracking );
}


/*
 * @decision D87  THE CURRENT PAGE IS MARKED, and it looks like a hovered item on purpose.
 *
 * Brad, on the mobile menu: "the first item in the list ... has an active hover
 * effect applied", then corrected himself — it is the ACTIVE PAGE, which
 * happens to be Home on their homepage. Measured: brand colour AND indented,
 * x=148.5 against 96 at desktop, 49.4 against 40 on mobile.
 *
 * So the active state and the hover state ARE the same treatment, which is
 * exactly why one reads as the other.
 */
.dfs-peptide-floating-header__nav a { color: var( --dfs-sm-nav-fg ); }


/*
 * @decision D139 — CURRENT PAGE READS ITS OWN VARIABLE. Fixes A4.
 *
 * These four selectors were ONE rule, and it is `!important`. `nav_colour_current`
 * writes `color: {{VALUE}}` through Elementor `selectors`, which never carries
 * `!important` — so the control was unreachable for exactly the reason A1 was,
 * and the current page always measured the active colour. Same bug class, same
 * fix: read a variable inside the declaration that already wins.
 *
 * THE SPLIT IS ORDERED, AND THE ORDER IS LOAD-BEARING. Both selectors weigh
 * (0,2,1), so equal specificity means source order decides. Current page goes
 * FIRST and hover/focus SECOND, which keeps "HOVER SUPERSEDES ACTIVE" — the
 * behaviour the block below this one documents and the marker JS depends on —
 * true even when a distinct current-page colour is set. Appending the new rule
 * instead of splitting would have inverted it and lit two links at once.
 *
 * The fallback keeps the default render byte-identical: unset resolves to
 * `--dfs-sm-nav-fg-active`, which is what the single rule did.
 */
.dfs-peptide-floating-header__nav .current-menu-item > a,
.dfs-peptide-floating-header__nav a[aria-current="page"] {
	color: var( --dfs-sm-nav-fg-current, var( --dfs-sm-nav-fg-active ) ) !important;
	--dfs-sm-nav-x: var( --dfs-sm-nav-indent );
}


.dfs-peptide-floating-header__nav a:hover,
.dfs-peptide-floating-header__nav a:focus-visible {
	color: var( --dfs-sm-nav-fg-active ) !important;
	--dfs-sm-nav-x: var( --dfs-sm-nav-indent );
}


/*
 * HOVER SUPERSEDES ACTIVE — exactly one link is ever lit.
 *
 * The active page and a hovered link get the same treatment, which is the
 * reference's design and is why one reads as the other. What that leaves out is
 * what happens when they are DIFFERENT links: Home stayed orange and indented
 * while another item was hovered, so two items were lit at once and the marker
 * sat beside only one of them. Brad: "HOME stays orange and inlined from where
 * the marker was, it should go back like all other menu items do".
 *
 * So while the LIST is hovered, any current-page link that is not itself the
 * hovered one drops back to the inactive treatment. Scoped to `.__nav:hover`
 * rather than to each link, because the question is "is something else in this
 * list being pointed at", which no single link can answer about itself.
 *
 * `:not(:hover)` and not a JS class: the state is exactly "hovered", the
 * browser already tracks it, and a class would have to be added and removed on
 * every enter and leave with the marker's own handlers already doing that work.
 */
.dfs-peptide-floating-header__nav:hover a[aria-current="page"]:not(:hover),
.dfs-peptide-floating-header__nav:focus-within a[aria-current="page"]:not(:focus-visible) {
	color: var( --dfs-sm-nav-fg ) !important;
	--dfs-sm-nav-x: 0px;
}


/*
 * A hover must not wait for the open stagger. That delay is
 * expand-delay + i * 0.06s, which on the fifth link is nearly half a second —
 * long enough that the indent feels broken rather than eased.
 */
.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__nav a:hover,
.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__nav a:focus-visible { transition-delay: 0s; }


/*
 * FOCUS IS A RULE WIPE FROM THE LEFT EDGE, NOT A RING.
 *
 * Two hairlines, one retracting to the right while the other grows from the
 * left with a 300ms delay, on a 700ms cubic-bezier(.625,.05,0,1). Their
 * `group-focus-visible:` variants declare exactly this and no focus ring at
 * all. An earlier ledger entry recorded focus as "unverified, add our own".
 */
.dfs-peptide-floating-header__wipe {
	pointer-events: none;
	position: absolute;
	inset-inline: 0;
	bottom: -1px;
}


.dfs-peptide-floating-header__wipe span {
	position: absolute;
	inset-inline: 0;
	top: 0;
	height: 1px;
	background-color: currentColor;
	transition: transform 0.7s cubic-bezier( 0.625, 0.05, 0, 1 );
}


.dfs-peptide-floating-header__wipe span:first-child {
	transform-origin: left;
	transform: scaleX( 1 );
	transition-delay: 0.3s;
}


.dfs-peptide-floating-header__wipe span:last-child {
	transform-origin: right;
	transform: scaleX( 0 );
	transition-delay: 0s;
}


.dfs-peptide-floating-header__wipe-host { position: relative; width: fit-content; display: block; }


.dfs-peptide-floating-header__wipe-host:focus-visible { outline: none; }


.dfs-peptide-floating-header__wipe-host:focus-visible .dfs-peptide-floating-header__wipe span:first-child {
	transform-origin: right;
	transform: scaleX( 0 );
	transition-delay: 0s;
}


.dfs-peptide-floating-header__wipe-host:focus-visible .dfs-peptide-floating-header__wipe span:last-child {
	transform-origin: left;
	transform: scaleX( 1 );
	transition-delay: 0.3s;
}


.dfs-peptide-floating-header__contact {
	color: var( --dfs-sm-contact-fg );
	display: flex;
	flex-direction: column;
	gap: 24px;
	font-size: var( --dfs-sm-body-sm-size );
	line-height: var( --dfs-sm-body-sm-leading );
}


@media ( min-width: 1024px ) {
	.dfs-peptide-floating-header__contact { font-size: var( --dfs-sm-body-size ); }
}


.dfs-peptide-floating-header__contact-group { display: flex; flex-direction: column; gap: 4px; }


.dfs-peptide-floating-header__contact-line { margin: 0; }

/* D210 — rich lines: a pasted list keeps the column's rhythm, not the
 * browser's. The li rows already wear the line class, so type and hover
 * arrive from the rules below; only the box needs taming here. */
.dfs-peptide-floating-header__contact-group ul,
.dfs-peptide-floating-header__contact-group ol { margin: 0; padding-left: 1.1em; }

/*
 * @decision D168 — a linked line is an <a> wearing the same class, so every
 * colour/typography rule above applies unchanged; only the display needs
 * restating, because an anchor is inline where the <p> was block. The hover
 * colour comes from the existing `.dfs-peptide-floating-header__contact a:hover` rule.
 */
a.dfs-peptide-floating-header__contact-line { display: block; }


.dfs-peptide-floating-header__contact-label {
	margin: 0;
	color: var( --dfs-sm-muted );
	font-size: var( --dfs-sm-label-size );
	transition: color var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out );
}


/*
 * @decision D88  EVERY LINE IN THE CONTACT COLUMN SWAPS COLOUR ON HOVER, links or not.
 *
 * Brad asked for the whole middle column to respond, defaulting to the accent
 * the CTA already uses. Applied to the line and the label rather than only to
 * anchors, because half the column is plain text — an address, a status, a
 * heading — and a column where only some rows react reads as broken rows
 * rather than as a deliberate distinction.
 */
.dfs-peptide-floating-header__contact a { color: inherit; text-decoration: none; }


.dfs-peptide-floating-header__contact-line,
.dfs-peptide-floating-header__contact-label,
.dfs-peptide-floating-header__contact a,
.dfs-peptide-floating-header__status-item {
	transition: color var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out );
}


/*
 * EVERY LINE GROWS SLIGHTLY AS WELL AS RECOLOURING, at Brad's request.
 *
 * 1.03, and the scale origin is the LEFT edge — scaling from the centre pushes
 * a left-aligned line outwards in both directions, so the text appears to drift
 * left as it grows. 'transform-origin: left' keeps the line anchored where it
 * started and the growth reads as emphasis rather than movement.
 *
 * Labels grow too. Brad asked for the titles in that column to respond as well,
 * and a column where the headings sit inert while their lines react looks like
 * the headings are broken.
 */
.dfs-peptide-floating-header__contact-line,
.dfs-peptide-floating-header__contact-label,
.dfs-peptide-floating-header__contact a,
.dfs-peptide-floating-header__status-item {
	display: block;
	width: fit-content;
	transform-origin: left center;
	transform: scale( 1 );
	transition: color var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out ),
		transform var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out );
}


.dfs-peptide-floating-header__status-item { display: flex; }


.dfs-peptide-floating-header__contact-line:hover,
.dfs-peptide-floating-header__contact-label:hover,
.dfs-peptide-floating-header__contact a:hover,
.dfs-peptide-floating-header__status-item:hover {
	color: var( --dfs-sm-contact-hover, var( --dfs-sm-brand ) );
	transform: scale( var( --dfs-sm-contact-scale ) );
}


.dfs-peptide-floating-header__card { display: flex; flex-direction: column; gap: 8px; }


/*
 * @decision D81  4:5, NOT 3:2 — and this one number is worth 134px of panel height.
 *
 * Derived rather than eyeballed. At 1920 the reference panel measures 677.98
 * tall; strip its 64px padding top and bottom and the content is 550. The nav
 * column is 5 links x 80px plus 4 x 4px gaps = 416, so the nav is NOT what sets
 * the height — the image cards are. A card spans 3 of 12 columns inside a
 * 1728px content width with a 16px gutter, which is 420px wide, and 420 x 1.25
 * is 525 plus the caption row. That is 4:5.
 *
 * Built at 3:2 the panel came out 544 against their 678, and the whole
 * difference would have read as "panel height wrong" — one number, in a row
 * that names the wrong element, exactly the shape of defect that gets chased in
 * the padding for an hour.
 */
.dfs-peptide-floating-header__card-media {
	position: relative;
	display: block;
	overflow: hidden;
	/*
	 * D160 — this class is EXEMPT from the anchor shield so its border/radius/
	 * shadow controls can land, which also means a hostile theme's link rules
	 * reach it again. These four restate the shield's protection at NORMAL
	 * weight: enough to beat a theme's bare `a` rules on specificity, while
	 * Elementor's per-post control CSS (higher specificity, later in the
	 * document) still overrides them — which is the whole point.
	 */
	background: none;
	border: 0;
	box-shadow: none;
	text-decoration: none;
	/*
	 * 2:3, DERIVED — and it took three attempts, because the row that goes
	 * wrong says "panel height" while the cause is a child's aspect ratio.
	 *
	 * At 1920 the panel is 677.98 tall; strip 64px of padding top and bottom
	 * and the content is 550. Neither obvious candidate accounts for it: the
	 * nav column's content is 5 links x 80 plus 4 x 4 gaps = 416, the contact
	 * column is 232. Measuring the panel's children shows all three STRETCH to
	 * 550, so the tallest content wins — and it is the cards.
	 *
	 * Their card wrapper is columns 8-12 of a 1728px 12-column grid with a 16px
	 * gutter = 710.7px, holding two flex-1 children with a 16px gap, so each
	 * card is 347.3px wide. Solving 550 = media + 8 (gap) + 21 (caption at
	 * text-accent-sm 14px x 1.5) gives media = 521, and 521 / 347.3 = 1.5.
	 *
	 * 4:5 predicts 434 and leaves the panel 87px short; 3:2 predicts 231 and
	 * leaves it 300px short. Both were tried before the children were measured.
	 */
	aspect-ratio: 2 / 3;
}


.dfs-peptide-floating-header__card-media img,
.dfs-peptide-floating-header__card-media video,
.dfs-peptide-floating-header__card-media iframe {
	height: 100%;
	width: 100%;
	object-fit: cover;
	display: block;
	border: 0;
	transition: transform 0.7s var( --dfs-sm-ease-in-out-quint );
}


.dfs-peptide-floating-header__card-media:hover video { transform: scale( 1.04 ); }


.dfs-peptide-floating-header__card-media:hover img { transform: scale( 1.04 ); }


/*
 * THE MASK REVEAL — the strongest effect in the suite, and it is not a fade.
 *
 * Two nested translates that cancel: the WINDOW slides its own height down to
 * zero while the media inside slides the exact inverse. Because they cancel,
 * the image never moves relative to the page — it is UNCOVERED. Building this
 * as an opacity fade looks approximately right at both endpoints and wrong in
 * every frame between, which is precisely what the Video Showcase measurement
 * found on sharebien: opacity held at exactly 1.00 across 141 sampled scroll
 * positions while three nested translateY did all the work.
 *
 * The counter-translate lives on a child of the clip, so 'overflow: hidden' on
 * the clip is what does the masking. Both must move by the SAME distance or the
 * image drifts, which reads as a parallax nobody asked for.
 */
.dfs-peptide-floating-header__card--mask .dfs-peptide-floating-header__card-media {
	--dfs-sm-reveal: 100%;
}


.dfs-peptide-floating-header__card--mask .dfs-peptide-floating-header__card-media > * {
	display: block;
	transform: translateY( var( --dfs-sm-reveal ) );
	transition: transform var( --dfs-sm-reveal-duration ) var( --dfs-sm-ease-in-out-quart );
	transition-delay: calc( var( --dfs-sm-expand-delay ) + var( --dfs-sm-card-i, 0 ) * var( --dfs-sm-reveal-stagger ) );
}


.dfs-peptide-floating-header__card--mask .dfs-peptide-floating-header__card-media img,
.dfs-peptide-floating-header__card--mask .dfs-peptide-floating-header__card-media video,
.dfs-peptide-floating-header__card--mask .dfs-peptide-floating-header__card-media iframe {
	transform: translateY( calc( -1 * var( --dfs-sm-reveal ) ) );
	transition: transform var( --dfs-sm-reveal-duration ) var( --dfs-sm-ease-in-out-quart );
	transition-delay: calc( var( --dfs-sm-expand-delay ) + var( --dfs-sm-card-i, 0 ) * var( --dfs-sm-reveal-stagger ) );
}


.is-open .dfs-peptide-floating-header__card--mask .dfs-peptide-floating-header__card-media,
.is-preview-open .dfs-peptide-floating-header__card--mask .dfs-peptide-floating-header__card-media {
	--dfs-sm-reveal: 0%;
}


/*
 * A CLIP STAGGER, as an alternative to the mask. Same stagger, different
 * mechanism: the card is wiped in from the bottom edge rather than uncovered.
 * They are mutually exclusive by construction — a card cannot be both masked
 * and clipped without the two transforms fighting — so the control is a SELECT
 * rather than two switches.
 */
.dfs-peptide-floating-header__card--clip .dfs-peptide-floating-header__card-media {
	clip-path: inset( 100% 0 0 0 );
	transition: clip-path var( --dfs-sm-reveal-duration ) var( --dfs-sm-ease-in-out-quart );
	transition-delay: calc( var( --dfs-sm-expand-delay ) + var( --dfs-sm-card-i, 0 ) * var( --dfs-sm-reveal-stagger ) );
}


.is-open .dfs-peptide-floating-header__card--clip .dfs-peptide-floating-header__card-media,
.is-preview-open .dfs-peptide-floating-header__card--clip .dfs-peptide-floating-header__card-media {
	clip-path: inset( 0 0 0 0 );
}


/*
 * Uppercase, and brand on hover. Their caption's SOURCE text is "Featured
 * Project" in mixed case with the capitals coming from CSS, so the copy stays
 * readable in the editor while the render matches.
 */
.dfs-peptide-floating-header__card-caption {
	margin: 0;
	color: var( --dfs-sm-card-fg );
	font-size: var( --dfs-sm-caption-size );
	letter-spacing: var( --dfs-sm-label-tracking );
	text-transform: uppercase;
	transition: color var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out );
}


.dfs-peptide-floating-header__card:hover .dfs-peptide-floating-header__card-caption { color: var( --dfs-sm-card-fg-hover ); }


/* ----------------------------------------------------------- the backdrop */

/*
 * Inside the header, behind its own content. The header already carries
 * z-index 9999 and therefore its own stacking context, so `z-index: -1` here
 * puts the backdrop under the bar and the panel while still leaving it above
 * everything on the page. A sibling at 9998 would sit above page content but
 * could not be guaranteed under the bar at every theme.
 */
/*
 * @decision D114 — `.dfs-peptide-floating-header-page-scaled` was here and is removed with
 * its control. The constraint it was built around still holds for anything
 * added in its place: a transform on an ancestor creates a containing block,
 * so a `position: fixed` header inside a transformed element resolves against
 * that element rather than the viewport and stops being fixed. That is the
 * exact failure D71's portal exists to prevent.
 */

/*
 * A SLOW DRIFT ACROSS THE PANEL SURFACE.
 *
 * 24 seconds for one pass, and the gradient is a 4% lightness step over the
 * surface colour — at that speed and that contrast it is below the threshold
 * where the eye reads it as motion, which is the entire point. A drift anyone
 * can SEE moving is a distraction behind a navigation list.
 */
.dfs-peptide-floating-header--drift .dfs-peptide-floating-header__panel {
	background-image: linear-gradient(
		120deg,
		rgba( 255, 255, 255, 0 ) 0%,
		rgba( 255, 255, 255, 0.04 ) 45%,
		rgba( 255, 255, 255, 0 ) 90%
	);
	background-size: 250% 100%;
	animation: dfs-sm-drift var( --dfs-sm-drift-duration ) linear infinite;
}


@keyframes dfs-sm-drift {
	from { background-position: 0% 0; }
	to   { background-position: 250% 0; }
}


.dfs-peptide-floating-header__backdrop {
	position: fixed;
	inset: 0;
	/*
	 * The gap under the open menu. Declared AFTER `inset` so it overrides the
	 * shorthand's bottom — this element is sized by inset, not by height, so a
	 * height calc would not govern it. MEASURED: with the calc in the
	 * panel-full block instead, the overlay strip was correct at 180px while
	 * the backdrop stayed 884px and revealed nothing.
	 */
	bottom: var( --dfs-sm-panel-gap, 0px );
	z-index: -1;
	background-color: var( --dfs-sm-backdrop, rgba( 0, 0, 0, 0.3 ) );
	backdrop-filter: blur( var( --dfs-sm-backdrop-blur, 8px ) );
	-webkit-backdrop-filter: blur( var( --dfs-sm-backdrop-blur, 8px ) );
	opacity: 0;
	pointer-events: none;
	transition: opacity var( --dfs-sm-backdrop-duration ) var( --dfs-sm-ease-out );
}


.dfs-peptide-floating-header.is-open .dfs-peptide-floating-header__backdrop {
	opacity: 1;
	pointer-events: auto;
}


/* ---------------------------------------------------------- editor / a11y */

/*
 * @decision D70  The editor gets a static, laid-out header.
 *
 * Same reasoning as D01 on Scroll Steps: `position: fixed` inside the editor
 * canvas either escapes the frame or collapses to a strip that cannot be
 * selected, and a widget that cannot be clicked cannot be configured. The
 * editor render is `relative` and the panel can be forced open from the panel
 * so the alternate state is visible — D65 recorded the cost of an editor that
 * can only ever show one state.
 */
/*
 * @decision D93  THE EDITOR RENDER BREAKS OUT TO THE FULL CANVAS WIDTH.
 *
 * @decision D70 makes the editor header `position: relative` so it can be
 * clicked, and that is still right — but relative means it sits inside the
 * section's content column, and Elementor's default column is 1140px. So the
 * editor drew a 1060px bar with ~365px of dead space each side while the front
 * end drew 1840px with 40px each side. Measured in the canvas at a 1789px
 * iframe: bar x=355 w=1060, container 1140.
 *
 * Brad: *"in your editor the widget is still confined to a fixed width"* and
 * *"even in editor is no longer full width"*. He is right, and it is the same
 * complaint @decision D65 answered for colour: an editor that can only show a
 * state the front end never has is not a preview, it is a second design nobody
 * ships.
 *
 * `50%` resolves against the containing block and `50vw` against the canvas, so
 * the negative margin is exactly the column's own inset — and it collapses to 0
 * by itself when the widget IS already in a full-width section, which is how it
 * should be used. Editor-scoped, so the front end cannot see any of it.
 */
.dfs-peptide-floating-header--editor {
	position: relative;
	transform: none;
	transition: none;
	width: var( --dfs-sm-editor-vw, 100vw );
	max-width: var( --dfs-sm-editor-vw, 100vw );
	margin-left: calc( 50% - var( --dfs-sm-editor-vw, 100vw ) / 2 );
	margin-right: calc( 50% - var( --dfs-sm-editor-vw, 100vw ) / 2 );
}


.dfs-peptide-floating-header--editor .dfs-peptide-floating-header__backdrop { display: none; }


.dfs-peptide-floating-header--editor.is-preview-open .dfs-peptide-floating-header__panel { grid-template-rows: 1fr; }

.dfs-peptide-floating-header--editor.is-preview-open .dfs-peptide-floating-header__nav a { --dfs-sm-nav-y: 0%; }


/*
 * NOTHING MAY ANIMATE UNDER `reduce`. The panel still opens and closes — it
 * has to, it is the navigation — it simply arrives instantly.
 */
@media ( prefers-reduced-motion: reduce ) and (dfs-reduced-motion-honoured) {
	.dfs-peptide-floating-header,
	.dfs-peptide-floating-header *,
	.dfs-peptide-floating-header *::before,
	.dfs-peptide-floating-header *::after {
		transition-duration: 0.01ms !important;
		transition-delay: 0ms !important;
		animation-duration: 0.01ms !important;
	}
}


/*
 * Degrade sanely with no JS: the header is a plain inset bar at the top of the
 * page and the panel is reachable, because the toggle is a real <button> and
 * `is-open` is the only thing the JS adds. Without JS there is no ramp and no
 * hide/show, which are enhancements rather than the navigation itself.
 */
/*
 * BIGGER TAP TARGETS, MOBILE ONLY — a pseudo-element, never padding.
 *
 * Padding would grow the box and move everything below it; a positioned
 * ::after grows only what the finger has to find. Nothing visible changes,
 * which is the entire requirement: the ink stays where it was designed and the
 * target reaches the 44px a thumb actually needs.
 */
@media ( max-width: 1023px ) {
	.dfs-peptide-floating-header--touch .dfs-peptide-floating-header__toggle,
	.dfs-peptide-floating-header--touch .dfs-peptide-floating-header__nav a,
	.dfs-peptide-floating-header--touch .dfs-peptide-floating-header__contact a,
	.dfs-peptide-floating-header--touch .dfs-peptide-floating-header__logo { position: relative; }

	.dfs-peptide-floating-header--touch .dfs-peptide-floating-header__toggle::after,
	.dfs-peptide-floating-header--touch .dfs-peptide-floating-header__nav a::after,
	.dfs-peptide-floating-header--touch .dfs-peptide-floating-header__contact a::after,
	.dfs-peptide-floating-header--touch .dfs-peptide-floating-header__logo::after {
		content: "";
		position: absolute;
		left: 0;
		right: 0;
		top: 50%;
		min-height: 44px;
		height: 100%;
		transform: translateY( -50% );
	}
}


/*
 * A FADE WHERE THE PANEL SCROLLS.
 *
 * The panel caps to the window and scrolls internally, and a list that simply
 * stops at the bottom edge looks FINISHED rather than continued. The mask is
 * applied to the clip itself so it moves with the scroll, and it is only ever
 * present when the panel is open — a permanent gradient over a closed panel
 * would soften the bar's own bottom edge.
 */
.dfs-peptide-floating-header--fade.is-open .dfs-peptide-floating-header__panel-clip {
	-webkit-mask-image: linear-gradient( to bottom, #000 calc( 100% - 48px ), transparent 100% );
	mask-image: linear-gradient( to bottom, #000 calc( 100% - 48px ), transparent 100% );
}


/*
 * DEPTH — the panel casts a real shadow as it opens.
 *
 * It currently sits flat on the page: the backdrop darkens what is behind, but
 * nothing says the panel is ABOVE it rather than painted onto it. The shadow
 * grows with the panel rather than appearing at the end, so the depth arrives
 * with the movement instead of being announced after it.
 */
.dfs-peptide-floating-header--depth .dfs-peptide-floating-header__panel {
	box-shadow: none;
	transition: grid-template-rows var( --dfs-sm-expand-duration ) var( --dfs-sm-expand-ease ) var( --dfs-sm-expand-delay ),
		box-shadow var( --dfs-sm-expand-duration ) var( --dfs-sm-ease-out ) var( --dfs-sm-expand-delay ),
		background-color var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out );
}


.dfs-peptide-floating-header--depth.is-open .dfs-peptide-floating-header__panel,
.dfs-peptide-floating-header--depth.is-preview-open .dfs-peptide-floating-header__panel {
	box-shadow: var( --dfs-sm-panel-shadow );
}


/*
 * THE UNHOVERED LINKS BLUR BACK.
 *
 * A strong effect and deliberately off by default — used on a list of five it
 * reads as focus, used on everything it reads as a gimmick. Applied to the
 * SIBLINGS via the list's hover state rather than to the hovered item, because
 * what changes is everything the pointer is NOT on.
 *
 * Blur is expensive to composite, so it is paired with a short duration and
 * lives on the link rather than the row: blurring a container would blur the
 * travelling marker with it.
 */
.dfs-peptide-floating-header--blur .dfs-peptide-floating-header__nav:hover a:not( :hover ) {
	filter: blur( var( --dfs-sm-sibling-blur ) );
	opacity: 0.55;
}


.dfs-peptide-floating-header--blur .dfs-peptide-floating-header__nav a {
	transition: transform 0.55s var( --dfs-sm-ease-in-out-quint ),
		color var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out ),
		filter 0.25s var( --dfs-sm-ease-out ),
		opacity 0.25s var( --dfs-sm-ease-out );
}


/*
 * GRAIN. The cheapest premium trick there is, and it only works when it is
 * almost invisible — 5% over a flat surface. An inline SVG turbulence rather
 * than a bitmap: no request, no cache, and it scales to any panel size.
 *
 * pointer-events: none, or it eats every click in the panel.
 */
.dfs-peptide-floating-header--grain .dfs-peptide-floating-header__panel-inner { position: relative; }


.dfs-peptide-floating-header--grain .dfs-peptide-floating-header__panel-inner::before {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	opacity: var( --dfs-sm-grain-opacity );
	background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E" );
}


/*
 * CAPTIONS ARRIVE AFTER THEIR IMAGES.
 *
 * Together they read as one block sliding in; apart, the image lands and the
 * name follows, which is how a caption behaves in print. One delay, on top of
 * whatever reveal the card is already using.
 */
.dfs-peptide-floating-header--caption-stagger .dfs-peptide-floating-header__card-caption {
	opacity: 0;
	transform: translateY( 8px );
	transition: opacity 0.5s var( --dfs-sm-ease-out ), transform 0.5s var( --dfs-sm-ease-out ),
		color var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out );
	transition-delay: calc( var( --dfs-sm-expand-delay ) + var( --dfs-sm-caption-delay ) + var( --dfs-sm-card-i, 0 ) * var( --dfs-sm-reveal-stagger ) );
}


.dfs-peptide-floating-header--caption-stagger.is-open .dfs-peptide-floating-header__card-caption,
.dfs-peptide-floating-header--caption-stagger.is-preview-open .dfs-peptide-floating-header__card-caption {
	opacity: 1;
	transform: translateY( 0 );
}


/*
 * THE CHARACTER RISE. Each character is its own inline-block so it can be
 * transformed; the link keeps its own translate for the reveal and the indent,
 * so this is a SECOND transform one level down rather than a third axis on the
 * same one.
 *
 * The stagger is per character on top of the per-link stagger, which is why the
 * increment is small — 18ms across an eight-letter word is 126ms, and anything
 * larger reads as the word assembling rather than arriving.
 */
.dfs-peptide-floating-header__char {
	display: inline-block;
	transform: translateY( 110% );
	transition: transform 0.6s var( --dfs-sm-ease-in-out-quart );
	transition-delay: calc(
		var( --dfs-sm-expand-delay ) +
		var( --dfs-sm-i, 0 ) * 0.06s +
		var( --dfs-sm-char-i, 0 ) * 0.018s
	);
}


.is-open .dfs-peptide-floating-header__char,
.is-preview-open .dfs-peptide-floating-header__char { transform: translateY( 0 ); }


/*
 * With characters doing the rising, the LINK must not also rise — two
 * translates on the same axis would double the travel and the word would start
 * a full line lower than its mask.
 */
.dfs-peptide-floating-header--char-rise .dfs-peptide-floating-header__nav a { --dfs-sm-nav-y: 0%; }


.dfs-peptide-floating-header__sr {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect( 0, 0, 0, 0 );
	white-space: nowrap;
	border: 0;
}


/* ===================================================== 3.75.0 ADDITIONS ==
 * @decision D369 — SITE MENU MODE 7, "FULLSCREEN STEP LOADED".
 *
 * Measured off https://amiable-instance-782682.framer.app/ at 1920x940,
 * 1024x800 and 390x844, every frame of the open AND the close, plus every
 * hover. Spec: widget-suite/MEASURED-SPEC-stepload.md and its addendum.
 *
 * FOUR NUMBERS ARE THE MODE:
 *   stagger 150ms · travel 100px up · power3.out · ~520ms per item
 * and OPACITY STAYS 1.0 THE WHOLE WAY. The items do not fade; it is pure
 * translate under D82's existing per-link clip. Building this as a fade-up
 * would look plausible and be a different mechanism.
 *
 * THE EASE IS THE REAL power3.out TWIN, AND THAT IS DELIBERATE.
 * cubic-bezier(0.165,0.84,0.44,1) won a re-fit against 11 candidates on the raw
 * frames: summed rms 11.35 desktop / 11.89 tablet, against 30.94 / 32.09 for the
 * house token cubic-bezier(0.215,0.61,0.355,1). D162 calls that house token
 * "power3.out stated as its cubic-bezier twin" and it is MISLABELLED — it is
 * easeOutCubic, which the fitter calls power2.out. It fits 2.7x worse here, so
 * this mode ships the correct curve. Whether card-fan / repeater / transitions
 * were also meant to be quart is a separate question and is NOT answered by
 * editing them from inside this cut.
 */
.dfs-peptide-floating-header--nav-stepload {
	/* The measured curve, named once so every rule below reads from one owner. */
	--dfs-sm-step-ease: cubic-bezier( 0.165, 0.84, 0.44, 1 );
	--dfs-sm-step-travel: 100px;
	--dfs-sm-step-stagger: 150ms;
	--dfs-sm-step-lead: 430ms;
	--dfs-sm-step-duration: 520ms;

	/*
	 * The close is TIGHTER, not a mirror — measured median end-gap 50ms against
	 * the open's 100ms. Halving the stagger reproduces that; it is derived from
	 * the end-gaps rather than measured as a start-gap, and is labelled so
	 * nobody later "corrects" it to 150ms for symmetry.
	 */
	--dfs-sm-step-stagger-close: 75ms;

	/*
	 * The overlay: a top-down clip wipe, 785ms, on the in-out-quart token. Both
	 * halves are machinery we already had — D189's appear-wipe IS this
	 * mechanism. Only the duration and the ease differ from its defaults.
	 */
	--dfs-sm-expand-duration: 0.785s;
	--dfs-sm-appear-ease: var( --dfs-sm-ease-in-out-quart );

	/* Row pitch: 104.75px desktop, 57px at 390. Measured, not chosen. */
	--dfs-sm-step-pitch: 104.75px;
	/* Number column at x60 w40, label at x120 -> a 60px gutter after the number. */
	--dfs-sm-step-num-col: 40px;
	--dfs-sm-step-num-gap: 20px;
	/* The number sits 36px BELOW the label's top edge (12px at 390). */
	--dfs-sm-step-num-drop: 36px;
}

/*
 * THE TRAVEL IS PIXELS, NOT D82's 110%.
 *
 * D82 measured a DIFFERENT reference whose links rise 110% of their own box, so
 * the percentage was right there and drifts correctly with fluid type. This
 * reference is a flat 100px at every viewport — identical at 1920, 1024 and 390
 * — so a percentage would be wrong by construction here, most visibly on mobile
 * where the label box is half the height.
 *
 * `--dfs-sm-nav-y` is D82's own variable and the transform that reads it stays
 * exactly where D82 put it: on the `<a>`. That placement is load-bearing twice
 * over now — see the numbers rule below.
 */
.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__nav a {
	--dfs-sm-nav-y: var( --dfs-sm-step-travel );
	transition:
		transform var( --dfs-sm-step-duration ) var( --dfs-sm-step-ease ),
		opacity 0.25s var( --dfs-sm-ease-out ),
		color var( --dfs-sm-colour-duration ) var( --dfs-sm-ease-out );
	transition-delay: calc( var( --dfs-sm-i, 0 ) * var( --dfs-sm-step-stagger-close ) ), 0s, 0s;
}

.dfs-peptide-floating-header--nav-stepload.is-open .dfs-peptide-floating-header__nav a,
.dfs-peptide-floating-header--editor.dfs-peptide-floating-header--nav-stepload.is-preview-open .dfs-peptide-floating-header__nav a {
	--dfs-sm-nav-y: 0px;
	/* Lead-in + per-item stagger. The lead-in is NOT a hold waiting for the
	 * panel: the wipe is still 33% short when the first item departs at 436ms,
	 * so the two deliberately overlap. */
	transition-delay: calc(
		var( --dfs-sm-step-lead ) + var( --dfs-sm-i, 0 ) * var( --dfs-sm-step-stagger )
	), 0s, 0s;
}

/*
 * THE STEP NUMBERS DO NOT MOVE. AT ALL.
 *
 * Measured: static at their final y from frame 0, at opacity 0.5, while the
 * label rises past them. `01` sits at y246 on frame 0 and y246 on the last
 * frame; `WORK` goes 310 -> 210.
 *
 * This is why D82's transform placement matters a second time. `__nav-num` is a
 * SIBLING of the `<a>` inside `__nav-slide`, and the transform is on the `<a>`,
 * so the number already stays put and the label already moves alone. Hoisting
 * the transform up to `__nav-slide` "so the row moves together" is the change
 * someone will reach for; it would break this mode while passing every gate.
 *
 * The grid gives the two their measured columns: number at x60 in a 40px column,
 * label at x120.
 */
.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__nav-slide {
	display: grid;
	grid-template-columns: var( --dfs-sm-step-num-col ) 1fr;
	column-gap: var( --dfs-sm-step-num-gap );
	align-items: start;
}

.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__nav-num {
	/* Reset D184's inline/superscript treatment — this mode gives the number its
	 * own grid column, so vertical-align has nothing to do here. */
	display: block;
	vertical-align: baseline;
	margin-right: 0;
	padding-top: var( --dfs-sm-step-num-drop );

	/*
	 * Geist Mono 25px/600. THE REFERENCE GIVES THESE NO FAMILY AT ALL — they
	 * compute to the browser default sans while everything around them is
	 * branded, which is a reference defect rather than a design. Rule 2 says
	 * reproduce, so this is a deliberate, recorded deviation: shipping a system
	 * font next to Geist in a luxury menu fails the brand bar. `--dfs-sm-mono`
	 * already names "Geist Mono" first, so this is the house convention for
	 * numbers rather than a new decision.
	 */
	font-family: var( --dfs-sm-mono );
	font-size: 25px;
	font-weight: 600;
	letter-spacing: normal;

	/*
	 * 0.5 OPACITY, NOT A GREY. Measured: colour is rgb(255,255,255) and the
	 * numbers read grey only because they sit at half opacity. Setting a grey
	 * here would look right at rest and be wrong the moment the panel surface
	 * changes.
	 */
	color: var( --dfs-sm-nav-fg, #FFFFFF );
	opacity: 0.5;
}

/* Row pitch is set on the item, so it holds whether or not numbering is on. */
.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__nav-item {
	min-height: var( --dfs-sm-step-pitch );
}

/*
 * HOVER DOES TWO THINGS AT ONCE, AND THE SECOND IS THE HALF THAT GETS MISSED.
 *
 *   1. the hovered label shifts +12px right (x 120 -> 132)
 *   2. EVERY OTHER item drops to opacity 0.5; the hovered one stays at 1.0
 *
 * The dimming lives on the SIBLINGS, not on the hovered element, so a
 * selector-scoped `:hover` rule reproduces only half of it. `:has()` puts the
 * state on the nav and lets the siblings read it.
 *
 * Step numbers have no hover state of their own and must keep their 0.5 — they
 * are excluded rather than left to inherit, because inheriting the dim would
 * take them to 0.25 and read as a bug.
 */
.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__nav a:hover,
.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__nav a:focus-visible {
	--dfs-sm-nav-x: 12px;
}

@supports selector( :has( * ) ) {
	.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__nav:has( a:hover ) a:not( :hover ),
	.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__nav:has( a:focus-visible ) a:not( :focus-visible ) {
		opacity: 0.5;
	}
}

/*
 * THE SIDE COLUMN RIDES THE SAME TIMELINE, INDEX-PAIRED.
 *
 * Measured element by element: WORK+TWITTER both depart at 436ms,
 * SERVICES+FACEBOOK at 585ms, ABOUT+INSTAGRAM at 735ms. The 16ms on the fourth
 * pair is one rAF tick, not a second timeline. Same 100px, same easing.
 *
 * ONE index space drives both columns (render() writes the same `--dfs-sm-i`
 * across contact lines in document order), so a five-item nav and a four-item
 * side column stay in step and item five simply arrives alone — exactly what
 * the reference does with BLOG. Two independent staggers would reproduce the
 * look today and drift the moment either list changes length.
 *
 * The clip is on the GROUP: lines fly 100px below their rest position, which is
 * ~3 row pitches, so unclipped they would cross the rows below. They never
 * overlap each other in flight — the stagger spreads them further apart than
 * their 31.3px resting pitch and they converge on arrival.
 */
.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__contact-group {
	overflow: clip;
	gap: 0;
}

.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__contact-line {
	/* 31.3px measured pitch, at both viewports. */
	line-height: 31.3px;
	transform: translateY( var( --dfs-sm-step-travel ) );
	transition: transform var( --dfs-sm-step-duration ) var( --dfs-sm-step-ease );
	transition-delay: calc( var( --dfs-sm-i, 0 ) * var( --dfs-sm-step-stagger-close ) );
}

.dfs-peptide-floating-header--nav-stepload.is-open .dfs-peptide-floating-header__contact-line,
.dfs-peptide-floating-header--editor.dfs-peptide-floating-header--nav-stepload.is-preview-open .dfs-peptide-floating-header__contact-line {
	transform: translateY( 0 );
	transition-delay: calc(
		var( --dfs-sm-step-lead ) + var( --dfs-sm-i, 0 ) * var( --dfs-sm-step-stagger )
	);
}

/* The container inset: 60px desktop, 32px at 390 — the number column's own x. */
@media ( min-width: 1024px ) {
	.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__panel-inner {
		padding-left: 60px;
	}
	/* Right-aligned, right edge at vw - 60. */
	.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__contact {
		text-align: right;
		padding-right: 60px;
	}
}

/*
 * MOBILE CHANGES THE SIDE COLUMN'S ANCHOR, and that is a layout change rather
 * than a reflow.
 *
 * Desktop: right-aligned, right edge vw-60, y 372->466 — beside the nav.
 * 390:     LEFT-aligned at x32, BELOW the nav, y 484->578.
 *
 * So it is not the same column narrowing; it moves to a different place in the
 * reading order and has to be built as such. The nav grid already stacks below
 * 1024, which puts the contact column after the nav — the change here is the
 * alignment and the inset.
 */
@media ( max-width: 1023px ) {
	.dfs-peptide-floating-header--nav-stepload {
		--dfs-sm-step-pitch: 57px;
		--dfs-sm-step-num-drop: 12px;
	}
	.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__panel-inner {
		padding-left: 32px;
	}
	/* Number at x32, label at x92 -> a 28px gutter after a 32px column. */
	.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__nav-slide {
		grid-template-columns: 32px 1fr;
		column-gap: 28px;
	}
	.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__contact {
		text-align: left;
		padding-right: 0;
		padding-left: 0;
	}
}

/*
 * REDUCED MOTION. The mode's whole content is a translate, so honouring the
 * setting means arriving with no travel and no stagger — NOT a faded arrival,
 * which would substitute one animation for another. Opacity is already 1
 * throughout, so there is nothing to unfade; the numbers keep their designed
 * 0.5 because that is a static design value, not motion.
 */
@media ( prefers-reduced-motion: reduce ) and (dfs-reduced-motion-honoured) {
	.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__nav a,
	.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__contact-line {
		transition: none;
		transition-delay: 0s;
	}
	.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__contact-line {
		transform: none;
	}
	.dfs-peptide-floating-header--nav-stepload .dfs-peptide-floating-header__nav a { --dfs-sm-nav-y: 0px; }
}

/* AVIF wrapper (dfs-core-performance avif-picture.php): <img> arrives as <picture class="dfs-avif"><source><img></picture>. picture is display:contents, but its <source>
 * can compute to a non-none display and then becomes a flex/grid item (gap, grid cell). Hide it; the browser still picks the AVIF from srcset. */
.dfs-peptide-floating-header picture[class="dfs-avif"] > source { display: none; }


/*
 * 5.207.0-co64 — Content > Mode > Hamburger & Menu Animation (DFS Menu FX), Modes 1-7.
 *
 * .dfs-peptide-floating-header--mfx (a Hamburger Icon or Icon Animation other than "As now"): three
 * lines, each as wide as its shape says and lined up on the shape's side. They are
 * spaced in LAYOUT (top/bottom with auto margins), not by `transform` or `translate`:
 * GSAP 3.12+ folds the CSS `translate` into its own y, so the engine's y:0 moved the
 * outer lines to the centre and the X landed as a chevron (co64). Layout spacing is
 * invisible to GSAP, so y = +/- the spacing meets in the box centre at any line height.
 */
.dfs-peptide-floating-header.dfs-peptide-floating-header--mfx .dfs-peptide-floating-header__burger span:nth-child( n ) {
	left: var( --mfx-l, 0 );
	right: var( --mfx-r, 0 );
	top: 0;
	bottom: 0;
	margin: auto;
	width: calc( 100% * var( --mfx-f1, 1 ) );
	transform: none;
	translate: none;
	transition: none;
}

.dfs-peptide-floating-header.dfs-peptide-floating-header--mfx .dfs-peptide-floating-header__burger span:nth-child( 1 ) { top: calc( -2 * var( --dfs-sm-burger-gap-offset, var( --dfs-sm-burger-offset, 3px ) ) ); }

.dfs-peptide-floating-header.dfs-peptide-floating-header--mfx .dfs-peptide-floating-header__burger span:nth-child( 2 ) { width: calc( 100% * var( --mfx-f2, 1 ) ); }

.dfs-peptide-floating-header.dfs-peptide-floating-header--mfx .dfs-peptide-floating-header__burger span:nth-child( 3 ) { width: calc( 100% * var( --mfx-f3, 1 ) ); bottom: calc( -2 * var( --dfs-sm-burger-gap-offset, var( --dfs-sm-burger-offset, 3px ) ) ); }

/* No GSAP on the page: the same X, by CSS. */
.dfs-peptide-floating-header.dfs-peptide-floating-header--mfx.is-open:not( .dfs-peptide-floating-header--gsap ) .dfs-peptide-floating-header__burger span:nth-child( 1 ) { width: 100%; top: 0; transform: rotate( 45deg ); }

.dfs-peptide-floating-header.dfs-peptide-floating-header--mfx.is-open:not( .dfs-peptide-floating-header--gsap ) .dfs-peptide-floating-header__burger span:nth-child( 3 ) { width: 100%; bottom: 0; transform: rotate( -45deg ); }

/* .dfs-peptide-floating-header--mfx-menu (a Menu Animation other than "As now"): GSAP moves each row, so the links' own rise stands down. */
.dfs-peptide-floating-header--mfx-menu .dfs-peptide-floating-header__nav a { --dfs-sm-nav-y: 0%; }

/* With GSAP the middle line is the timeline's to hide, not the three-line rule's. */
.dfs-peptide-floating-header.dfs-peptide-floating-header--mfx.dfs-peptide-floating-header--gsap .dfs-peptide-floating-header__burger span:nth-child( 2 ) { opacity: 1; }

/* ==========================================================================
   Mode 1 - the Figma frame (162:4071). Own root class: nothing above reaches it.
   Every value is the frame's (SPEC.md) and is also the default of a control.
   The ground colour is the Elementor section's: the widget ships transparent.
   ========================================================================== */
.dfs-peptide-floating-header-frame { display: block; position: relative; width: 100%; box-sizing: border-box; background: transparent; }
.dfs-peptide-floating-header-frame *, .dfs-peptide-floating-header-frame *::before, .dfs-peptide-floating-header-frame *::after { box-sizing: border-box; }
.dfs-peptide-floating-header-frame__stage { width: 100%; padding: 22px 40px 64px 40px; }
.dfs-peptide-floating-header-frame__pill { position: relative; max-width: 1440px; margin-left: auto; margin-right: auto; display: grid; grid-template-columns: var( --pfh-pill-columns, 1fr auto 1fr ); align-items: center; min-height: 64px; padding: 0 38.7px 0 33px; background-color: var(--pfh-bar-bg, #FFFFFF); border-radius: 6px; }

.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__logo { grid-column: 1; justify-self: start; font-family: Montserrat, sans-serif; font-weight: 600; font-size: 28.5px; line-height: 50px; letter-spacing: -1px; text-transform: none; color: var(--pfh-logo, #143245); text-decoration: none; white-space: nowrap; }

.dfs-peptide-floating-header-frame__nav { grid-column: 2; display: flex; }
.dfs-peptide-floating-header-frame__nav-list { display: flex; align-items: center; gap: 29.5px; margin: 0; padding: 0; list-style: none; }
.dfs-peptide-floating-header-frame__nav-list li { margin: 0; padding: 0; list-style: none; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__nav-link { font-family: Montserrat, sans-serif; font-weight: 600; font-size: 20px; line-height: 19.7px; letter-spacing: 0; text-transform: capitalize; color: var(--pfh-nav, #18181B); text-decoration: none; white-space: nowrap; transition: color .2s ease; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__nav-link:hover, .dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__nav-link:focus-visible { color: var(--pfh-nav-hover, #143245); }

.dfs-peptide-floating-header-frame__side { grid-column: 3; justify-self: end; display: flex; align-items: center; }
.dfs-peptide-floating-header-frame__account { display: flex; align-items: center; gap: 15px; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__login { font-family: 'Nb international pro webfont', sans-serif; font-weight: 400; font-size: 13.1px; line-height: 19.7px; letter-spacing: 0; text-transform: none; color: var(--pfh-login, #18181B); text-decoration: none; white-space: nowrap; transition: color .2s ease; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__login:hover, .dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__login:focus-visible { color: var(--pfh-login-hover, #143245); }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__join { display: inline-flex; align-items: center; justify-content: center; min-width: 150.5px; padding: 9.5px 21px; border-radius: 6px; background-color: var(--pfh-join-bg, #061E2F); color: var(--pfh-join, #FFFFFF); font-family: 'Nb international pro webfont', sans-serif; font-weight: 400; font-size: 13.1px; line-height: 19.7px; letter-spacing: 0; text-transform: none; text-decoration: none; white-space: nowrap; transition: color .2s ease, background-color .2s ease; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__join:hover, .dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__join:focus-visible { color: var(--pfh-join-hover, #FFFFFF); background-color: var(--pfh-join-bg-hover, #143245); }

.dfs-peptide-floating-header-frame__dots-wrap { position: relative; margin-left: 18.7px; display: flex; }
.dfs-peptide-floating-header-frame button.dfs-peptide-floating-header-frame__dots { display: grid; grid-template-columns: repeat( 3, auto ); column-gap: 5.4px; row-gap: 6.6px; align-items: center; margin: 0; padding: 0; border: 0; border-radius: 0; background: none; cursor: pointer; box-shadow: none; }
.dfs-peptide-floating-header-frame button.dfs-peptide-floating-header-frame__dots:focus-visible { outline: 2px solid #143245; outline-offset: 4px; }
.dfs-peptide-floating-header-frame span.dfs-peptide-floating-header-frame__dot { display: block; width: 5.5px; aspect-ratio: 1 / 1; border-radius: 50%; background-color: var(--pfh-dot, #18181B); transition: background-color 0.2s ease; }

/* The panel: floating under the dots. display is authored below, so [hidden] is stated. */
.dfs-peptide-floating-header-frame__panel { position: absolute; top: 100%; right: 0; z-index: 50; display: flex; flex-direction: column; gap: 12px; width: 300px; max-width: calc( 100vw - 16px ); margin-top: 14px; padding: 20px 24px; background-color: #FFFFFF; border-radius: 6px; box-shadow: 0 12px 32px rgba( 20, 50, 69, 0.18 ); }
.dfs-peptide-floating-header-frame__panel[hidden] { display: none; }
.dfs-peptide-floating-header-frame__panel-list { display: flex; flex-direction: column; gap: 12px; margin: 0; padding: 0; list-style: none; }
.dfs-peptide-floating-header-frame__panel-list li { margin: 0; padding: 0; list-style: none; }
.dfs-peptide-floating-header-frame__panel-main, .dfs-peptide-floating-header-frame__panel-account { display: none; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__panel-link { font-family: Montserrat, sans-serif; font-weight: 500; font-size: 18px; line-height: 24px; letter-spacing: 0; text-transform: capitalize; color: #18181B; text-decoration: none; transition: color .2s ease; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__panel-link:hover, .dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__panel-link:focus-visible { color: #143245; }

/* Set by the script only while the full row does not fit the card (between the phone and ~1060): logo + dots, the links inside the panel. */
.dfs-peptide-floating-header-frame.dfs-peptide-floating-header-frame--compact .dfs-peptide-floating-header-frame__nav,
.dfs-peptide-floating-header-frame.dfs-peptide-floating-header-frame--compact .dfs-peptide-floating-header-frame__account { display: none !important; }
.dfs-peptide-floating-header-frame.dfs-peptide-floating-header-frame--compact .dfs-peptide-floating-header-frame__panel-main,
.dfs-peptide-floating-header-frame.dfs-peptide-floating-header-frame--compact .dfs-peptide-floating-header-frame__panel-account { display: flex !important; }

/* Mode 9, Phone Menu = Popup panel (pfh_mobile_menu). Drawn only when picked. Motion is GSAP (no CSS transitions). Values = the approved mockup. */
.dfs-peptide-floating-header-frame__pop { position: fixed; inset: 0; z-index: 99999; font-family: "DM Sans", sans-serif; }
.dfs-peptide-floating-header-frame__pop[hidden] { display: none; }
.dfs-peptide-floating-header-frame__pop-dim { position: absolute; inset: 0; background-color: rgba( 3, 15, 90, 0.45 ); }
.dfs-peptide-floating-header-frame__pop-panel { position: absolute; top: 0; right: 0; bottom: 0; width: 85%; box-sizing: border-box; display: flex; flex-direction: column; overflow-y: auto; padding: 20px 24px 28px; background-color: #0A2239; color: #FFFFFF; font-weight: 500; box-shadow: -8px 0 30px rgba( 3, 15, 90, 0.35 ); }
.dfs-peptide-floating-header-frame .dfs-peptide-floating-header-frame__pop-logo { position: absolute; top: 26px; left: 50%; transform: translateX( -50% ); width: 124px; display: block; color: #FFFFFF; font-size: 20px; font-weight: 600; text-align: center; text-decoration: none; }
.dfs-peptide-floating-header-frame .dfs-peptide-floating-header-frame__pop-logo img { display: block; width: 100%; height: auto; }
.dfs-peptide-floating-header-frame button.dfs-peptide-floating-header-frame__pop-x { align-self: flex-end; display: grid; place-content: center; width: 44px; height: 44px; margin: 0; padding: 0; border: 0; border-radius: 0; background: none; box-shadow: none; cursor: pointer; flex: none; }
/* No tap-highlight box behind the close dots or the dots button (the blue square at the start of a close on phones). */
.dfs-peptide-floating-header-frame button.dfs-peptide-floating-header-frame__pop-x, .dfs-peptide-floating-header-frame button.dfs-peptide-floating-header-frame__dots { -webkit-tap-highlight-color: transparent; }
.dfs-peptide-floating-header-frame button.dfs-peptide-floating-header-frame__pop-x:focus-visible { outline: 2px solid #FFFFFF; outline-offset: 2px; }
.dfs-peptide-floating-header-frame__pop-dots { display: grid; grid-template-columns: repeat( 3, 6px ); gap: 6px; }
.dfs-peptide-floating-header-frame i.dfs-peptide-floating-header-frame__pop-dot { display: block; width: 6px; height: 6px; border-radius: 50%; background-color: #FFFFFF; }
.dfs-peptide-floating-header-frame__pop-links { display: flex; flex-direction: column; margin-top: 44px; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__pop-link,
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__pop-row { display: flex; justify-content: space-between; align-items: center; padding: 28px 0; border-top: 1px solid rgba( 255, 255, 255, 0.18 ); text-decoration: none; line-height: normal; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__pop-link { font-size: 24px; color: #FFFFFF; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__pop-row { font-size: 20px; color: #8FD1AE; border-bottom: 1px solid rgba( 255, 255, 255, 0.18 ); }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__pop-row + a.dfs-peptide-floating-header-frame__pop-row { border-top: 0; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__pop-link::after,
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__pop-row::after { content: ""; flex: none; width: 9px; height: 9px; margin-right: 4px; border-top: 2px solid #6FA487; border-right: 2px solid #6FA487; transform: rotate( 45deg ); box-sizing: border-box; }
.dfs-peptide-floating-header-frame__pop-cta { margin-top: 40px; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__pop-login { display: block; padding: 16px; border-radius: 14px; background-color: #FFFFFF; color: #0A2239; font-size: 17px; font-weight: 600; line-height: normal; text-align: center; text-decoration: none; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__pop-create { display: block; margin-top: 14px; color: #C4D0EC; font-size: 14px; line-height: normal; text-align: center; text-decoration: underline; }

/* ==========================================================================
   Desktop List Menu (switch "Desktop List Menu"). Printed only when the switch is on; every rule here
   targets the __dl- classes, so with the switch off nothing below matches. 1025px and wider only.
   ========================================================================== */
.dfs-peptide-floating-header-frame__dl-dim { position: fixed; inset: 0; z-index: 40; background: rgba( 8, 35, 56, 0.38 ); -webkit-backdrop-filter: blur( 10px ); backdrop-filter: blur( 10px ); }
.dfs-peptide-floating-header-frame__dl-dim[hidden] { display: none; }
.dfs-peptide-floating-header-frame__dl-panel { position: absolute; top: calc( 100% + 12px ); right: 0; z-index: 2; display: block; width: 320px; max-width: calc( 100vw - 16px ); box-sizing: border-box; padding: 12px 24px 28px; border-radius: 24px; background-color: #FFFFFF; box-shadow: 0 24px 60px rgba( 8, 35, 56, 0.25 ); font-family: 'DM Sans', sans-serif; color: #082338; text-align: left; }
.dfs-peptide-floating-header-frame__dl-panel[hidden] { display: none; }
.dfs-peptide-floating-header-frame__dl-rows { display: block; }
/* the bar's links inside the list: only while the row is compact (the script adds --compact), since the bar hides them then */
.dfs-peptide-floating-header-frame .dfs-peptide-floating-header-frame__dl-main { display: none; }
.dfs-peptide-floating-header-frame.dfs-peptide-floating-header-frame--compact .dfs-peptide-floating-header-frame__dl-main { display: block; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__dl-row { display: flex; align-items: center; min-height: 0; padding: 20px 0 21px; font-family: 'DM Sans', sans-serif; font-size: 22px; font-weight: 500; line-height: 1; letter-spacing: 0.2px; text-transform: none; text-decoration: none; color: #082338; border-bottom: 1px solid rgba( 8, 35, 56, 0.18 ); }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__dl-login { display: flex; align-items: center; justify-content: center; width: 100%; margin-top: 28px; padding: 15px 40px 16px; border-radius: 999px; background-color: #082338; color: #FFFFFF; font-family: 'DM Sans', sans-serif; font-size: 18px; font-weight: 500; line-height: 1; letter-spacing: 0; text-transform: none; text-decoration: none; white-space: nowrap; box-sizing: border-box; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__dl-create { display: block; margin-top: 18px; font-family: 'DM Sans', sans-serif; font-size: 15px; font-weight: 400; line-height: 1; letter-spacing: 0; text-align: center; text-transform: none; text-decoration: underline; color: #082338; }
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__dl-row:focus-visible,
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__dl-login:focus-visible,
.dfs-peptide-floating-header-frame a.dfs-peptide-floating-header-frame__dl-create:focus-visible { outline: 2px solid #082338; outline-offset: 3px; }
@media ( min-width: 1025px ) {
	/* the bar stays above the dim while the list is open (JS adds __dl-open) */
	.dfs-peptide-floating-header-frame.dfs-peptide-floating-header-frame__dl-open .dfs-peptide-floating-header-frame__pill { z-index: 41; }
	/* the old 300px dropdown never opens on desktop when the list is on */
	.dfs-peptide-floating-header-frame[data-pfh-dlist] .dfs-peptide-floating-header-frame__panel { display: none !important; }
}
@media ( max-width: 1024px ) {
	.dfs-peptide-floating-header-frame__dl-panel, .dfs-peptide-floating-header-frame__dl-dim { display: none !important; }
}
/* Brad 11-09: the dots off per device (classes from the widget's Dots Menu switches). Never while compact: the links live behind the dots then. */
@media ( min-width: 1025px ) {
	.dfs-peptide-floating-header-frame.dfs-peptide-floating-header-frame--nodots-d:not(.dfs-peptide-floating-header-frame--compact) .dfs-peptide-floating-header-frame__dots-wrap { display: none !important; }
}
@media ( min-width: 768px ) and ( max-width: 1024px ) {
	.dfs-peptide-floating-header-frame.dfs-peptide-floating-header-frame--nodots-t:not(.dfs-peptide-floating-header-frame--compact) .dfs-peptide-floating-header-frame__dots-wrap { display: none !important; }
}
@media ( max-width: 767px ) {
	.dfs-peptide-floating-header-frame.dfs-peptide-floating-header-frame--nodots-m:not(.dfs-peptide-floating-header-frame--compact) .dfs-peptide-floating-header-frame__dots-wrap { display: none !important; }
}
