/**
 * 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;
}

/* A window is open. showModal() makes the page behind it inert to the pointer
   and to the keyboard but not to the wheel, and a page that scrolls under a
   window it cannot touch is a page that has come loose. The reserved gutter
   above is what keeps this from shifting the layout sideways. */
html.has-modal {
	overflow: hidden;
}

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. */
/* The tile itself is a custom property and not a background declaration,
   because one thing on this site is painted above the sheet below: a
   <dialog> opened with showModal() is in the top layer, which is over every
   z-index there is, this one included. So the window carries its own grain
   from this same property — one tile, one definition, two users. */
:root {
	--l-grain-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");
}

.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: var(--l-grain-image);
	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;
	/* Taller again, and the arc is what pays for it. The height was cut to 9vh
	   because a straight band that deep left every paragraph being read through
	   a smudge — but the arc takes its depth out of the middle of the screen,
	   which is exactly where those paragraphs are. The corners get the height
	   back and the reading column keeps less haze than it had at 9vh. */
	block-size: var(--l-floor-height);
	pointer-events: none;

	/* The soft top edge, and it is not optional.
	 *
	 * An arc centred on the top edge is transparent at exactly one point — the
	 * middle — and already deep at the two top corners, because distance from
	 * that centre grows sideways as fast as it grows downward. The band's own
	 * top border then cuts the haze off mid-strength across the width, and
	 * that cut is a **hard line**. No stop tuning fixes it: pulling the corners
	 * down to transparent means widening the ellipse until the arc is a
	 * straight edge again, which is the thing being replaced.
	 *
	 * So shape and softness are two masks, not one. The arc says how much haze
	 * belongs at each column; this says every column arrives at it gradually.
	 * They are intersected, so the answer is the smaller of the two. */
	--edge-fade: var(--l-floor-fade);
}

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

/* The masks are radial, not linear, and that one word is the whole curve.
 *
 * The ellipse is centred on the **top edge, at the middle** — outside the band
 * looking down into it — with a horizontal radius narrower than the window.
 * A point's distance from that centre therefore grows both downward *and*
 * sideways, so the height at which a pane switches on is reached lower in the
 * middle of the screen and higher at the corners. The boundary that falls out
 * of it is an ellipse arc: the floor dips in the centre and climbs at the
 * edges, a circle stretched wide and half-buried.
 *
 * Centring it below instead — the obvious first try — draws the same arc the
 * other way up, a dome, which is a hill in the middle of the screen.
 *
 * The stops are the old linear ones read from the other end. `transparent 42%`
 * here is the pane that used to stop 58% of the way up: the distance is
 * measured from the top now, so the two numbers are complements.
 *
 * And it is on each pane rather than once on the parent, deliberately: a mask
 * on `.lamp-edge` would make it a backdrop root, and a backdrop-filter inside
 * a backdrop root has nothing behind it to blur. The whole effect would go
 * quietly transparent.
 */
/* Two mask layers on every pane: the arc, then the fade, intersected. */
.lamp-edge i {
	-webkit-mask-composite: source-in;
	mask-composite: intersect;
}

.lamp-edge i:nth-child(1) {
	-webkit-backdrop-filter: blur(2px);
	backdrop-filter: blur(2px);
	-webkit-mask-image: radial-gradient(var(--l-floor-arc) 100% at 50% 0%, transparent 0%, #000 100%), var(--edge-fade);
	mask-image: radial-gradient(var(--l-floor-arc) 100% at 50% 0%, transparent 0%, #000 100%), var(--edge-fade);
}

.lamp-edge i:nth-child(2) {
	-webkit-backdrop-filter: blur(8px);
	backdrop-filter: blur(8px);
	-webkit-mask-image: radial-gradient(var(--l-floor-arc) 100% at 50% 0%, transparent 42%, #000 100%), var(--edge-fade);
	mask-image: radial-gradient(var(--l-floor-arc) 100% at 50% 0%, transparent 42%, #000 100%), var(--edge-fade);
}

.lamp-edge i:nth-child(3) {
	-webkit-backdrop-filter: blur(22px);
	backdrop-filter: blur(22px);
	-webkit-mask-image: radial-gradient(var(--l-floor-arc) 100% at 50% 0%, transparent 74%, #000 100%), var(--edge-fade);
	mask-image: radial-gradient(var(--l-floor-arc) 100% at 50% 0%, transparent 74%, #000 100%), var(--edge-fade);
}

/* Painted after the panes, because it has no z-index of its own and comes
   last in the box.

   Near-black rather than the ground's own colour. A floor the same shade as
   the room reads as a smudge laid on the room; darker, it reads as the room
   ending — and the light in the middle of the page has something to fall off
   into. The stops repeat --l-floor's channels rather than mixing it, matching
   how the rest of this file spells a token with an alpha on it. */
.lamp-edge::after {
	content: "";
	position: absolute;
	inset: 0;
	/* The same ellipse as the panes, so the tint follows the arc instead of
	   laying a straight band across a curved blur. The stops repeat
	   --l-floor's channels rather than mixing it, matching how the rest of
	   this file spells a token with an alpha on it.
	   It never reaches full opacity: a floor that goes solid is a strip of a
	   different colour along the bottom of the window, and this is haze. */
	/* The colour travels as well as the opacity: near-black where the haze
	   begins, the room's own grey where it ends. Over a near-black page the
	   first stops are invisible — there is no colour change to find an edge
	   at — and by the bottom the tint *is* the page, so a picture pinned
	   behind it is covered rather than tinted.

	   Every stop is the same colour — --l-floor's channels, which are the
	   room's — and only the alpha moves. `transparent` is not used for the
	   zero stop because `transparent` is transparent *black*, and fading from
	   it drags the middle of the ramp towards black on the way down. */
	background: radial-gradient(
		var(--l-floor-arc) 100% at 50% 0%,
		rgb(28 28 26 / 0) 0%,
		rgb(28 28 26 / 0.45) 44%,
		rgb(28 28 26 / 0.85) 76%,
		var(--l-floor) 100%
	);
	/* One layer, so no compositing to do: the tint is a background and the
	   fade is its mask. Same fade as the panes, so the tint and the blur are
	   never softening on different schedules. */
	-webkit-mask-image: var(--edge-fade);
	mask-image: var(--edge-fade);
}

/* With a picture behind the page, the floor falls into black instead.
 *
 * The settle colour is «whatever is down there», and over a film that is not
 * the room: the room's grey laid over a lit frame is a grey veil on somebody's
 * footage, and it reads as a smudge exactly the way the first floor did on the
 * room. Black is what a picture ends into.
 *
 * It cuts rather than fades, with the scene it belongs to. «القطع لا التلاشي»
 * — the floor changing on its own schedule would be a second thing moving. */
.lamp-scene .lamp-edge::after {
	background: radial-gradient(
		var(--l-floor-arc) 100% at 50% 0%,
		rgb(0 0 0 / 0) 0%,
		rgb(0 0 0 / 0.45) 44%,
		rgb(0 0 0 / 0.85) 76%,
		#000 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. It comes
   after the rule above deliberately: a pale ground is a scene too, and this is
   the more specific thing to say about it. */
.lamp-pale .lamp-edge::after {
	background: radial-gradient(
		var(--l-floor-arc) 100% at 50% 0%,
		transparent 0%,
		rgb(255 255 255 / 0.12) 52%,
		rgb(255 255 255 / 0.34) 100%
	);
	-webkit-mask-image: var(--edge-fade);
	mask-image: var(--edge-fade);
}

/* 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;
	}
}
