/**
 * The landing page: the breach, the wall of worlds, and the header over both.
 *
 * Ported from tools/breach-lab/index.html's inline stylesheet. That file carries the
 * reasoning for nearly every rule here — why the wall is fixed rather than scrolling, why
 * the sky and the artwork are clipped to the panel's rectangle, why a name goes inside its
 * opening, why percentage padding was what broke the first version of an aperture. Read it
 * before changing a number. What follows records only what is different in the theme.
 *
 * FOUR THINGS CHANGED ON THE WAY IN.
 *
 * 1  EVERY CLASS IS PREFIXED `aethyrdancer-`, AND THE PREFIX IS NOT NEGOTIABLE.
 *
 *    The lab owns its whole document and can call a thing `.panel`; this sheet loads into
 *    a page that also holds Astra, WooCommerce and the admin bar, where `.panel`,
 *    `.layer`, `.world`, `.labels` and `.meta` are all names something else will
 *    eventually want.
 *
 *    IT WAS `ad-` FOR A DAY AND CONTENT BLOCKERS ATE THE PAGE. `ad-` is the most filtered
 *    prefix on the web, and `.ad-masthead`, `.ad-panel` and `.ad-world` are indistinguishable
 *    from ad-slot class names — a masthead is literally the name of an ad format. Reported
 *    2026-09-03 from a Chrome profile with a blocker: the header, the breach and the artwork
 *    behind it were all gone while the wall of worlds drew perfectly, because the wall's own
 *    classes happened not to match any filter. `getBoundingClientRect()` on the mark returned
 *    0x0 at 0,0 — the signature of `display: none` — with the correct stylesheet loaded and
 *    no console error, so nothing on the page or in the server logs pointed at the cause.
 *
 *    The custom properties stay `--ad-*`. A cosmetic filter matches selectors, never
 *    property names, and `--ad-*` is the token namespace every other stylesheet here uses.
 *
 * 2  THE RIG'S TOGGLES ARE GONE, and with them four states this had to describe. `dim-near`
 *    and `clip-holes` were on by default and are now unconditional; `lede-on-wall`,
 *    `show-meta`, `garments-behind` and the aperture-as-frame were off and are not here at
 *    all. `.aethyrdancer-is-drifting` stays, because reduced motion still switches it.
 *
 * 3  THE LOGO STACK IS NOT REDECLARED. The lab reproduces assets/css/logo.css by hand
 *    because it is served out of uploads/ and cannot enqueue a theme handle. Here the real
 *    file loads and this one overrides a single declaration in it: the mark's height, which
 *    is the only thing the landing masthead does differently.
 *
 * 4  THE NAVIGATION DOES NOT RIDE THE CAMERA. See `.aethyrdancer-masthead__rider` below.
 *
 * The generated panels arrive as custom properties from inc/landing.php, each carrying a
 * filemtime, because they live in uploads/ and a stylesheet in the theme cannot reach them
 * with a relative path — and because nothing versions a `url()` inside a stylesheet.
 */

/*
 * THE PARENT THEME'S CONTENT CONTAINER, UNDONE.
 *
 * Astra wraps the template in `#content > .ast-container`, which is a max-width, a pair of
 * paddings, and — the one that actually broke this — `display: flex`. Both of the first two
 * are right for a page of text and wrong for a wall that has to reach the edges of the
 * viewport: capped at the container width, the near panel letterboxes and the far wall's ten
 * openings shrink with it.
 *
 * `display: block` IS THE LOAD-BEARING LINE. That container is a flex ROW, meant to hold a
 * content column beside a sidebar. Left as one, the masthead, the stage and the wall section
 * became three flex items side by side rather than three bands stacked: the masthead and the
 * scene measured 0px wide, because everything inside them is absolutely positioned and a
 * flex item's automatic size is its content's. The symptom was a front page that opened part
 * way through its own camera move with no hero anywhere — the stage's negative top margin was
 * measuring against a sibling instead of against the header above it.
 *
 * Scoped to the landing body class, so no other template is touched.
 */
.aethyrdancer-landing #content > .ast-container {
	display: block;
	max-width: none;
	padding-left: 0;
	padding-right: 0;
}

.aethyrdancer-landing #content,
.aethyrdancer-landing .site-content > .ast-container > .ast-row {
	margin: 0;
	padding: 0;
}

.aethyrdancer-landing {
	overflow-x: hidden;
}

/*
 * AND THE SAME BELT AND BRACES collections-index.css ALREADY WEARS, FOR THE SAME REASON.
 *
 * The rule above undoes the flex row, and that is one override standing between this page
 * and a layout collapse. If it is ever outranked — a sidebar enabled on the front page, a
 * Customizer container change, a parent-theme update — the four blocks below become flex
 * items again, and a flex item's automatic size is its content's. Everything inside the
 * masthead and the scene is absolutely positioned, so their content size is zero: the
 * header and the whole hero measure 0px wide and vanish, while the wall of worlds, whose
 * children are in flow, carries on drawing. That is not a hypothetical, it is what this
 * page did before the rule above was written, and the symptom gives no hint of the cause.
 *
 * `flex` and `width` together because the parent may be either. In an ordinary block parent
 * `width: 100%` is simply the default and `flex` is inert.
 *
 * IT DOES NOT FULLY RESTORE THE LAYOUT AND IS NOT MEANT TO. Three flex items asking for
 * 100% of one unwrapped row get a third each — measured at 1440, the masthead comes back at
 * 480 rather than 0. That is the point: the page degrades to visibly wrong instead of
 * silently absent, and a header a third of the width is a symptom somebody can act on.
 * `display: block` above is still the fix.
 */
.aethyrdancer-landing .aethyrdancer-masthead,
.aethyrdancer-landing .aethyrdancer-stage,
.aethyrdancer-landing .aethyrdancer-wallsec,
.aethyrdancer-landing .aethyrdancer-landing-fallback {
	flex: 1 1 100%;
	width: 100%;
}

:root {
	/*
	 * The masthead's resting height. Taller than the site's 204: the mark is drawn larger
	 * here, and it cannot grow inside a band sized for a crest.
	 *
	 * `--ad-head`, not `--head`. The apertures set `--head` on themselves — it is the top of
	 * a hole as a fraction of its own box — and a root-level `--head: 300px` is inherited by
	 * any aperture that does not, which is every crack. `.aethyrdancer-cap.is-above`'s fallback would
	 * then resolve a length where it wanted a fraction.
	 */
	--ad-head: 300px;

	/* Written by landing.js each frame. Declared here so a failed script leaves the page at
	   rest rather than with unresolved variables. */
	--ad-t: 0;
	--ad-wall-scale: 1;
	--ad-world-rel: 1;
	--ad-sky-rel: 1;
	--ad-wall-alpha: 1;
	--ad-lede-alpha: 1;
	--ad-dim-max: 0.55;
	--ad-cta-alpha: 0;
}

/*
 * ONE WALL, AND IT DOES NOT MOVE.
 *
 * tokens.css already paints this on `body` — the ground tile over the page ground — and it
 * already scrolls. Fixing it is what makes the masthead, the hero and the wall section one
 * continuous surface with the camera moving across it rather than three bands sliding past
 * each other. The lab's own note records the wrong turn: a fixed background on the header
 * alone leaves the header still scrolling, so its painted band shrinks from the bottom and
 * uncovers the body's ground, which was still moving.
 *
 * COST: a fixed tiled background takes Chrome off its fast scroll path, because the
 * background is repainted as content moves under it. Cheap on one 10KB tile, and confined
 * to this template — do not lift it to a page that sells without measuring.
 */
.aethyrdancer-landing {
	background-attachment: fixed, fixed;
}

/* -------------------------------------------------------------------------- masthead */

.aethyrdancer-masthead {
	/* The sticky header remains after its artwork leaves. Only its visible links
	 * should intercept the collection wall underneath. */
	pointer-events: none;
	position: sticky;
	top: 0;
	z-index: 5;
	height: var(--ad-head);

	/* Paints nothing: the body's ground is fixed to the viewport, so the mark rises out of
	   frame and the wall it was hanging on stays exactly where it is. */
	background: transparent;
}

/*
 * THE MARK IS ON THE NEAR WALL, SO IT MOVES LIKE THE NEAR WALL.
 *
 * It takes the wall's scale exactly — no second curve to keep in step, and none to get
 * wrong. Scaling about the aperture (the origin is the axis of travel) already carries it
 * upward, because it sits above that axis; `--ad-head-lift` only finishes the job.
 *
 * THE RIDER IS A SEPARATE ELEMENT FROM THE HEADER, AND THAT IS THE WHOLE POINT.
 *
 * The lab put this transform on the header itself, which was fine because the lab's header
 * held nothing but the mark. This one holds the navigation, and a transformed ancestor
 * makes `position: fixed` behave like `absolute` — so a nav inside the transform would
 * scale, fade and lift out of frame along with the mark, leaving the landing page with no
 * navigation at all below the first screen. CLAUDE.md's own header note is explicit that a
 * persistent nav is navigation rather than an effect.
 *
 * The rider is inset to the header's own box, so `transform-origin: 50% 50vh` means exactly
 * what it meant when it was on the header.
 */
.aethyrdancer-masthead__rider {
	position: absolute;
	inset: 0;
	display: grid;
	place-items: center;
	transform: translateY(var(--ad-head-lift, 0px)) scale(var(--ad-head-scale, 1));
	transform-origin: 50% 50vh;
	opacity: var(--ad-head-alpha, 1);
	will-change: transform;
}

.aethyrdancer-masthead__mark {
	pointer-events: auto;
	display: block;
	line-height: 0;
	text-decoration: none;
	max-width: 92vw;
}

/* The one declaration this sheet takes back off logo.css: the site header draws the mark at
   75px, and the ask here was a larger mark on more wall. The canvas is 7:1, so this is
   1050px wide, and the narrow breakpoint hands over to a width-driven mark before it
   overflows. */
.aethyrdancer-masthead .aethyrdancer-logo-stack__top {
	height: 150px;
}

.aethyrdancer-masthead__mark--text {
	font-size: clamp(1.6rem, 5vw, 3rem);
	line-height: 1.1;
	color: var(--ad-text-strong);
	letter-spacing: 0.04em;
}

.aethyrdancer-masthead__rider.is-away {
	visibility: hidden;
}

/* ------------------------------------------------------------------------ navigation */

/*
 * FIXED, NOT STICKY, AND OUTSIDE THE CAMERA MOVE.
 *
 * It is a child of the masthead so it is the header's navigation in the document, and it
 * sits at the top right of the header at rest. It is fixed so it stays there when the
 * header lifts away: a landing page whose only navigation leaves at 26% of the first scroll
 * is a landing page you cannot leave except by the one link at the bottom.
 */
.aethyrdancer-landing-nav {
	pointer-events: auto;
	position: fixed;
	top: clamp(12px, 2.2vh, 26px);
	right: clamp(12px, 2.4vw, 34px);
	z-index: 6;
}

.aethyrdancer-landing-nav ul {
	display: flex;
	align-items: flex-start;
	gap: clamp(4px, 1.2vw, 14px);
	margin: 0;
	padding: 6px 10px;
	list-style: none;

	/*
	 * A SCRIM, BECAUSE THE BACKDROP IS NOT A CONSTANT.
	 *
	 * On the hero these icons sit on masonry at luminance 17 and need nothing. Over the wall
	 * of worlds they can end up on an opening full of high-chroma fractal at full
	 * brightness, and a 1.5px stroke in `currentColor` disappears into it. Principle 8, the
	 * same local scrim the hero's headline gets, at the strength this needs rather than that
	 * one's.
	 */
	background: rgb(3 3 8 / 62%);
	border: 1px solid rgb(63 56 80 / 55%);
	border-radius: 999px;
}

/*
 * The blur is desktop-only. A backdrop filter over a wall that is being scaled and
 * scrolled is a paint of the backdrop every frame, which a phone GPU notices and a laptop
 * does not; below the breakpoint the scrim is simply a little more opaque instead.
 */
@media (min-width: 901px) {
	.aethyrdancer-landing-nav ul {
		backdrop-filter: blur(5px);
	}
}

@media (max-width: 900px) {
	.aethyrdancer-landing-nav ul {
		background: rgb(3 3 8 / 78%);
	}
}

.aethyrdancer-landing-nav li {
	margin: 0;
	padding: 0;
	list-style: none;
}

.aethyrdancer-landing-nav a {
	display: block;

	/*
	 * 44px, which is the touch target rather than the icon. The glyph is 1.55em on a 0.9rem
	 * box, so it draws at about 22px and the padding carries the rest — an icon-only nav
	 * whose targets are icon-sized is the commonest way a phone header becomes unusable.
	 */
	min-width: 44px;
	min-height: 44px;
	padding: 11px;
	color: var(--ad-text);
	font-size: 0.9rem;
	text-decoration: none;
	transition: color 160ms ease;
}

.aethyrdancer-landing-nav a:hover,
.aethyrdancer-landing-nav a:focus-visible {
	color: var(--ad-accent-soft);
}

.aethyrdancer-landing-nav a:focus-visible {
	outline: 2px solid var(--ad-accent-soft);
	outline-offset: 2px;
	border-radius: 4px;
}

/*
 * BELOW THE TOOLTIP BREAKPOINT THE WORD IS THE LABEL.
 *
 * nav-symbols.css un-hides `.aethyrdancer-navsym__label` at 921px and switches the tooltip
 * off, because there is no hover on touch and an icon-only menu on a phone is an icon-only
 * menu with no way to find out what the icons mean. That rule is right and this does not
 * fight it — it only changes the axis. Astra's drawer is a vertical list with room for a
 * word beside a glyph; this is a horizontal cluster with room for one under it.
 */
@media screen and (max-width: 921px) {
	.aethyrdancer-landing-nav .aethyrdancer-navsym {
		flex-direction: column;
		gap: 3px;
	}

	/*
	 * 0.68rem, NOT 0.5. The first pass set these at half a root em, which measured 7.3px
	 * against Astra's 14.6px root — smaller than any body text on the site and below what a
	 * label can be read at on a phone held at arm's length. 0.68rem is about 10px, and five
	 * of them plus their gaps measure inside 375px with room to spare.
	 */
	.aethyrdancer-landing-nav .aethyrdancer-navsym__label {
		font-size: 0.68rem;
		letter-spacing: 0;
		text-transform: lowercase;
		white-space: nowrap;
		color: var(--ad-text-muted);
	}

	.aethyrdancer-landing-nav a {
		padding: 8px 3px;
		font-size: 0.82rem;
	}

	.aethyrdancer-landing-nav ul {
		gap: 0;
		padding: 4px 4px;
	}
}

/* ----------------------------------------------------------------------- the stage */

/*
 * PULLED UP UNDER THE HEADER, so `t` starts at the first pixel of scroll. The mark's exit
 * and the push-in are one camera move and they share one number.
 */
.aethyrdancer-stage {
	position: relative;
	height: 240vh;
	margin-top: calc(-1 * var(--ad-head));
}

.aethyrdancer-scene {
	position: sticky;
	top: 0;
	height: 100vh;
	overflow: hidden;

	/*
	 * The page's own ground, fixed to the viewport for the same reason the body's is — and
	 * for one more: this element is sticky, so a scrolling background would put its tile
	 * origin somewhere down the viewport and the two tilings would sit out of phase.
	 * Anchoring both to the viewport makes the join between them unfindable.
	 */
	background-color: var(--ad-ground-100);
	background-image:
		linear-gradient(
			rgb(14 12 22 / calc(1 - var(--ad-grain-opacity))),
			rgb(14 12 22 / calc(1 - var(--ad-grain-opacity)))
		),
		var(--ad-ground-tile, url("../images/grain.webp"));
	background-repeat: repeat, repeat;
	background-size: auto, var(--ad-grain-size);
	background-attachment: fixed, fixed;
}

/*
 * THE SKY AND THE ARTWORK ARE ONLY EVER SEEN THROUGH THE HOLE, SO THEY ARE CLIPPED TO IT.
 *
 * Layering an opaque wall over full-bleed artwork is the obvious arrangement and is wrong in
 * one place: the panel's outer edge feathers away, so the artwork bleeds out around the rim
 * and past the feather there is nothing to be wall at all. This box is the panel's own
 * rectangle, it carries the wall's transform, and overflow:hidden means nothing behind the
 * wall can appear anywhere the wall is not.
 */
.aethyrdancer-viewbox {
	position: absolute;
	left: 50%;
	top: 50%;
	width: 1560px;
	height: 1000px;
	translate: -50% calc(-50% + var(--ad-head-pad, 0px));
	transform: scale(var(--ad-wall-scale));

	/* MEASURED OFF THE PANEL, NOT ASSUMED — build_breach.py prints it. The generator is
	   asked for a centre of (0.50, 0.52) and does not deliver it: whole-brick removal, the
	   fray and the upward arch all move the rim. */
	transform-origin: 50.4% 51%;
	overflow: hidden;
	will-change: transform;
}

.aethyrdancer-layer {
	position: absolute;
	inset: 0;
	transform-origin: 50.4% 51%;
	backface-visibility: hidden;
}

/*
 * THE WORLD STOPS SHORT OF THE PANEL'S FEATHERED EDGE.
 *
 * The panel's outer 140px ramp to transparent so it dissolves into the page's tiled ground.
 * Clipping to the panel's rectangle is therefore not quite enough: inside it the last 140px
 * are semi-transparent, so past about 1300px wide the artwork showed as two coloured bands
 * down the sides of the hero.
 */
.aethyrdancer-sky,
.aethyrdancer-world {
	inset: 150px;
}

/*
 * The depths are RELATIVE to the wall's, because these layers sit inside the wall's own
 * transform and inherit it. Writing the absolute scale here multiplies the two and the
 * artwork races the wall.
 */
.aethyrdancer-sky {
	/* This one carries its own fallback inside the `var()`, so the shorthand is safe — the
	   theme's own copy of the sky is always there. Written long anyway, to match. */
	background-image: var(--ad-sky-image, url("../images/night-sky.webp"));
	background-repeat: repeat;
	background-size: 1024px auto;
	transform: scale(var(--ad-sky-rel));
	opacity: 0.85;
}

.aethyrdancer-world {
	background: no-repeat center / cover;
	background-image: var(--ad-world-art, none);
	transform: scale(var(--ad-world-rel));
}

/* Drawn at its authored size, NOT stretched to the viewport: its bricks are generated at
   26px because that is what the masthead's crest renders at, and the page's ground tile is
   fixed at 512px whatever the viewport is. */
.aethyrdancer-panel {
	/*
	 * LONGHANDS, NOT THE SHORTHAND, AND THE REASON IS A FAILURE MODE RATHER THAN A STYLE.
	 *
	 * `background: var(--ad-landing-panel) no-repeat 0 0 / 100% 100%` reads well and fails
	 * badly. A `var()` that resolves to nothing makes the WHOLE declaration invalid at
	 * computed-value time, so the shorthand takes the repeat, the position and the size down
	 * with it and the element paints nothing at all — with no error, and no clue in the
	 * inspector beyond a property that is simply not there.
	 *
	 * The property is written by inc/landing.php only when the file exists in the payload,
	 * so "resolves to nothing" is a state this page can genuinely be in: a checkout with no
	 * sync, a half-finished sync, a build that has not run. As longhands with a `none`
	 * fallback, that state is a wall with no breach drawn on it — which is wrong but is
	 * visibly the shape of the page — instead of a hero that has silently ceased to exist.
	 */
	background-image: var(--ad-landing-panel, none);
	background-repeat: no-repeat;
	background-position: 0 0;
	background-size: 100% 100%;
	opacity: var(--ad-wall-alpha);
}

.aethyrdancer-lede {
	position: absolute;
	left: 50%;
	top: 50%;
	translate: -50% calc(-50% + var(--ad-head-pad, 0px));
	width: min(94vw, 56rem);
	margin: 0;
	text-align: center;
	opacity: var(--ad-lede-alpha);
	pointer-events: none;
	isolation: isolate;
}

.aethyrdancer-lede > * {
	margin: 0;
}

.aethyrdancer-lede__sub {
	margin-top: 1rem;
}

/*
 * A LOCAL SCRIM — principle 8, and not optional over artwork.
 *
 * Shadows alone were tried and failed: over fine filament work the headline was legible in
 * the quiet areas and gone in the busy ones, which is worse than uniformly hard to read
 * because it breaks up mid-word. A feathered ellipse darkens what the type sits on and
 * leaves the rest of the artwork at full strength.
 */
.aethyrdancer-lede::before {
	content: "";
	position: absolute;
	inset: -3rem -4rem;
	z-index: -1;
	background: radial-gradient(
		ellipse at center,
		rgb(3 3 8 / 88%) 0%, rgb(3 3 8 / 74%) 42%, rgb(3 3 8 / 34%) 68%, transparent 82%
	);
}

/* ------------------------------------------------------------- the wall of worlds */

.aethyrdancer-wallsec {
	position: relative;
	padding: clamp(2.5rem, 7vh, 5rem) 0 clamp(3rem, 9vh, 6rem);
}

.aethyrdancer-wallhead,
.aethyrdancer-wallfoot {
	max-width: 62rem;
	margin: 0 auto;
	padding: 0 1.5rem;
	text-align: center;
}

.aethyrdancer-wallhead {
	margin-bottom: clamp(1.5rem, 4vh, 3rem);
}

/*
 * THE HEADING WITH ITS WORDS REMOVED.
 *
 * front-page.php keeps the <h2> and hides it, because the section's `aria-labelledby`
 * points at it and an empty heading is worse than none. This sheet loads after
 * lettering.css, so the rule above would otherwise win `margin` and `padding` against the
 * `.screen-reader-text` recipe on the same element — same specificity, later source. The
 * box is absolutely positioned either way, so nothing visible depended on it; restating
 * the two declarations means the recipe is whole wherever it is read from.
 */
.aethyrdancer-wallhead.screen-reader-text {
	margin: -1px;
	padding: 0;
}

/*
 * THE CLOSING LINK IS ON THE WALL, NOT ON THE PAGE.
 *
 * In flow it rose up past the openings with everything else, which made it one more thing
 * travelling and gave it the same weight as a collection. Fixed, it belongs to the surface
 * the wall is painted on — which is itself fixed — so it does not move at all: it becomes
 * visible as you reach the foot of the collections, the way lettering on a wall appears when
 * the light gets to it. Its own flow space stays as a spacer, so the section still ends
 * where it ended.
 */
.aethyrdancer-wallfoot {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 7vh;
	z-index: 4;
	opacity: var(--ad-cta-alpha, 0);
	transition: opacity 260ms linear;
}

.aethyrdancer-wallfoot[data-idle="1"] {
	pointer-events: none;
}

.aethyrdancer-wallfoot-spacer {
	height: clamp(5rem, 14vh, 9rem);
}

/*
 * DRAWN AT THE SIZE IT WAS AUTHORED, like the near panel and for the same reason.
 *
 * Stretched to the section's width, the panel's baked ground tile rendered at 358px against
 * the page's own 512 — so the filaments came out thirty per cent small, the brick scale
 * disagreed, and the wall read as a different material however well its colour was matched.
 * `min()` keeps it 1:1 on any viewport wide enough and only gives that up on a narrow one.
 *
 * `cover` was the first instinct and it clips: two of the ten openings sit close enough to
 * the panel's edges that a cover crop would cut their light in half, and an aperture half
 * off screen is a navigation target half off screen.
 */
.aethyrdancer-wallframe {
	position: relative;
	width: min(calc(var(--ad-wall-w, 1404) * 1px), 100%);
	margin: 0 auto;
	aspect-ratio: var(--ad-wall-w, 1404) / var(--ad-wall-h, 1680);
	isolation: isolate;

	/*
	 * THE HOVER LIGHT MUST NOT LENGTHEN THE PAGE SIDEWAYS.
	 *
	 * `.aethyrdancer-ap::after` is an absolutely positioned pseudo-element at `inset: -55%`, so an
	 * aperture near either edge of the wall puts its glow past the wall and past the
	 * viewport — and a positioned box, unlike a paint effect, counts towards scrollable
	 * overflow. Measured at 375px: the document scrolled 76px to the right, on a page whose
	 * every visible element was inside the viewport. Hiding `.aethyrdancer-aps` was what identified it;
	 * nothing was visibly wrong, which is why it is worth writing down.
	 *
	 * `clip` rather than `hidden`, and on x only. `hidden` would make this a scroll container
	 * and coerce the y axis to `auto`, which would cut the crack labels that hang below the
	 * wall on purpose. `clip` clips one axis and leaves the other genuinely visible.
	 *
	 * The wall's own rectangle is the honest boundary for this: the glow is light falling on
	 * masonry, and there is no masonry past the edge of the panel.
	 */
	overflow-x: clip;
	overflow-y: visible;

	/* So a label inside an aperture can size itself against the wall rather than the root
	   font, and every opening's type shrinks together as the panel does. */
	container-type: inline-size;
}

.aethyrdancer-wallframe > * {
	position: absolute;
	inset: 0;
}

.aethyrdancer-worlds {
	z-index: 1;
}

.aethyrdancer-wallimg {
	z-index: 2;
	/* Longhands, for the reason recorded on `.aethyrdancer-panel`. */
	background-image: var(--ad-landing-wall, none);
	background-repeat: no-repeat;
	background-position: 0 0;
	background-size: 100% 100%;
	pointer-events: none;
}

.aethyrdancer-aps {
	z-index: 3;
}

.aethyrdancer-labels {
	z-index: 4;
	pointer-events: none;
}

/*
 * One world per opening, positioned and sized from the manifest's MEASURED bounding box.
 * Exactly the box, not a margin around it: the openings are close enough that an enlarged
 * block would slide under a neighbour and show through the wrong hole.
 */
.aethyrdancer-wd {
	position: absolute;
	left: calc(var(--x) * 100%);
	top: calc(var(--y) * 100%);
	width: calc(var(--w) * 100%);
	height: calc(var(--h) * 100%);

	/* The picture is on a child rather than here, and this clips it: the live-filter
	   fallback below has to overhang its own box — a blur samples past its edges — while the
	   box itself still has to measure exactly the manifest's. */
	overflow: hidden;
}

.aethyrdancer-wd > div {
	position: absolute;
	inset: 0;
	background: no-repeat center / cover var(--art, none);

	/*
	 * POINTER PARALLAX. `--ad-px` and `--ad-py` run -1 to 1 across the wall, written once
	 * per frame by landing.js on fine pointers only. The scene shifts WITH the pointer, as
	 * the view through a window shifts with your head, and `scale(1.05)` is the overscan
	 * that keeps the box covered at the extremes. Both layers of a world carry it, so the
	 * sharp and the far picture never separate. The transition is the whole feel: the
	 * pointer moves, the scene settles after it.
	 */
	transform: translate(
		calc(var(--ad-px, 0) * var(--ad-parallax, 2.4%)),
		calc(var(--ad-py, 0) * var(--ad-parallax, 2.4%))
	) scale(1.05);
	transition: transform 640ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

/*
 * THE DIM IS A CROSS-FADE BETWEEN TWO STACKED WORLDS, NOT A FILTER ON ONE.
 *
 * Animating `filter: blur()` is a paint per frame on the backdrop, nine times over, and it
 * is the one part of this effect that would actually cost. Two layers and an opacity is
 * compositor-only. `--ad-dim-max` caps it well short of 1 ON PURPOSE: a full cross-fade is
 * the resting blur that was already rejected, arriving a moment at a time.
 */
.aethyrdancer-wd .aethyrdancer-w-far {
	opacity: calc(var(--near, 0) * var(--ad-dim-max, 0.55));
	transition: opacity 180ms linear;
}

/* Nothing baked yet: the same two numbers as far_world.py, live. Costs a paint, which is why
   it is the fallback and not the design. The blur is in cqw against `.aethyrdancer-wallframe`'s
   container, so the radius stays the same FRACTION of the opening at every viewport — what
   FAR_BLUR means in the generator. Keep the two in step by hand; nothing checks them. */
.aethyrdancer-wd:not(.is-baked) .aethyrdancer-w-far {
	background-image: var(--art);
	inset: calc(var(--w) * -6cqw);
	filter: blur(calc(var(--w) * 2.8cqw)) brightness(0.55);
}

.aethyrdancer-wd .aethyrdancer-w-sharp {
	background-image: var(--art);
}

.aethyrdancer-wd.is-baked .aethyrdancer-w-far {
	background-image: var(--art-far);
}

/*
 * NOTHING IN HERE IS LAID OUT IN FLOW, AND PERCENTAGE PADDING IS WHY.
 *
 * Percentage padding resolves against the containing block's WIDTH, and an `.aethyrdancer-ap` is
 * absolutely positioned inside `.aethyrdancer-aps`, which is the whole panel — so a 5%/4%/7% padding
 * put 70px top, 98px bottom and 56px a side on every opening whatever its size, which is
 * more than the height of the box itself. Absolute positioning instead: percentage
 * `max-height`, `top` and `bottom` on an absolutely positioned child resolve against THIS
 * box, which has a definite height.
 */
.aethyrdancer-ap {
	position: absolute;
	left: calc(var(--x) * 100%);
	top: calc(var(--y) * 100%);
	width: calc(var(--w) * 100%);
	height: calc(var(--h) * 100%);
	padding: 0;
	text-decoration: none;
	border-radius: 50%;
}

/*
 * HOW FAR THE HOLE'S MIDDLE IS FROM THE BOX'S, as a share of the box. A box is fixed by
 * EXTREMES, so one spiky brick on one side moves it while the opening the eye sees stays
 * where it was. The vertical share is damped to a third: the horizontal error is what the
 * eye catches, and the vertical is mostly the arch pulling the centroid up out of a shape
 * whose lower half is where the garment wants to hang anyway.
 */
.aethyrdancer-ap,
.aethyrdancer-capbox {
	--offx: calc((var(--fx, 0.5) - 0.5) * 100%);
	--offy: calc((var(--fy, 0.5) - 0.5) * 33%);
}

/*
 * THE NAME IS PAINTED ON THE WALL, NOT ON THE THING BEHIND IT.
 *
 * `.aethyrdancer-aps` is a stacking context, so nothing inside it can paint above the wall, and a name
 * that overflows its opening — "Otherdimensional Visions" does, by design — would be sliced
 * off by masonry at both ends. The caption gets its own box in `.aethyrdancer-labels`, above every
 * layer that moves, with the aperture's own vars on it so it resolves against the same
 * rectangle. Pointer-events stay off the whole way down so the layer cannot intercept a
 * hover meant for the opening beneath it; the caption's own link takes them back for itself
 * alone — see `a.aethyrdancer-cap__link` below.
 */
.aethyrdancer-capbox {
	position: absolute;
	left: calc(var(--x) * 100%);
	top: calc(var(--y) * 100%);
	width: calc(var(--w) * 100%);
	height: calc(var(--h) * 100%);
	pointer-events: none;
}

/*
 * WHAT IS IN AN OPENING IS CLIPPED TO THE OPENING, NOT TO THE BOX AROUND IT.
 *
 * The mask is `img/mask/<wall>-<slug>.png`, cut by build_wall.py as `255 - alpha` over the
 * component the box was measured from, so the panel and the mask sum to 255 through the
 * softened rim and there is no edge either can be off by. It is what lets items drift
 * THROUGH an opening rather than across a rectangle.
 *
 * THE GLOW MUST STAY OUTSIDE THIS ELEMENT. `.aethyrdancer-ap::after` is the hover light, and masking
 * it would clip the spill to the hole, which is the one thing the spill is not supposed to
 * do. So the mask goes on the wrapper and the pseudo-element remains a child of the link.
 */
.aethyrdancer-gm {
	/* Moving images must never change the aperture hit target. */
	pointer-events: none;
	position: absolute;
	inset: 0;

	/*
	 * ABOVE THE HOVER LIGHT. The glow is a positioned pseudo-element, so in paint order it
	 * lands after the image and washes over the garment. Note what is NOT done here:
	 * `isolation: isolate` on the link would also reorder them, and it would break the glow,
	 * because `mix-blend-mode: screen` blends against the nearest stacking context.
	 */
	z-index: 1;

	/* `var(--hole, none)` rather than a bare var: an aperture with no mask built yet computes
	   to the initial value and goes unclipped, instead of the whole declaration falling to
	   guaranteed-invalid and taking the positioning with it. */
	mask: var(--hole, none) no-repeat center / 100% 100%;
}

/*
 * THE GARMENT IS SIZED BY THE OPENING'S HEIGHT, WHICH IS THE SCARCE DIMENSION.
 *
 * The cutouts are portrait and an aperture is wider than it is tall by about 1.23:1, so
 * width is never the binding constraint. Centred at 46% rather than 50% so the name has the
 * foot of the opening to sit in; 88% rather than 92% because the centroid offset moves the
 * garment up in most openings and at 92 the tallest cutouts cleared the top of their own box
 * by 1-3px.
 *
 * `translate` centres the item and `transform` carries the drift: separate properties
 * composed in that order, so the script writes one `transform` per item per frame and never
 * touches the centring the stylesheet already worked out. Both are compositor properties.
 */
.aethyrdancer-ap img {
	position: absolute;
	left: calc(50% + var(--offx));
	top: calc(46% + var(--offy));
	translate: -50% -50%;
	max-width: 62%;
	max-height: 88%;
	object-fit: contain;

	/* The garment is separated from its own world by light, not by a plate. */
	filter: drop-shadow(0 2px 10px rgb(3 3 8 / 85%))
		drop-shadow(0 0 22px rgb(from var(--accent) r g b / 55%));

	/* The scale rides on `transform`, which the drift is already writing, so it is applied in
	   the script rather than here — declaring it in CSS would overwrite the position every
	   hover. This is only the timing. */
	transition: transform 260ms cubic-bezier(0.22, 0.61, 0.36, 1);

	/* Its transform changes every frame the wall is on screen; say so, so the browser gives
	   it a layer of its own up front instead of promoting it mid-drift. */
	will-change: transform;
}

/*
 * THE DEPARTURE — see `depart()` in landing.js.
 *
 * The chosen opening's world goes fully sharp and its light stays up; everything else on
 * the wall drops half a step so the eye is left holding one picture. All opacity, all
 * transitioned, and the end state is one the page can sit in for as long as the next
 * document takes to arrive.
 */
.aethyrdancer-ap,
.aethyrdancer-wd,
.aethyrdancer-capbox {
	transition: opacity 480ms ease;
}

.aethyrdancer-wallframe.is-departing .aethyrdancer-ap:not(.is-departing),
.aethyrdancer-wallframe.is-departing .aethyrdancer-wd:not(.is-chosen),
.aethyrdancer-wallframe.is-departing .aethyrdancer-capbox:not(.is-departing) {
	opacity: 0.45;
}

.aethyrdancer-wd.is-chosen .aethyrdancer-w-far {
	opacity: 0;
}

/*
 * THE PUSH-THROUGH LAYER — built by depart() in landing.js, removed on pageshow.
 *
 * Fixed over the whole page and above everything on it. `__hole` is the opening's world
 * clipped by the opening's own mask, laid out at its final size and scaled down to start;
 * `__bleed` is the same art edge to edge, faded up through the second half so the end
 * state is the artwork whatever the mask's exact outline. Both are `cover`, both are
 * compositor-only to animate. The garment clone inside `__hole` is placed by the same
 * rule the opening uses, since the layer carries the opening's variables.
 */
.aethyrdancer-through {
	position: fixed;
	inset: 0;
	z-index: 40;
	pointer-events: none;
	overflow: hidden;
	/* The opening's centroid offsets, exactly as `.aethyrdancer-ap` derives them. */
	--offx: calc((var(--fx, 0.5) - 0.5) * 100%);
	--offy: calc((var(--fy, 0.5) - 0.5) * 33%);
}

.aethyrdancer-through__bleed {
	position: absolute;
	inset: 0;
	background: no-repeat center / cover;
	opacity: 0;
}

.aethyrdancer-through__hole {
	position: absolute;
	background: no-repeat center / cover;
	mask: var(--hole, none) no-repeat center / 100% 100%;
	transform-origin: 50% 50%;
	will-change: transform;
}

.aethyrdancer-through__hole img {
	position: absolute;
	left: calc(50% + var(--offx));
	top: calc(46% + var(--offy));
	translate: -50% -50%;
	max-width: 62%;
	max-height: 88%;
	object-fit: contain;
	filter: drop-shadow(0 2px 10px rgb(3 3 8 / 85%))
		drop-shadow(0 0 22px rgb(from var(--accent, #8886de) r g b / 55%));
}

.aethyrdancer-capbox .aethyrdancer-meta {
	transition: opacity 400ms ease;
}

/* The garment's name leaves with the garment. The collection's name stays: it is the title
   of the page being travelled to. */
.aethyrdancer-capbox.is-departing .aethyrdancer-meta {
	opacity: 0;
}

.aethyrdancer-is-drifting .aethyrdancer-ap img {
	transition: none;
}

/*
 * Hover and focus add light rather than a border, because the wall's whole language is light
 * through openings. Screen, so it lifts the masonry the way the baked spill does instead of
 * painting a disc over it.
 *
 * LIGHT BY PROXIMITY, NOT ONLY BY POINTER: `--near` runs 0 to 1 as the closest item
 * approaches the middle of its opening, so a breach brightens as something passes whether or
 * not anyone is pointing at it. Still an opacity on a gradient that already existed, so it
 * stays free; the alternative was a filter, and a filter here is a paint per frame per
 * opening.
 */
.aethyrdancer-ap::after {
	content: "";
	position: absolute;
	inset: -55%;
	border-radius: 50%;
	background: radial-gradient(closest-side, rgb(from var(--spill) r g b / 55%), transparent 72%);
	mix-blend-mode: screen;
	opacity: calc(var(--near, 0) * var(--near-light, 0.72));
	transition: opacity 140ms ease;
	pointer-events: none;
}

.aethyrdancer-ap:hover::after,
.aethyrdancer-ap:focus-visible::after,
.aethyrdancer-ap.is-lit::after {
	opacity: 1;
}

.aethyrdancer-ap:focus-visible {
	outline: 2px solid var(--ad-accent-soft);
	outline-offset: 6px;
}

/* A CLOSED COLLECTION IS A div, NOT A DISABLED LINK, so it never enters the tab order — the
   same decision the collections index already made. */
.aethyrdancer-ap.is-closed {
	cursor: default;
}

.aethyrdancer-ap.is-closed::after {
	display: none;
}

/*
 * AN OPENING'S NAME GOES INSIDE IT — immediately under the opening, not across it.
 *
 * `.aethyrdancer-capbox` measures exactly the aperture's bounding box, so hanging the caption off its
 * bottom edge takes nothing from the opening: the garment keeps the whole hole and the name
 * sits clear of it rather than lying across the print it is there to sell.
 *
 * THE GAP IS A FRACTION OF THE OPENING, BECAUSE THE LIGHT IS TOO. The spill baked into the
 * wall falls off over `radius * SPILL_REACH`, so the lit masonry runs to roughly 15% of this
 * box's height below the hole. A fixed `0.5rem` ignored all of it and on the largest opening
 * the name cleared the light entirely and sat on unlit brick.
 */
.aethyrdancer-cap {
	position: absolute;
	left: var(--offx);
	right: calc(-1 * var(--offx));
	top: calc(var(--foot, 1) * 100% + var(--capgap, 2%));
	z-index: 2;
	text-align: center;
	pointer-events: none;
}

/*
 * THE NAME IS A TARGET, AND ONLY THE NAME IS.
 *
 * Pointer events are off from `.aethyrdancer-labels` down so that hovering a name still
 * lights the opening under it rather than being caught by the layer above. A descendant can
 * take them back without giving them to its ancestors, so the link — and nothing else in the
 * caption — is hittable. `inline-block` rather than the block `a.aethyrdancer-tx` gives the
 * CTA: a block anchor would span the whole aperture box and swallow clicks on the masonry
 * either side of a short name.
 *
 * `.aethyrdancer-ap.is-lit` above is how this keeps the breach lighting under the pointer;
 * landing.js mirrors the hover across, because no selector reaches from a caption back to
 * the opening it names.
 */
a.aethyrdancer-cap__link {
	display: inline-block;
	pointer-events: auto;
	text-decoration: none;
}

/* An opening low on the panel would hang its name off the bottom edge of the wall, so it
   goes above instead — the same flip `.aethyrdancer-lb` makes, at the same threshold, into the same
   spill, which is radial and so reaches as far up as it does down. */
.aethyrdancer-cap.is-above {
	top: auto;
	bottom: calc((1 - var(--head, 0)) * 100% + var(--capgap, 2%));
}

/*
 * NAMES ARE SIZED BY HEIGHT, NOT BY THE WIDTH OF THE OPENING THEY SIT IN.
 *
 * Width-driven, every name fills its own aperture, so the longest name on the wall was the
 * least readable. These are ten navigation labels: they are a set, and a set has one size.
 * Cap height is fixed against the panel's own width, so they shrink together as the wall
 * does, and a long name is simply wider — overflowing onto the masonry, which reads as a
 * caption rather than as a mistake.
 */
.aethyrdancer-cap .aethyrdancer-tx svg.aethyrdancer-compose,
.aethyrdancer-lb .aethyrdancer-tx svg.aethyrdancer-compose {
	width: auto;

	/* The multiplier turns a cap height into a box height — see --ad-box-per-cap in the
	   renderer. Without it a 19px rule renders about six pixels of letter. */
	height: calc(clamp(11px, 1.25cqw, 20px) * var(--ad-box-per-cap, 3));
	margin: 0 auto;
}

/* NO max-width on a caption inside an opening: a long name is simply wider than its hole and
   overflows onto the masonry. Clamping it instead shrinks the longest names — exactly the
   ones that most need the size. A crack's label is on bare wall and does get one. */
.aethyrdancer-lb .aethyrdancer-tx svg.aethyrdancer-compose {
	max-width: 100%;
}

.aethyrdancer-cap .aethyrdancer-meta {
	margin-top: 0.15rem;
	font-size: clamp(0.5rem, 0.62cqw, 0.7rem);
	line-height: 1.25;
	letter-spacing: 0.05em;
	color: var(--ad-text-strong);
	text-shadow: 0 0 5px rgb(3 3 8 / 96%), 0 0 12px rgb(3 3 8 / 90%), 0 1px 2px rgb(3 3 8);

	/*
	 * OFF, as it was in the rig by default. The wall's job is to get someone into a
	 * collection, and a name plus a silhouette does that. Adding the product's own name and
	 * price put three wrapped lines into an opening 195px wide, which crowded out the garment
	 * they described — and the price belongs on the page that can also carry the sizing
	 * notice. The node stays because the drift loop rewrites it as the lead item changes, and
	 * because a name in the DOM is a name a screen reader can reach.
	 */
	display: none;
}

/*
 * A CRACK IS THE EXCEPTION, and has to be: there is no opening to write on, so its name sits
 * on the masonry beside it. That is also the honest signal — the one collection whose label
 * is not in a hole is the one you cannot go into.
 */
.aethyrdancer-lb {
	position: absolute;
	left: calc(var(--cx) * 100%);
	top: calc((var(--y) + var(--h)) * 100%);
	translate: -50% 0.5rem;
	width: max(8rem, 15%);
	text-align: center;
}

.aethyrdancer-lb.is-above {
	top: calc(var(--y) * 100%);
	translate: -50% calc(-100% - 0.5rem);
}

.aethyrdancer-lb .aethyrdancer-meta {
	margin-top: 0.15rem;
	font-size: 0.72rem;
	letter-spacing: 0.06em;
	color: rgb(from var(--accent) r g b / 92%);
	text-shadow: 0 0 8px rgb(3 3 8 / 92%), 0 0 18px rgb(3 3 8 / 80%);
}

/*
 * LETTERING HOSTS: NATURAL SIZE, SHRINKING TO FIT.
 *
 * `width: 100%` was the first rule and it made the font size do nothing: the element filled
 * its container whatever the viewBox held, and raising the size widens the viewBox faster
 * than it heightens it, so bigger letters rendered smaller. Natural width with `max-width`
 * and a declared `aspect-ratio` instead — one canvas unit renders as one pixel, and when the
 * container is narrower the width clamps and the height follows the ratio down. That is the
 * theme's own `fit: fluid`.
 */
.aethyrdancer-tx {
	line-height: 0;
}

/* Until the renderer runs, these hosts hold plain text — the copy is in the markup so a
   crawler and a failed script both get it. The line-height above would collapse it. */
.aethyrdancer-tx:not(:has(svg)) {
	line-height: 1.25;
}

.aethyrdancer-tx svg.aethyrdancer-compose {
	display: block;
	width: calc(var(--ad-nat-h, 60px) * var(--ad-aspect, 4));
	max-width: 100%;
	height: auto;
	aspect-ratio: var(--ad-aspect, 4);
	margin: 0 auto;
	overflow: visible;
}

a.aethyrdancer-tx {
	display: block;
	text-decoration: none;
}

.aethyrdancer-landing-fallback {
	margin: 0;
	padding: 0.75rem 1rem;
	text-align: center;
	color: var(--ad-text-muted);
	background: rgb(10 9 18 / 80%);
}

/*
 * EVERY EFFECT DEGRADES — principle 5.
 *
 * Reduced motion gets the rest state, not the middle of the move. Freezing halfway was the
 * first attempt and it is the one state that carries less than either end: by then the
 * headline has faded, so the still was a wall, a hole and no words. Rest carries everything
 * and is identical to what a failed script leaves behind, so there is one fallback to check
 * rather than two.
 */
@media (prefers-reduced-motion: reduce) {
	.aethyrdancer-stage {
		height: calc(100vh - var(--ad-head)) !important;
	}

	.aethyrdancer-scene {
		position: relative;
	}

	.aethyrdancer-logo-stack__strip {
		animation: none;
	}

	.aethyrdancer-ap img,
	.aethyrdancer-ap::after,
	.aethyrdancer-wallfoot,
	.aethyrdancer-landing-nav a {
		transition: none;
	}

	/* No parallax: the world sits still behind its opening. The script never writes the
	   pointer numbers under reduced motion either; this is the belt to that brace. */
	.aethyrdancer-wd > div {
		transform: none;
		transition: none;
	}
}

/*
 * A PORTRAIT VIEWPORT NEEDS PORTRAIT PANELS. THIS IS NOT A REFINEMENT.
 *
 * The near panel is landscape at 1.56:1 and drawn 1:1 so its bricks match the site. Cover a
 * 390x844 phone with it and the aperture goes to nearly twice the width of the screen: the
 * visitor arrives already inside the hole, with no wall anywhere and so nothing to have
 * broken through. Measured on a capture, not predicted. The far wall fails the same way in
 * reverse — ten openings across a 2000px panel fitted to 390px are 35px each.
 *
 * Two more runs of the same generators, served by media query. landing.js watches the same
 * 900px breakpoint and rebuilds the wall from the other manifest when it is crossed.
 */
@media (max-width: 900px) {
	:root {
		--ad-head: 210px;
	}

	/*
	 * THE WIDTH IS ON THE STACK, NOT ON THE MARK — see the same note in logo.css.
	 *
	 * `__anim` and `__designs` are `inset: 0` on the stack, so capping only `__top` left the
	 * artwork window and the bevelled DESIGNS layer at the full width of the masthead while the
	 * wordmark stopped at 330px. Both layers carry the mark's colour and its edge, and both were
	 * rendering off register with the letterforms. A definite length rather than a percentage:
	 * the anchor above it is shrink-to-fit inside the rider's grid, so a percentage here has
	 * nothing settled to resolve against.
	 */
	.aethyrdancer-masthead .aethyrdancer-logo-stack {
		display: block;
		width: min(330px, 92vw);
		max-width: 100%;
	}

	/* The departing, enlarged logo must not widen the scrollable page. */
	.aethyrdancer-masthead {
		overflow-x: clip;
		overflow-y: visible;
	}

	.aethyrdancer-masthead .aethyrdancer-logo-stack__top {
		height: auto;
		width: 100%;
		max-width: 100%;
	}

	.aethyrdancer-masthead .aethyrdancer-logo-stack__top .aethyrdancer-wordmark {
		height: auto;
		width: 100%;
	}

	/*
	 * THE MARK GETS OUT OF THE NAVIGATION'S WAY RATHER THAN SHARING THE LINE WITH IT.
	 *
	 * At 375px the mark is 330px wide and the icon cluster is about 250px: there is no
	 * arrangement of the two on one row that leaves either legible. The nav keeps the top
	 * right — it is the thing that has to be reachable — and the mark drops below it, which
	 * the header has room for because it is 210px tall and the mark is about 47.
	 */
	.aethyrdancer-masthead__rider {
		place-items: end center;
		padding-bottom: clamp(1.5rem, 6vh, 3.5rem);
	}

	.aethyrdancer-viewbox {
		width: 780px;
		height: 1560px;
		transform-origin: 49.8% 40.1%;
	}

	.aethyrdancer-layer {
		transform-origin: 49.8% 40.1%;
	}

	.aethyrdancer-panel {
		background-image: var(--ad-landing-panel-portrait);
	}

	.aethyrdancer-lede {
		top: calc(50% - 156px);
	}

	.aethyrdancer-wallimg {
		background-image: var(--ad-landing-wall-portrait);
	}

	.aethyrdancer-lb {
		width: max(7rem, 30%);
	}

	/* The closing link sits above the phone's own browser chrome rather than under it. */
	.aethyrdancer-wallfoot {
		position: relative;
		bottom: auto;
		opacity: 1;
		padding: 1rem 1.5rem 2rem;
		pointer-events: auto;
	}

	.aethyrdancer-wallfoot[data-idle="1"] {
		pointer-events: auto;
	}

	.aethyrdancer-wallfoot-spacer {
		display: none;
	}

	.aethyrdancer-landing-nav {
		left: max(12px, env(safe-area-inset-left));
		right: max(12px, env(safe-area-inset-right));
		top: max(12px, env(safe-area-inset-top));
	}

	.aethyrdancer-landing-nav ul {
		justify-content: space-around;
		background: rgb(3 3 8 / 88%);
	}

	.admin-bar .aethyrdancer-landing-nav {
		top: calc(46px + max(12px, env(safe-area-inset-top)));
	}

	.aethyrdancer-landing-nav .aethyrdancer-navsym__label {
		font-family: system-ui, sans-serif;
		font-size: 11px;
		line-height: 1.35;
		color: var(--ad-text-strong);
	}

	.aethyrdancer-landing-nav a {
		color: var(--ad-text-strong);
		min-height: 52px;
	}

	.aethyrdancer-lb .aethyrdancer-meta {
		color: var(--ad-text);
		font-family: system-ui, sans-serif;
		font-size: 12px;
		white-space: nowrap;
	}

	/* Keep camera geometry stable when the phone's address bar retracts. */
	@supports (height: 100svh) {
		.aethyrdancer-scene {
			height: 100svh;
		}

		.aethyrdancer-masthead__rider {
			transform-origin: 50% 50svh;
		}
	}

	@media (prefers-reduced-motion: reduce) {
		.aethyrdancer-stage {
			height: 100svh !important;
		}
	}
}
