/*
 * os.css — the desktop shell: the surface, the menu bar, the dock, the
 * windows and the widget rail.
 *
 * Every rule is scoped to `.js` and to a viewport wide enough for a desktop,
 * so this file costs nothing when scripts are off and nothing on a phone,
 * where phone.css draws the shell instead. What goes *inside* a window is not
 * here: app views are shared with the phone and live in apps.css.
 */

@media (min-width: 768px) {

	/* ---------------------------------------------------------------------
	 * Desktop surface
	 * ------------------------------------------------------------------ */

	.js body {
		height: 100vh;
		overflow: hidden;
		overscroll-behavior: none;
	}

	.js .eos-desktop {
		position: fixed;
		inset: 0;
		overflow: hidden;
	}

	/* Painted in base.css; the desktop is one of the two shells that stand on it. */
	.js .eos-wallpaper {
		display: block;
	}

	/* The document layer becomes the content store the windows draw from. */
	.js .eos-doc,
	.js .eos-pageheader,
	.js .eos-pagefooter,
	.js .eos-skip {
		display: none;
	}

	/* ---------------------------------------------------------------------
	 * Menu bar
	 * ------------------------------------------------------------------ */

	.js .eos-menubar {
		position: absolute;
		top: 10px;
		left: 12px;
		right: 12px;
		height: 32px;
		z-index: 50;
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 0 14px;
		border-radius: 16px;
		background: var(--glass);
		-webkit-backdrop-filter: blur(40px) saturate(1.8);
		backdrop-filter: blur(40px) saturate(1.8);
		border: 1px solid var(--edge);
		box-shadow: var(--shadow-bar), var(--inner-glass);
		font-size: 12.5px;
		color: var(--ink);
	}

	.js .eos-menubar__group {
		display: flex;
		align-items: center;
		gap: 16px;
	}

	.js .eos-menubar__group:first-child {
		gap: 10px;
	}

	/*
	 * The name is the way into About. It carries the mark and the title, so it
	 * has to sit exactly where the two used to on their own: the padding that
	 * gives the hover something to fill is taken back out of the margin.
	 */
	.js .eos-menubar__home {
		display: flex;
		align-items: center;
		gap: 8px;
		margin: 0 -8px;
		padding: 4px 8px;
		border-radius: 9px;
		color: inherit;
		text-decoration: none;
		cursor: pointer;
		transition: background var(--speed) var(--ease);
	}

	.js .eos-menubar__home:hover {
		background: var(--accent-hover);
		color: inherit;
	}

	.js .eos-menubar__logo {
		width: 14px;
		height: 14px;
		border-radius: 4px;
		background: var(--accent);
	}

	.js .eos-menubar__app {
		font-weight: 700;
		max-width: 26ch;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	/* ---------------------------------------------------------------------
	 * Menu bar hover panels
	 *
	 * The same affordance the dock labels use: a glass panel that fades in
	 * under the thing it explains, on hover and on keyboard focus. Shared by
	 * every menu bar item that has something to say — the lock and the network
	 * meter — so the two can never explain themselves differently.
	 * ------------------------------------------------------------------ */

	.js .eos-status--panel {
		position: relative;
		height: 32px;
		cursor: default;
	}

	.js .eos-status__tip {
		position: absolute;
		top: calc(100% + 6px);
		right: -8px;
		z-index: 10;
		display: flex;
		flex-direction: column;
		gap: 6px;
		width: 250px;
		padding: 12px 14px;
		border-radius: 14px;
		background: var(--glass5);
		-webkit-backdrop-filter: blur(40px) saturate(1.8);
		backdrop-filter: blur(40px) saturate(1.8);
		border: 1px solid var(--edge);
		box-shadow: var(--shadow-bar), var(--inner-glass);
		text-align: left;
		opacity: 0;
		pointer-events: none;
		transition: opacity 150ms var(--ease);
	}

	.js .eos-status--panel:hover .eos-status__tip,
	.js .eos-status--panel:focus-visible .eos-status__tip {
		opacity: 1;
	}

	.js .eos-status__tiphead {
		display: flex;
		align-items: center;
		gap: 8px;
		font-size: 12.5px;
		font-weight: 600;
		color: var(--ink);
	}

	.js .eos-status__tipdot {
		flex: none;
		width: 8px;
		height: 8px;
		border-radius: 50%;
		background: var(--accent);
	}

	.js .eos-status__tipbody {
		font-size: 12px;
		line-height: 1.45;
		color: var(--ink2);
	}

	.js .eos-status__tipmeta {
		font: 500 10.5px var(--mono);
		color: var(--muted);
	}

	/* ---------------------------------------------------------------------
	 * Desktop icons
	 *
	 * A column under the menu bar, as the design draws it. One folder per
	 * content collection, server-rendered — so the desktop is populated before
	 * a script runs, and stays populated without one.
	 * ------------------------------------------------------------------ */

	.js .eos-icons {
		display: block;
		position: absolute;
		top: 66px;
		left: 26px;
		z-index: 20;
		/* Never over the widget rail, and never under the dock. */
		max-height: calc(100vh - 180px);
		/*
		 * No width of its own: absolutely positioned, it takes the width of
		 * its longest folder name, and every icon stretches to match. So a
		 * name is never broken across two lines to fit a number picked here,
		 * and the column stays as narrow as the names allow. The cap is for a
		 * name long enough to reach across the desktop.
		 */
		max-width: 168px;
	}

	.js .eos-icons__list {
		list-style: none;
		margin: 0;
		padding: 0;
		display: flex;
		flex-direction: column;
		gap: 14px;
	}

	/*
	 * A folder is a link and Help is a button — one has somewhere to go
	 * without scripts and one does not — so this dresses both back down to
	 * the same icon.
	 */
	.js .eos-icon {
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 6px;
		width: 100%;
		min-width: 84px;
		/* Dragged, not selected as text, and not scrolled on a touchpad. */
		user-select: none;
		-webkit-user-select: none;
		touch-action: none;
		padding: 6px 4px 5px;
		border: 0;
		border-radius: 10px;
		background: none;
		font-family: var(--font);
		font-size: 12px;
		font-weight: 500;
		line-height: 1.25;
		text-align: center;
		color: var(--ink);
		text-decoration: none;
		cursor: default;
		transition: background var(--speed) var(--ease);
	}

	/*
	 * Lifted while it is being moved, and moving with the pointer rather than
	 * easing after it — a transition here would make the icon lag the cursor.
	 */
	.js .eos-icons__item[data-dragging="true"] {
		z-index: 2;
		cursor: grabbing;
	}

	.js .eos-icons__item[data-dragging="true"] .eos-icon {
		background: var(--tile);
		box-shadow: 0 10px 24px rgba(20, 40, 50, .22);
		transform: scale(1.04);
	}

	.js .eos-icon:hover {
		background: var(--tile2);
		color: var(--ink);
	}

	/*
	 * Selected, not open. The scrim stays translucent and the name takes the
	 * accent, rather than the tile taking it: the folder art is accent too,
	 * and would vanish into a block of it.
	 */
	.js .eos-icon[data-selected="true"] {
		background: var(--tile);
	}

	.js .eos-icon[data-selected="true"] .eos-icon__label {
		background: var(--accent);
		color: #fff;
	}

	.js .eos-icon__art {
		position: relative;
		display: grid;
		place-items: center;
		width: 48px;
		height: 40px;
	}

	.js .eos-icon__folder {
		width: 48px;
		height: 40px;
		fill: var(--accent);
		filter: drop-shadow(0 2px 5px rgba(20, 40, 50, .22));
	}

	/* The app's own dock glyph, small, on the folder's face. */
	.js .eos-icon__glyph {
		position: absolute;
		top: 15px;
		width: 17px;
		height: 17px;
		color: #fff;
		opacity: .92;
		/* The two-tone glyphs punch their counter through in the tile colour. */
		--tile-bg: var(--accent);
	}

	/* Padded whether or not it is selected, so selecting shifts nothing. */
	.js .eos-icon__page {
		display: flex;
		flex-direction: column;
		justify-content: flex-end;
		width: 31px;
		height: 40px;
		border-radius: 5px;
		overflow: hidden;
		background: var(--surface);
		box-shadow: 0 2px 5px rgba(20, 40, 50, .2), inset 0 0 0 1px var(--line3);
	}

	.js .eos-icon__pagetab {
		display: grid;
		place-items: center;
		height: 12px;
		background: var(--accent);
		font: 700 8px var(--mono);
		color: #fff;
	}

	.js .eos-icon__label {
		max-width: 100%;
		padding: 1px 6px;
		border-radius: 5px;
		/* One line. Past the column's cap it truncates rather than wraps. */
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
	}

	.js .eos-icon__count {
		font: 500 10px var(--mono);
		letter-spacing: .02em;
		color: var(--muted);
	}

	/* ---------------------------------------------------------------------
	 * Dock
	 * ------------------------------------------------------------------ */

	.js .eos-dock {
		position: absolute;
		bottom: 14px;
		left: 50%;
		transform: translateX(-50%);
		z-index: 40;
		max-width: none;
		margin: 0;
		padding: 8px 9px 4px;
		border-radius: 24px;
		background: var(--glass);
		-webkit-backdrop-filter: blur(40px) saturate(1.8);
		backdrop-filter: blur(40px) saturate(1.8);
		border: 1px solid var(--edge);
		box-shadow: var(--shadow-dock), var(--inner-glass);
	}

	.js .eos-dock__list {
		gap: 10px;
		flex-wrap: nowrap;
	}

	.js .eos-dock__item {
		position: relative;
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 3px;
	}

	.js .eos-dock__link {
		position: relative;
		display: block;
		border-radius: var(--radius-tile);
	}

	/*
	 * Size and lift only. Background and colour stay with the per-app variants
	 * in base.css — setting them here would tie on specificity and, loading
	 * later, would flatten every tile to the default treatment.
	 */
	.js .eos-dock__tile {
		width: 52px;
		height: 52px;
		border-radius: var(--radius-tile);
		box-shadow: 0 2px 6px rgba(20, 40, 50, .14), inset 0 0 0 1px var(--line1);
		transition: transform var(--speed) var(--ease);
	}

	.js .eos-dock__glyph {
		width: 26px;
		height: 26px;
	}

	.js .eos-dock__link:hover .eos-dock__tile,
	.js .eos-dock__link:focus-visible .eos-dock__tile {
		transform: translateY(-6px) scale(1.06);
	}

	/* The label doubles as the dock tooltip. */
	.js .eos-dock__label {
		position: absolute;
		bottom: calc(100% + 12px);
		left: 50%;
		transform: translateX(-50%);
		padding: 4px 10px;
		border-radius: 9px;
		white-space: nowrap;
		font-size: 12px;
		color: var(--ink);
		background: var(--glass6);
		-webkit-backdrop-filter: blur(20px) saturate(1.8);
		backdrop-filter: blur(20px) saturate(1.8);
		border: 1px solid var(--edge);
		box-shadow: var(--shadow-bar);
		opacity: 0;
		pointer-events: none;
		transition: opacity 150ms var(--ease);
	}

	.js .eos-dock__link:hover .eos-dock__label,
	.js .eos-dock__link:focus-visible .eos-dock__label {
		opacity: 1;
	}

	.js .eos-dock__dot {
		display: block;
		width: 4px;
		height: 4px;
		border-radius: 50%;
		background: var(--tile-ink);
		opacity: 0;
		transition: opacity var(--speed) var(--ease);
	}

	.js .eos-dock__item[data-open="true"] .eos-dock__dot {
		opacity: .8;
	}

	/* ---------------------------------------------------------------------
	 * Windows
	 * ------------------------------------------------------------------ */

	.js .eos-windows {
		display: block;
		position: absolute;
		inset: 0;
		z-index: 30;
		pointer-events: none;
	}

	.js .eos-win {
		position: absolute;
		display: flex;
		flex-direction: column;
		pointer-events: auto;
		border-radius: var(--radius-win);
		overflow: hidden;
		background: var(--surface);
		border: 1px solid var(--edge);
		box-shadow: var(--shadow-win-idle);
		transform-origin: center;
		transition:
			opacity var(--speed) var(--ease),
			transform var(--speed) var(--ease),
			box-shadow var(--speed) var(--ease);
	}

	.js .eos-win[data-active="true"] {
		box-shadow: var(--shadow-win);
	}

	/*
	 * Closing is a fade in place: the window is going nowhere, it is going
	 * away. Opening travels, when there is somewhere to travel from.
	 */
	.js .eos-win[data-phase="opening"],
	.js .eos-win[data-phase="closing"] {
		opacity: 0;
		transform: scale(.96);
	}

	/* Out of its own dock icon — the exact move minimise makes, reversed. */
	.js .eos-win[data-phase="opening"][data-origin="dock"] {
		transform: translate(var(--min-dx, 0), var(--min-dy, 0)) scale(.14);
	}

	.js .eos-win[data-phase="open"] {
		opacity: 1;
		transform: none;
	}

	/* Minimise collapses toward the app's own dock icon. */
	.js .eos-win[data-phase="min"] {
		opacity: 0;
		transform: translate(var(--min-dx, 0), var(--min-dy, 40vh)) scale(.14);
	}

	.js .eos-win[hidden] {
		display: none;
	}

	/* No transition mid-gesture, or dragging lags a frame behind the pointer. */
	.js .eos-win[data-gesture="true"] {
		transition: none;
	}

	.js .eos-win__bar {
		position: relative;
		z-index: 1;
		flex: none;
		height: 48px;
		display: flex;
		align-items: center;
		gap: 8px;
		padding: 0 14px;
		background: var(--glass2);
		-webkit-backdrop-filter: blur(40px) saturate(1.8);
		backdrop-filter: blur(40px) saturate(1.8);
		border-bottom: 1px solid var(--line3);
		cursor: grab;
		user-select: none;
	}

	.js .eos-win[data-gesture="true"] .eos-win__bar {
		cursor: grabbing;
	}

	.js .eos-win__lights {
		display: flex;
		gap: 8px;
	}

	.js .eos-light {
		position: relative;
		width: 12px;
		height: 12px;
		padding: 0;
		border: 0;
		border-radius: 50%;
		background: var(--light-off);
		cursor: pointer;
		transition: background var(--speed) var(--ease), filter var(--speed) var(--ease);
	}

	.js .eos-win[data-active="true"] .eos-light--close,
	.js .eos-win__lights:hover .eos-light--close,
	.js .eos-win__lights:focus-within .eos-light--close {
		background: var(--tl-close);
	}

	.js .eos-win[data-active="true"] .eos-light--min,
	.js .eos-win__lights:hover .eos-light--min,
	.js .eos-win__lights:focus-within .eos-light--min {
		background: var(--tl-min);
	}

	.js .eos-win[data-active="true"] .eos-light--max,
	.js .eos-win__lights:hover .eos-light--max,
	.js .eos-win__lights:focus-within .eos-light--max {
		background: var(--tl-max);
	}

	.js .eos-light:hover {
		filter: brightness(.9);
	}

	/* ---------------------------------------------------------------------
	 * Light glyphs
	 *
	 * A cross, a minus and a plus, drawn from bars rather than set in a font:
	 * at 8px across a glyph has to be pixel-exact, and a bar can be. They are
	 * invisible until the pointer is over the set, so the lights read as three
	 * dots at rest and as controls the moment they are reachable.
	 * ------------------------------------------------------------------ */

	.js .eos-light::before,
	.js .eos-light::after {
		content: "";
		position: absolute;
		background: rgba(0, 0, 0, .6);
		border-radius: 1px;
		opacity: 0;
		transition: opacity 120ms var(--ease);
	}

	/* Horizontal bar: the minus, the cross's first stroke, the plus's arm. */
	.js .eos-light::before {
		left: 2px;
		top: 5.25px;
		width: 8px;
		height: 1.5px;
	}

	.js .eos-light--close::before {
		transform: rotate(45deg);
	}

	.js .eos-light--close::after {
		left: 2px;
		top: 5.25px;
		width: 8px;
		height: 1.5px;
		transform: rotate(-45deg);
	}

	/* Nothing crosses the minus. */
	.js .eos-light--min::after {
		content: none;
	}

	.js .eos-light--max::after {
		left: 5.25px;
		top: 2px;
		width: 1.5px;
		height: 8px;
	}

	.js .eos-win__lights:hover .eos-light::before,
	.js .eos-win__lights:hover .eos-light::after,
	.js .eos-win__lights:focus-within .eos-light::before,
	.js .eos-win__lights:focus-within .eos-light::after {
		opacity: 1;
	}

	/* Each light explains itself on hover, in the dock label's idiom. */
	.js .eos-light__tip {
		position: absolute;
		top: calc(100% + 12px);
		left: -6px;
		z-index: 10;
		padding: 5px 9px;
		border-radius: 8px;
		white-space: nowrap;
		font-family: var(--font);
		font-size: 11.5px;
		font-weight: 500;
		line-height: 1.2;
		color: var(--ink);
		background: var(--glass6);
		-webkit-backdrop-filter: blur(30px) saturate(1.8);
		backdrop-filter: blur(30px) saturate(1.8);
		border: 1px solid var(--edge);
		box-shadow: var(--shadow-bar);
		opacity: 0;
		pointer-events: none;
		transition: opacity 120ms var(--ease);
	}

	/* The green light holds both labels; the window's state picks one. */
	.js .eos-light__tip--restore,
	.js .eos-win[data-maximised="true"] .eos-light__tip--fill {
		display: none;
	}

	.js .eos-win[data-maximised="true"] .eos-light__tip--restore {
		display: block;
	}

	.js .eos-light:hover .eos-light__tip,
	.js .eos-light:focus-visible .eos-light__tip {
		opacity: 1;
	}

	.js .eos-win__title {
		margin: 0 0 0 6px;
		font-size: 13px;
		font-weight: 600;
		letter-spacing: 0;
		color: var(--ink);
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.js .eos-win[data-active="false"] .eos-win__title {
		color: var(--muted);
	}

	/* Content areas stay solid and highly legible — the glass is chrome only. */
	.js .eos-win__body {
		flex: 1;
		min-height: 0;
		overflow: auto;
		padding: 22px 26px;
		background: var(--surface);
		overscroll-behavior: contain;
	}

	/* The relocated document section sheds its document-layer framing. */
	.js .eos-win .eos-app {
		padding: 0;
		border-top: 0;
	}

	.js .eos-win .eos-app__title {
		position: absolute;
		width: 1px;
		height: 1px;
		padding: 0;
		margin: -1px;
		overflow: hidden;
		clip-path: inset(50%);
		white-space: nowrap;
	}

	.js .eos-win__resize {
		position: absolute;
		right: 0;
		bottom: 0;
		width: 18px;
		height: 18px;
		padding: 0;
		border: 0;
		background: transparent;
		cursor: nwse-resize;
		touch-action: none;
	}

	.js .eos-win__resize::after {
		content: "";
		position: absolute;
		right: 4px;
		bottom: 4px;
		width: 7px;
		height: 7px;
		border-right: 2px solid var(--line6);
		border-bottom: 2px solid var(--line6);
		border-bottom-right-radius: 3px;
	}

	.js .eos-win[data-maximised="true"] .eos-win__resize {
		display: none;
	}

	/* ---------------------------------------------------------------------
	 * Widget rail
	 *
	 * Same glass as the dock and the menu bar, sitting below the window layer
	 * so windows pass over it the way they would on a real desktop. The
	 * cascade in os.js already keeps new windows clear of it on wide screens.
	 * ------------------------------------------------------------------ */

	.js .eos-widgets {
		position: absolute;
		top: 66px;
		right: 28px;
		z-index: 20;
		width: 330px;
		max-width: none;
		/* Stops above the dock rather than running behind it. */
		max-height: calc(100vh - 170px);
		/*
		 * The inline padding, cancelled by the negative margin, gives the row
		 * highlights their 6px of overhang without the scroll container
		 * clipping them.
		 */
		margin: 0 -6px 0 0;
		padding: 0 6px 20px;
		border-top: 0;
		display: flex;
		flex-direction: column;
		gap: 12px;
		overflow-x: hidden;
		overflow-y: auto;
		overscroll-behavior: contain;
		scrollbar-width: none;
	}

	.js .eos-widgets::-webkit-scrollbar {
		display: none;
	}

	.js .eos-widget {
		flex: none;
		padding: 14px 18px;
		border-radius: var(--radius-panel);
		background: var(--glass);
		-webkit-backdrop-filter: blur(40px) saturate(1.8);
		backdrop-filter: blur(40px) saturate(1.8);
		border: 1px solid var(--edge);
		box-shadow: var(--shadow-panel), var(--inner-glass);
	}

	/* Tiles read as frosted panes on the glass, not as cards on a page. */
	.js .eos-stat {
		background: var(--tile);
		border-color: var(--edge2);
	}

	/*
	 * Rows reach past the panel's padding when hovered, so the highlight
	 * frames the whole row rather than the text inside it.
	 */
	.js .eos-widget__post,
	.js .eos-widget__project {
		padding-inline: 6px;
		margin-inline: -6px;
		border-radius: 8px;
		transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
	}

	.js .eos-widget__post:hover,
	.js .eos-widget__post:focus-visible,
	.js .eos-widget__project:hover,
	.js .eos-widget__project:focus-visible {
		background: var(--glass2);
	}

	.js .eos-widget__post:hover .eos-widget__posttitle,
	.js .eos-widget__post:focus-visible .eos-widget__posttitle,
	.js .eos-widget__project:hover .eos-widget__projecttitle,
	.js .eos-widget__project:focus-visible .eos-widget__projecttitle {
		color: var(--accent-text);
	}

	/*
	 * Reflow: the rail narrows and tightens before it stands aside altogether
	 * — below that the desktop is too cramped to run windows and a rail side
	 * by side.
	 */
	@media (max-width: 1240px) {
		.js .eos-widgets {
			width: 268px;
			right: 18px;
			gap: 10px;
		}

		.js .eos-widget {
			padding: 12px 15px;
		}
	}

	@media (max-width: 1080px) {
		.js .eos-widgets {
			width: 232px;
		}

		.js .eos-stats {
			gap: 6px;
		}

		.js .eos-stat__value {
			font-size: 20px;
		}
	}

	@media (max-width: 940px) {
		.js .eos-widgets {
			display: none;
		}
	}

	/* ---------------------------------------------------------------------
	 * App views
	 *
	 * A hydrated window hands its padding, scrolling and footer to the view
	 * inside it, so lists and grids run edge to edge the way the design draws
	 * them. The window body also becomes the container these layouts respond
	 * to: a window is resized independently of the viewport, so a media query
	 * would tell them nothing useful.
	 * ------------------------------------------------------------------ */

	.js .eos-win[data-hydrated="true"] .eos-win__body,
	.js .eos-win[data-bleed="true"] .eos-win__body {
		display: flex;
		padding: 0;
		overflow: hidden;
		container: eos-app / inline-size;
	}

	.js .eos-win[data-hydrated="true"] .eos-app,
	.js .eos-win[data-hydrated="true"] .eos-app__body {
		display: flex;
		flex-direction: column;
		flex: 1;
		min-width: 0;
		min-height: 0;
	}

	/* Visible only when a fetch failed, or below the desktop breakpoint. */
	.js .eos-win[data-hydrated="true"] .eos-static {
		flex: 1;
		min-height: 0;
		overflow: auto;
		padding: 18px 26px 26px;
	}

	/* ---------------------------------------------------------------------
	 * Detail windows
	 * ------------------------------------------------------------------ */

	/*
	 * An inert light is a dot and nothing more: no colour on hover, no glyph
	 * and no label. Keyed on the button's own disabled state rather than on
	 * the window's kind, so the one detail window that keeps a live light —
	 * the Finder's, which can fill the screen — keeps its full treatment.
	 */
	.js .eos-win .eos-light:disabled,
	.js .eos-win__lights:hover .eos-light:disabled,
	.js .eos-win__lights:focus-within .eos-light:disabled {
		background: var(--light-off);
		cursor: default;
	}

	.js .eos-light:disabled .eos-light__tip,
	.js .eos-light:disabled::before,
	.js .eos-light:disabled::after {
		display: none;
	}

	/* ---------------------------------------------------------------------
	 * Fallbacks
	 * ------------------------------------------------------------------ */

	@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
		.js .eos-menubar,
		.js .eos-dock,
		.js .eos-dock__label,
		.js .eos-light__tip,
		.js .eos-status__tip,
		.js .eos-win__bar {
			background: var(--glass6);
		}
	}
}

/* -------------------------------------------------------------------------
 * Boot screen and the welcome notification
 *
 * Outside the desktop query on purpose: these two belong to the site, not to
 * the window manager, so they play on a phone as well — where the document
 * layer is the experience and the dock and windows are not.
 *
 * Both are `display: none` until the `.js` class lands, which the head script
 * sets before first paint. Without scripts neither has ever existed.
 * ---------------------------------------------------------------------- */

.eos-boot,
.eos-notif {
	display: none;
}

.js .eos-boot {
	display: flex;
	position: fixed;
	inset: 0;
	z-index: 1200;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 22px;
	background: var(--bg);
	cursor: pointer;
	transition: opacity 300ms var(--ease);

	/*
	 * The failsafe. The `.js` class is set by an inline head script, so this
	 * overlay appears whether or not the deferred bundle ever arrives — and
	 * if it doesn't, nothing else would ever take it down, leaving the
	 * readable document sealed behind a splash screen. This clears it on its
	 * own, well after boot.js would have.
	 *
	 * boot.js cancels this the moment it takes over, so the two never fight
	 * over the same property.
	 */
	animation: eos-boot-failsafe 2.5s forwards;
}

@keyframes eos-boot-failsafe {
	0%, 88% {
		opacity: 1;
		visibility: visible;
	}

	100% {
		opacity: 0;
		visibility: hidden;
	}
}

/*
 * Already booted this visit. Stamped by the head script from sessionStorage
 * before the first frame, so the second page of a visit never flashes the
 * overlay — which is the whole reason that check cannot live in this file's
 * deferred script.
 */
.js[data-booted] .eos-boot {
	display: none;
}

.js .eos-boot[data-phase="out"] {
	opacity: 0;
	pointer-events: none;
}

.js .eos-boot__logo {
	display: grid;
	place-items: center;
	width: 56px;
	height: 56px;
	border-radius: 16px;
	background: var(--accent);
	color: #fff;
	font: 700 20px var(--mono);
	letter-spacing: -1px;
}

.js .eos-boot__track {
	width: 180px;
	height: 4px;
	border-radius: 2px;
	background: var(--line4);
	overflow: hidden;
}

.js .eos-boot__bar {
	display: block;
	width: 0;
	height: 100%;
	background: var(--accent);
	animation: eos-boot 1.1s cubic-bezier(.3, .7, .3, 1) forwards;
}

@keyframes eos-boot {
	from { width: 0; }
	to { width: 100%; }
}

.js .eos-boot__tagline {
	font-size: 11px;
	font-weight: 500;
	letter-spacing: .03em;
	color: var(--muted);
}

/* Same glass as the menu bar it slides out from under. */
.js .eos-notif {
	display: flex;
	position: fixed;
	top: 54px;
	left: 50%;
	z-index: 900;
	width: 420px;
	max-width: calc(100% - 32px);
	align-items: center;
	gap: 12px;
	padding: 12px 14px;
	border: 1px solid var(--edge);
	border-radius: 16px;
	background: var(--glass3);
	-webkit-backdrop-filter: blur(40px) saturate(1.8);
	backdrop-filter: blur(40px) saturate(1.8);
	box-shadow: var(--shadow-panel), inset 0 1px 1px var(--hl);
	transform: translate(-50%, -24px);
	opacity: 0;
	transition: transform 250ms var(--ease), opacity 250ms var(--ease);
}

/* Beats the `display: flex` above, which would otherwise outrank [hidden]. */
.js .eos-notif[hidden] {
	display: none;
}

.js .eos-notif[data-phase="in"] {
	transform: translate(-50%, 0);
	opacity: 1;
}

.js .eos-notif[data-phase="out"] {
	transform: translate(-50%, -24px);
	opacity: 0;
}

.js .eos-notif__icon {
	display: grid;
	place-items: center;
	flex: none;
	width: 34px;
	height: 34px;
	border-radius: 10px;
	background: var(--accent);
	color: #fff;
}

.js .eos-notif__icon svg {
	width: 18px;
	height: 18px;
}

.js .eos-notif__text {
	flex: 1;
	min-width: 0;
}

.js .eos-notif__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 8px;
}

.js .eos-notif__from {
	font-size: 12.5px;
	font-weight: 600;
	color: var(--ink);
}

.js .eos-notif__when {
	font-size: 10.5px;
	font-weight: 500;
	color: var(--muted);
}

.js .eos-notif__body {
	display: block;
	font-size: 12.5px;
	line-height: 1.4;
	color: var(--ink2);
}

.js .eos-notif__close {
	flex: none;
	width: 22px;
	height: 22px;
	padding: 0;
	border: 0;
	border-radius: 7px;
	background: none;
	color: var(--muted);
	font-size: 15px;
	line-height: 1;
	cursor: pointer;
}

.js .eos-notif__close:hover {
	background: var(--accent-hover);
	color: var(--ink);
}

/* -------------------------------------------------------------------------
 * Motion
 * ---------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	/*
	 * The boot screen still appears — it is a moment, not an animation — but
	 * it fills and clears at once. boot.js shortens its timers to match.
	 */
	.js .eos-boot__bar {
		animation-duration: 1ms;
	}

	.js .eos-boot,
	.js .eos-notif {
		transition-duration: 1ms !important;
	}

	.js .eos-sk {
		animation: none;
		background: var(--surface4);
	}

	.js .eos-cert,
	.js .eos-award,
	.js .eos-widget__post,
	.js .eos-widget__project {
		transition-duration: 1ms !important;
	}

	.js .eos-win,
	.js .eos-icon,
	.js .eos-menubar__home,
	.js .eos-dock__tile,
	.js .eos-dock__label,
	.js .eos-dock__dot,
	.js .eos-toggle,
	.js .eos-toggle__knob,
	.js .eos-activity__graph rect,
	.js .eos-light,
	.js .eos-light::before,
	.js .eos-light::after,
	.js .eos-light__tip,
	.js .eos-status__tip {
		transition-duration: 1ms !important;
	}

	.js .eos-win[data-phase="min"],
	.js .eos-win[data-phase="opening"][data-origin="dock"] {
		transform: none;
	}

	.js .eos-dock__link:hover .eos-dock__tile,
	.js .eos-dock__link:focus-visible .eos-dock__tile {
		transform: none;
	}
}
