/**
 * STRUCTURE — لمبة.
 *
 * The frame, the primitives, the room and the grain. Colour and type live in
 * identity.css and reach this file only as tokens; nothing below names a
 * colour or a family directly.
 *
 * Diverged from the starter's base.css on purpose: this site is a dark room
 * with a light in it, and the room is structural, not decorative. Three fixed
 * layers do all of it, and each one only ever animates `opacity`, so the
 * whole effect stays on the compositor.
 */

*,
*::before,
*::after {
	box-sizing: border-box;
}

html {
	/* So overscroll shows the room and not the browser's white. */
	background: var(--l-ground);

	/* The gutter is reserved whether or not there is a scrollbar. Two things
	   depend on it: the page does not jump sideways when the gate unlocks
	   scrolling, and Athar's scrollbar probe measures the same number before
	   and after, which is what keeps full-bleed sections exactly one viewport
	   wide instead of one viewport plus a scrollbar. */
	scrollbar-gutter: stable;
}

body {
	margin: 0;
	background: var(--l-ground);
	color: var(--l-text);
	font-family: var(--l-font);
	font-size: var(--l-t-body);
	font-weight: var(--l-w-body);
	line-height: var(--l-lh-body);
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

img,
svg,
video {
	max-inline-size: 100%;
	block-size: auto;
}

a {
	color: inherit;
}

:where(a, button, [tabindex]):focus-visible {
	outline: 2px solid var(--l-yellow);
	outline-offset: 3px;
	border-radius: var(--l-radius-s);
}

/* ==================================================================== room

   Three fixed layers, in this order, all inert to the pointer:

     .lamp-room   black, opacity 1 - lit. The dark itself.
     .lamp-edge   the floor: progressive blur and a gradient into it.
     .lamp-grain  the texture, constant.

   There is no page-wide warm layer, and that is the point. A translucent
   yellow plane over a near-black ground is not light: it renders as olive
   sludge, and being a plane it reads as a stack of layers rather than as
   illumination. Light belongs to whatever is emitting it, so the glow lives
   on the lamp itself and on the cards that light up. Only the dark and the
   grain are page wide, because those genuinely are.

   The room sits above the page rather than behind it because darkness is not
   a background: it has to fall on the content too. At --lit: 1 the room's
   opacity is 0, so a lit page pays for nothing but the two remaining layers.
   ======================================================================= */

.lamp-room,
.lamp-grain {
	position: fixed;
	inset: 0;
	pointer-events: none;
	/* Fixed layers stay put while the page moves under them; without this the
	   grain swims on iOS during momentum scroll. */
	will-change: opacity;
}

.lamp-room {
	z-index: 60;
	background: var(--l-black);
	opacity: calc((1 - var(--lit)) * var(--l-dark));
}

/* Computed, not a file. An feTurbulence tile costs one parse and no request,
   scales to any viewport and cannot 404.

   Two things changed on 2026-08-24, because the grain was in the markup and
   not on the screen:

   1. `overlay` cannot show grain on this ground, and no amount of opacity
      fixes it. Overlay multiplies wherever the destination is below mid-grey,
      and this room is 0.11 — so every grain of it was being multiplied into
      near-black by near-black. `screen` is the blend for a dark room: it
      lightens by the source and ignores it where it is black.

   2. Which is only useful if the noise is mostly black. Raw fractalNoise sits
      around mid-grey, and screening mid-grey over a dark room is a haze, not
      a grain. The gamma curve below pushes the bulk of it down to nothing and
      leaves sparse highlights standing — and sparse highlights on a dark
      ground is what film grain actually is.

   The colour matrix desaturates first (RGB noise would speckle in colour) and
   forces alpha to 1, so the layer's own opacity is the only dial. */
.lamp-grain {
	/* Over everything, gate included. Grain that stops at a layer boundary
	   announces the boundary. */
	z-index: 96;
	opacity: var(--l-grain-opacity);
	mix-blend-mode: screen;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0.33 0.33 0.33 0 0 0.33 0.33 0.33 0 0 0.33 0.33 0.33 0 0 0 0 0 0 1'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='gamma' exponent='3'/%3E%3CfeFuncG type='gamma' exponent='3'/%3E%3CfeFuncB type='gamma' exponent='3'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
	background-size: var(--l-grain-size) var(--l-grain-size);
}

/* ------------------------------------------------------------- the floor

   The bottom of the window, softened.

   Progressive blur: three panes, each blurring more than the last and each
   fading out higher up, so what is behind them melts toward the floor instead
   of meeting a line where a blur begins. One pane cannot do this — a single
   backdrop-filter has exactly one radius, and masking a uniform blur only
   fades a smudge in, which is the thing that reads as cheap.

   Lifted from ديلما, where it was built and where the pane count was argued
   down from six to three: three read the same and cost half. If it ever needs
   to be cheaper still, the 22px radius goes before a pane does — the ramp is
   what makes it progressive, and two panes with a ramp still work.

   The gradient rides on top rather than underneath, so it darkens the result
   of the blur and not its input. It is what stops a picture pinned behind the
   page from simply ending at the floor of the window.
   ==================================================================== */

.lamp-edge {
	position: fixed;
	inset-inline: 0;
	inset-block-end: 0;
	z-index: 80;
	/* Shorter than it was. At 15vh the bottom sixth of every screen was
	   permanently softened, and a paragraph passing through it was being read
	   through a smudge. It is an edge, not a treatment. */
	block-size: clamp(4rem, 9vh, 8rem);
	pointer-events: none;
}

.lamp-edge i {
	position: absolute;
	inset: 0;
	display: block;
}

.lamp-edge i:nth-child(1) {
	-webkit-backdrop-filter: blur(2px);
	backdrop-filter: blur(2px);
	-webkit-mask-image: linear-gradient(to top, #000 0%, transparent 100%);
	mask-image: linear-gradient(to top, #000 0%, transparent 100%);
}

.lamp-edge i:nth-child(2) {
	-webkit-backdrop-filter: blur(8px);
	backdrop-filter: blur(8px);
	-webkit-mask-image: linear-gradient(to top, #000 0%, transparent 58%);
	mask-image: linear-gradient(to top, #000 0%, transparent 58%);
}

.lamp-edge i:nth-child(3) {
	-webkit-backdrop-filter: blur(22px);
	backdrop-filter: blur(22px);
	-webkit-mask-image: linear-gradient(to top, #000 0%, transparent 26%);
	mask-image: linear-gradient(to top, #000 0%, transparent 26%);
}

/* Painted after the panes, because it has no z-index of its own and comes
   last in the box. The ground's own colour, so the floor is the room. */
.lamp-edge::after {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(
		to top,
		var(--l-ground) 0%,
		rgb(28 28 26 / 0.72) 34%,
		rgb(28 28 26 / 0.28) 66%,
		transparent 100%
	);
}

/* On a pale scene the floor is that scene's colour, not the room's — a dark
   band across the bottom of a yellow screen is a bar, not a horizon. */
.lamp-pale .lamp-edge::after {
	background: linear-gradient(
		to top,
		rgb(255 255 255 / 0.34) 0%,
		rgb(255 255 255 / 0.12) 48%,
		transparent 100%
	);
}

/* While the gate is up nothing behind it may scroll. */
.lamp-locked,
.lamp-locked body {
	overflow: hidden;
	touch-action: none;
}

/* =================================================================== frame */

/* The content column every template pours into, and the one Athar's sections
   measure themselves against. Athar has no width of its own: it reads the
   column it is placed in, which is this. */
.site-column {
	inline-size: min(100% - 2 * var(--l-gutter), var(--l-col));
	margin-inline: auto;
}

.site-main {
	display: block;
}

/* ============================================================ page headings */

.page-title {
	margin: 0 0 var(--l-rhythm-tight);
	font-family: var(--l-display);
	font-size: var(--l-t-h1);
	font-weight: var(--l-w-display);
	letter-spacing: var(--l-track-display);
	line-height: var(--l-lh-head);
	text-wrap: balance;
}

/* A composed page draws its own opening, so the theme's title would be a
   second one. functions.php marks those with .is-composed. */
.is-composed .page-title {
	position: absolute;
	inline-size: 1px;
	block-size: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

/* Skip link: visible only when it has the keyboard. */
.skip-link {
	position: fixed;
	inset-block-start: -100vh;
	inset-inline-start: var(--l-gutter);
	z-index: 200;
	padding: 0.7em 1.1em;
	border-radius: var(--l-radius-pill);
	background: var(--l-yellow);
	color: var(--l-black);
	font-weight: var(--l-w-mid);
	text-decoration: none;
}

.skip-link:focus {
	inset-block-start: var(--l-gutter);
}

/* =================================================================== motion

   One global switch. Everything below this line in every stylesheet is
   allowed to assume motion is wanted; this is where it is taken away. */

@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.001ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.001ms !important;
		scroll-behavior: auto !important;
	}
}
