/* Grid view - the "wall of work" layout axis (data-view='grid' on <html>).
   Loaded only when GRID_VIEW_ENABLED (config.php); the toggle lives in
   includes/settings/view-switcher.php and the wiring in settings-panel.js.

   The whole file is gated to >= 1600px: below that the grid doesn't exist and
   everyone gets the list, so a phone can never render a one-column "grid"
   that's just a worse list. Keep the 1600 here, in the FOUC script
   (includes/header.php), and in GRID_MIN (settings-panel.js) in sync.

   List view is the argument (a readable spine); grid view is the evidence
   (fifteen years of work at a glance). Cells keep their full card - date,
   title, media, description, read-more - just at column width; only motion
   stands down: loops don't autoplay in the grid (the guard is in
   includes/footer.php), stills carry the wall.

   In grid view the settings panel leaves its popover and sits UNBOXED on
   the wall's last lane - same width, same grid line, top shared with the
   intro in lane one - at regular scroll, so the controls and the first
   grid rows share the viewport and the playground moment (drag a slider,
   watch the wall respond) needs no sticky chrome. The JS swaps the popover
   attribute; everything visual lives here. */

/* The wall's geometry is three shared tokens, so every consumer (lanes,
   intro, minimap) reads the same truth instead of hand-syncing:
     --grid-columns  lane count - 2 from 1600, 3 on genuinely big monitors
     --wall-column   the designed lane measure; cards never stretch past it
     --wall-gap      the gutter between lanes
   Clear column widths constrain the layout: the wall's max-width derives
   from count x measure, so growing the window past a breakpoint adds a lane
   rather than inflating the cards. */
@media (width >= 1600px) {
	:root {
		--grid-columns: 2;
		--wall-column: 36rem;
		--wall-gap: 4rem;

		/* The wall's left rail: proportional, so the content isn't pinned
		   tight to the viewport edge on big screens. Extra width beyond the
		   constrained columns falls to the RIGHT (never a wider wall) - at
		   huge sizes a fourth column would also fit, but breathing room
		   beats being in their face; deliberate choice, revisit knowingly. */
		--wall-inset: 4vw;
	}
}

@media (width >= 1900px) {
	:root {
		--grid-columns: 3;
	}
}

/* The toggle only shows where choosing it can do something. */
.view-switcher {
	display: none;

	@media (width >= 1600px) {
		display: block;
	}
}

/* The grid invite - a one-tap door into grid view, sitting beside the
   settings trigger in the rail. Only exists where the grid exists, and only
   in list view (in grid view the rail's triggers hide wholesale). Until
   first used it pulses a soft accent halo - "touch me" - then settles into
   a plain toggle. The seen-state class comes from settings-panel.js
   (grid-invite-seen in localStorage). */
/* button-qualified to tie button.toolbox-trigger's display (settings-panel
   .css) so this later-loading hide wins - a bare .grid-invite loses on
   specificity and the invite leaks onto phones. */
button.grid-invite {
	display: none;
}

/* The corner island renders in the rail wherever the grid CAN exist, but it
   only ever SHOWS in grid view (rules in the [data-view='grid'] block below)
   - in list view the toolbox row already does its jobs. */
.corner-island {
	display: none;
}

@media (width >= 1600px) {
	/* List view = no data-view attribute (data-view is the single switch;
	   'grid' is its only value). In grid view the invite's job is done. */
	:root:not([data-view="grid"]) .grid-invite {
		display: inline-flex;

		opacity: 1;
		transition: opacity 400ms ease;

		@starting-style {
			opacity: 0;
		}

		&:not(.is-seen) {
			animation: grid-invite-pulse 2.2s ease-out infinite;
		}

		@media (prefers-reduced-motion: reduce) {
			&:not(.is-seen) {
				animation: none;
			}
		}
	}
}

@keyframes grid-invite-pulse {
	from {
		box-shadow: 0 0 0 0 color-mix(in oklch, var(--accent) 45%, transparent);
	}

	70%,
	to {
		box-shadow: 0 0 0 16px color-mix(in oklch, var(--accent) 0%, transparent);
	}
}

@media (width >= 1600px) {
	[data-view="grid"] {
		/* --- Page shell: the rail stops being a bar and parks its panel as a
		   card in the page's top-right corner, beside the intro (which keeps
		   its normal top-left position inside main). --- */

		.page-wrapper {
			display: block;
			position: relative;
		}

		.page-rail {
			position: absolute;

			/* Pinned to the same line the intro starts on (--page-top,
			   default-layout.css), and the LEFT edge sits on the wall's last
			   lane's grid line - the same arithmetic as the wall itself (its
			   inset start plus the lanes before the last one) - so the
			   controls are ON the grid, not floating at the viewport edge. */
			inset-block-start: var(--page-top);
			inset-inline-start: calc(max(var(--gutter), var(--wall-inset)) + (var(--grid-columns) - 1) * (var(--wall-column) + var(--wall-gap)));
			z-index: 5;
			padding: 0;
			background: none;
			border: 0;
		}

		/* The panel is inline now - its trigger has nothing to do. */
		.settings-trigger {
			display: none;
		}

		/* The panel: settings-panel.js removes the popover attribute in grid
		   view, making it a plain always-visible div. The popover styling in
		   settings-panel.css still applies (same class), so un-anchor it,
		   size it to exactly one lane, and UNBOX it - no fill, border, or
		   shadow; in the grid the controls sit on the page like content, the
		   lane alignment does the framing. opacity must be forced: a
		   non-popover never matches :popover-open, so the panel's
		   `&:not(:popover-open) { opacity: 0 }` would hide it.
		   :not([popover]) scopes all of this to the INLINE state - when the
		   island re-adds the attribute, the module's card chrome returns. */
		.toolbox-panel:not([popover]) {
			position: static;
			inset: auto;
			opacity: 1;
			width: var(--wall-column);
			min-width: 0;
			max-width: none;
			max-height: none;
			margin: 0;
			padding: 0;
			background: none;
			border: 0;
			border-radius: 0;
			box-shadow: none;
		}

		/* The island-opened popover: the module css would anchor it to the
		   rail (parked at the page TOP in grid view - off-screen once you've
		   scrolled), so pin it to the viewport beside the island instead. */
		.toolbox-panel[popover] {
			position: fixed;
			top: var(--page-top);
			right: calc(var(--gutter) + 3.5rem);
		}

		/* Inside the inline panel: the small controls stack in a left
		   column, the filter (slider + minimap schematic) takes a right
		   column at full height. The right column flexes because the panel
		   is a fixed lane width - a rigid minmax floor could overflow the
		   lane. The span count = the panel's control rows; a new row in
		   settings-panel.php means bumping it. The island-opened popover is
		   excluded - it keeps the module's single-column stack. */
		.settings-panel:not([popover]) {
			display: grid;
			grid-template-columns: max-content minmax(0, 1fr);
			gap: 1.25rem 2.5rem;
			align-items: start;
		}

		.settings-panel:not([popover]) .filter-control {
			grid-column: 2;
			grid-row: 1 / span 5;
		}

		/* --- The corner island: the conditional cluster (CLAUDE.md spec).
		   Exists only in grid view, and only while the inline panel is off
		   screen (.is-visible from the IntersectionObserver in
		   settings-panel.js). Fixed in the right margin the wall already
		   leaves empty - vertical, one circular button per job. Reserved
		   slots: members never appear/disappear individually, the whole
		   island does. --- */
		.corner-island {
			position: fixed;
			top: var(--page-top);
			right: var(--gutter);
			z-index: 6;

			display: flex;
			flex-direction: column;
			gap: 0.25rem;
			padding: 0.4rem;

			background: var(--fill-primary);
			border: 1px solid var(--stroke-primary);
			border-radius: 999px;

			opacity: 0;
			visibility: hidden;
			transition:
				opacity 200ms ease,
				visibility 200ms;

			&.is-visible {
				opacity: 1;
				visibility: visible;
			}
		}

		.corner-island .island-button {
			width: 2.4rem;
			height: 2.4rem;
			padding: 0;
			border-radius: 50%;
			border: 1px solid var(--stroke-secondary);
		}

		/* Intro, wall, and footer share one left edge, inset by the
		   proportional rail - everything on the page keeps the same width
		   situation, not just the timeline. */
		main,
		.site-footer {
			padding-inline-start: max(var(--gutter), var(--wall-inset));
		}

		/* The intro gets a reading measure (75ch), capped so it can never
		   cross into the panel's lane - the panel sits on the wall's LAST
		   lane and wears no card chrome, so an overrun doesn't stack behind
		   it, it interleaves with it (seen at the 2-column stage, where one
		   lane plus a gutter is narrower than 75ch). The cap is the room
		   before that lane starts, minus one gap of breathing space. */
		.page-header {
			max-width: min(75ch, calc((var(--grid-columns) - 1) * (var(--wall-column) + var(--wall-gap)) - var(--wall-gap)));
		}

		/* --- The wall itself. Cells keep the full card at column width. ---

		   Cards vary a lot in height (media vs text-only), and the wall must
		   read in a Z (row-major) - the reverse-chronology walks across the
		   columns, then down. CSS alone can't do both yet: multi-column packs
		   tight but flows DOWN each lane, and an aligned-rows grid keeps Z
		   order but leaves craters under short cards. So the packing is the
		   measured-row-span technique: this grid uses 1px auto-rows, and
		   scripts/grid-masonry.js gives each card a row span matching its
		   height (a ResizeObserver keeps spans true when brand scales, media
		   loads, or an unfolding read-more change heights). Grid
		   auto-placement then walks the cards left-to-right - the Z.

		   Without the script (its .is-packed class), the fallback is the
		   aligned-rows grid: correct order, craters accepted. When native
		   masonry ("grid lanes") ships, it replaces the script wholesale. */

		[role="list"].timeline {
			display: grid;
			grid-template-columns: repeat(var(--grid-columns), 1fr);
			column-gap: var(--wall-gap);
			row-gap: 4rem;

			/* Count x measure: lanes hold the designed width instead of
			   stretching to fill whatever screen this is. */
			max-width: calc(var(--grid-columns) * var(--wall-column) + (var(--grid-columns) - 1) * var(--wall-gap));

			/* Clearance for the absolute panel above-right: the first row
			   must start below it, and the intro alone isn't reliably
			   taller than the panel (brand scales move both). HEURISTIC -
			   the panel is absolute so nothing pushes; this token is sized
			   against the tallest current panel plus breathing room before
			   the wall. If the panel grows a row, re-check it. The honest
			   fix (top section as a real grid row that owns its height) is
			   sketched in the session notes - do that before this number
			   bites a third time. */
			margin-top: var(--wall-clearance, 12rem);
		}

		/* Packed mode (grid-masonry.js): rows become a 1px ruler for the
		   measured spans; each span already includes the breathing room, so
		   the row gap stands down. The script pins each card to its lane
		   (item i -> column i mod N) so the order reads STRICTLY across then
		   down - 1 2 3 / 4 5 6 - and `dense` lets each lane backfill right
		   below its own previous card instead of waiting for the slowest
		   lane (the row cursor never moves backwards without it). */
		[role="list"].timeline.is-packed {
			/* 8px ruler - must match ROW_UNIT in grid-masonry.js. */
			grid-auto-rows: 8px;
			grid-auto-flow: row dense;
			row-gap: 0;
		}

		.milestone {
			/* Cells fill their column instead of the list's reading measure. */
			max-width: none;
		}

		/* --- Minimap: the faithful schematic follows the layout it depicts
		   (same contract as its 1024px two-column flip): panel strip on top,
		   then a grid of cells matching the real column count. --- */

		.mini-map {
			grid-template-columns: 1fr;
			max-width: 160px;
		}

		.mini-map .mini-map-panel {
			display: block;
			order: -1;
			aspect-ratio: auto;
			height: 10px;
		}

		/* Cells, not bars - but still diagram-scale (the list mode's bars are
		   4px tall; these stay in that register, just wider than tall so the
		   grid reads as a grid without the schematic towering over the bar). */
		.mini-map .mini-map-bars {
			grid-template-columns: repeat(var(--grid-columns), 1fr);
			gap: 3px;

			li {
				height: 9px;
			}
		}
	}
}
