/* ============================================================================
   lucinite.com

   Hand-written, replacing RapidWeaver's "Marvel" theme and its 3.4MB of
   scaffolding. One stylesheet for the whole site.

   THEMING - three states, not two. "Default to system" only means something if
   system is a state you can return to, so an explicit choice is stored and
   anything else means follow the OS:

     :root                                   light tokens (the baseline)
     @media (prefers-color-scheme: dark)     dark, guarded so an explicit
       :root:not([data-theme="light"])       light choice still wins
     :root[data-theme="dark"]                dark chosen outright

   Colours are ONLY ever defined as tokens here. Nothing below the token blocks
   should name a colour directly, or it will be wrong in one of the three
   states - which is exactly the bug this structure exists to prevent. That
   includes gradients and glows: a gradient is a colour, so it gets a token.

   ATMOSPHERE - the page is lit rather than merely coloured. Three layers, all
   painted by pseudo-elements so no page needs extra markup for them:

     html::before    fixed aurora wash, strongest behind the header
     html::after     fixed film grain, which is what stops large flat
                     gradients banding on 8-bit panels
     .hero-atmos     the home page only: light beams and a horizon grid

   All three are pointer-events:none and sit at z-index -1 against the html
   canvas, which is why <body> deliberately has no background of its own - if
   it did, it would paint straight over them.
   ========================================================================= */

:root {
	color-scheme: light;

	--bg:            #ffffff;
	--bg-sunken:     #f4f5f7;
	--bg-raised:     #ffffff;
	--border:        #e2e5ea;
	--border-strong: #cbd1da;

	--text:          #16181d;
	--text-soft:     #565d69;
	--text-faint:    #838b98;

	--accent:        #0a66c2;
	--accent-hover:  #084e94;
	--accent-text:   #ffffff;
	--accent-soft:   #eaf2fb;

	/* A second hue, used only in gradients and glows and never for text - it is
	   lighting, not information. */
	--accent-2:      #7c3aed;

	--glow-a:        rgba(10, 102, 194, .20);
	--glow-b:        rgba(124, 58, 237, .14);
	--glow-c:        rgba(6, 182, 212, .13);
	--grid-line:     rgba(22, 24, 29, .038);
	--spot:          rgba(10, 102, 194, .07);
	--grain:         .04;

	--h1-grad:       linear-gradient(178deg, #14171d 0%, #444e5c 115%);
	--h1-glow:       none;
	--card-grad:     linear-gradient(180deg, #ffffff 0%, #fafbfd 100%);
	--card-edge:     rgba(255, 255, 255, .9);
	--sunken-grad:   linear-gradient(180deg, #f7f8fa 0%, #eef0f4 100%);
	--glass:         rgba(255, 255, 255, .72);
	--btn-grad:      linear-gradient(180deg, #1a7ad9 0%, #0a66c2 100%);
	--btn-grad-hi:   linear-gradient(180deg, #2b8ae9 0%, #0d70d2 100%);
	--btn-glow:      0 6px 20px rgba(10, 102, 194, .30);
	--sheen:         rgba(255, 255, 255, .55);
	--shot-glow:     0 40px 90px -30px rgba(10, 102, 194, .35);

	--shadow:        0 1px 2px rgba(16, 18, 24, .06), 0 8px 24px rgba(16, 18, 24, .08);
	--shadow-lift:   0 2px 4px rgba(16, 18, 24, .08), 0 16px 40px rgba(16, 18, 24, .12);

	--radius:        10px;
	--radius-lg:     16px;
	--maxw:          1120px;
	--gutter:        24px;
}

@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) {
		color-scheme: dark;

		--bg:            #0b0e13;
		--bg-sunken:     #080a0e;
		--bg-raised:     #151a22;
		--border:        #232935;
		--border-strong: #39414e;

		--text:          #e9edf3;
		--text-soft:     #a6afbd;
		--text-faint:    #737d8c;

		--accent:        #4c9aff;
		--accent-hover:  #7ab4ff;
		--accent-text:   #06101f;
		--accent-soft:   #16233a;

		--accent-2:      #9d7bff;

		--glow-a:        rgba(76, 154, 255, .30);
		--glow-b:        rgba(157, 123, 255, .22);
		--glow-c:        rgba(56, 224, 208, .13);
		--grid-line:     rgba(190, 215, 255, .06);
		--spot:          rgba(120, 180, 255, .10);
		--grain:         .05;

		--h1-grad:       linear-gradient(178deg, #ffffff 0%, #b4c3d8 115%);
		--h1-glow:       0 0 60px rgba(76, 154, 255, .28);
		--card-grad:     linear-gradient(180deg, #1a202a 0%, #11151c 100%);
		--card-edge:     rgba(255, 255, 255, .08);
		--sunken-grad:   linear-gradient(180deg, #080a0e 0%, #0d1117 100%);
		--glass:         rgba(11, 14, 19, .66);
		--btn-grad:      linear-gradient(180deg, #63a8ff 0%, #3b7fe0 100%);
		--btn-grad-hi:   linear-gradient(180deg, #7cb8ff 0%, #4a8dee 100%);
		--btn-glow:      0 6px 26px rgba(76, 154, 255, .40);
		--sheen:         rgba(255, 255, 255, .22);
		--shot-glow:     0 50px 120px -40px rgba(76, 154, 255, .45);

		--shadow:        0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px rgba(0, 0, 0, .5);
		--shadow-lift:   0 2px 4px rgba(0, 0, 0, .55), 0 24px 60px rgba(0, 0, 0, .6);
	}
}

:root[data-theme="dark"] {
	color-scheme: dark;

	--bg:            #0b0e13;
	--bg-sunken:     #080a0e;
	--bg-raised:     #151a22;
	--border:        #232935;
	--border-strong: #39414e;

	--text:          #e9edf3;
	--text-soft:     #a6afbd;
	--text-faint:    #737d8c;

	--accent:        #4c9aff;
	--accent-hover:  #7ab4ff;
	--accent-text:   #06101f;
	--accent-soft:   #16233a;

	--accent-2:      #9d7bff;

	--glow-a:        rgba(76, 154, 255, .30);
	--glow-b:        rgba(157, 123, 255, .22);
	--glow-c:        rgba(56, 224, 208, .13);
	--grid-line:     rgba(190, 215, 255, .06);
	--spot:          rgba(120, 180, 255, .10);
	--grain:         .05;

	--h1-grad:       linear-gradient(178deg, #ffffff 0%, #b4c3d8 115%);
	--h1-glow:       0 0 60px rgba(76, 154, 255, .28);
	--card-grad:     linear-gradient(180deg, #1a202a 0%, #11151c 100%);
	--card-edge:     rgba(255, 255, 255, .08);
	--sunken-grad:   linear-gradient(180deg, #080a0e 0%, #0d1117 100%);
	--glass:         rgba(11, 14, 19, .66);
	--btn-grad:      linear-gradient(180deg, #63a8ff 0%, #3b7fe0 100%);
	--btn-grad-hi:   linear-gradient(180deg, #7cb8ff 0%, #4a8dee 100%);
	--btn-glow:      0 6px 26px rgba(76, 154, 255, .40);
	--sheen:         rgba(255, 255, 255, .22);
	--shot-glow:     0 50px 120px -40px rgba(76, 154, 255, .45);

	--shadow:        0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px rgba(0, 0, 0, .5);
	--shadow-lift:   0 2px 4px rgba(0, 0, 0, .55), 0 24px 60px rgba(0, 0, 0, .6);
}

/* --------------------------------------------------------------- base ---- */

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

html {
	-webkit-text-size-adjust: 100%;
	scroll-behavior: smooth;
	background: var(--bg);
}

/* The atmosphere. Fixed, so it stays put while the page scrolls past it -
   parallax for free and no scroll handler. Both layers sit behind everything
   because <body> is transparent; give body a background and they vanish.

   Deliberately STATIC. An earlier pass drifted the aurora on a 32s transform
   loop, which meant re-rasterising three viewport-sized radial gradients under
   a blend mode - and re-running the header's backdrop-filter over the result -
   on every frame, for the whole visit. It locked the renderer solid on this
   machine. A gradient this large is a paint budget, not a decoration: it gets
   painted once and then left alone. */
html::before,
html::after {
	content: "";
	position: fixed;
	inset: 0;
	z-index: -1;
	pointer-events: none;
}
html::before {
	background:
		radial-gradient(60vw 46vh at 18% -8%,  var(--glow-a), transparent 70%),
		radial-gradient(52vw 42vh at 84% -4%,  var(--glow-b), transparent 72%),
		radial-gradient(70vw 50vh at 50% 108%, var(--glow-c), transparent 75%);
}
/* Film grain. Large flat gradients band badly on 8-bit panels; a few percent
   of noise is what removes the stripes without itself being visible as noise.
   Plain opacity, not a blend mode - blending a viewport-sized layer is the
   expensive half of the effect and buys nothing over the dark ground here. */
html::after {
	opacity: var(--grain);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
	*, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
	margin: 0;
	background: transparent;
	color: var(--text);
	font-family: "Segoe UI Variable Display", "Segoe UI", system-ui, -apple-system,
	             "Helvetica Neue", Arial, sans-serif;
	font-size: 17px;
	line-height: 1.65;
	-webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { line-height: 1.15; margin: 0 0 .5em; font-weight: 650; letter-spacing: -.015em; }
h1 { font-size: clamp(2.3rem, 5.6vw, 3.9rem); letter-spacing: -.035em; font-weight: 700; }
h2 { font-size: clamp(1.6rem, 3.2vw, 2.3rem); letter-spacing: -.028em; }
h3 { font-size: 1.2rem; }
p  { margin: 0 0 1.1em; }

/* Headlines are lit from above: near-white at the top falling away to a cool
   grey, with a bloom behind the glyphs in dark. background-clip needs a
   transparent fill, so the `color` declared alongside it is the fallback for
   anything that cannot clip a background to text. */
h1, .section-head h2 {
	color: var(--text);
	background: var(--h1-grad);
	-webkit-background-clip: text;
	        background-clip: text;
	-webkit-text-fill-color: transparent;
	padding-bottom: .06em;
}
h1 { text-shadow: var(--h1-glow); }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; }

img { max-width: 100%; height: auto; display: block; }
code, kbd { font-family: Consolas, "Cascadia Mono", Menlo, monospace; font-size: .9em; }
kbd {
	background: var(--bg-sunken); border: 1px solid var(--border);
	border-radius: 5px; padding: .1em .4em;
}

.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 var(--gutter); }
.muted { color: var(--text-soft); }
.center { text-align: center; }

/* Skip link - keyboard users should not have to tab the whole nav on every page. */
.skip {
	position: absolute; left: -9999px;
	background: var(--accent); color: var(--accent-text);
	padding: .6em 1em; border-radius: var(--radius); z-index: 100;
}
.skip:focus { left: var(--gutter); top: var(--gutter); }

/* Visible focus everywhere, since a fair amount of the page is now low-contrast
   glass that a browser's default outline disappears into. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

/* ------------------------------------------------------------- header ---- */

.site-header {
	position: sticky; top: 0; z-index: 50;
	background: var(--glass);
	backdrop-filter: saturate(180%) blur(14px);
	-webkit-backdrop-filter: saturate(180%) blur(14px);
	border-bottom: 1px solid transparent;
	transition: background .3s, border-color .3s, box-shadow .3s;
}
/* Flat until the page leaves the top, then the bar lifts off it. The class is
   set by site.js; without JS the header simply stays in its resting state,
   which is a perfectly good header. */
.site-header.is-stuck {
	border-bottom-color: var(--border);
	box-shadow: inset 0 1px 0 var(--card-edge), 0 10px 30px -20px rgba(0, 0, 0, .6);
}
.site-header .wrap { display: flex; align-items: center; gap: 20px; height: 62px; }

.brand {
	display: flex; align-items: center; gap: 10px;
	font-weight: 700; font-size: 1.05rem; letter-spacing: -.02em;
	color: var(--text); text-decoration: none; white-space: nowrap;
}
.brand:hover { text-decoration: none; }
.brand img {
	width: 26px; height: 26px;
	filter: drop-shadow(0 0 10px var(--glow-a));
	transition: transform .35s cubic-bezier(.2, .8, .2, 1);
}
.brand:hover img { transform: scale(1.08) rotate(-4deg); }

.site-nav { display: flex; gap: 4px; margin-left: auto; align-items: center; }
.site-nav a {
	position: relative;
	color: var(--text-soft); padding: .45em .7em;
	border-radius: 8px; font-size: .95rem; font-weight: 500;
	transition: color .2s, background .2s;
}
.site-nav a:hover { color: var(--text); background: var(--bg-sunken); text-decoration: none; }
/* The current page gets a lit rule under it as well as heavier text - readable
   at a glance, without having to compare font weights across the row. */
.site-nav a[aria-current="page"] { color: var(--text); font-weight: 650; }
/* THE PLATFORM SWITCH (2026-10-22): Windows or Mac, a pill in the nav, lit on the platform the page
   is for; each link goes to the page's counterpart, and site.js keeps the choice. */
.site-nav .platforms { display: inline-flex; gap: 2px; padding: 3px; margin: 0 6px;
                       border: 1px solid var(--border); border-radius: 999px; }
.site-nav .platforms a { padding: .25em .75em; font-size: .85rem; border-radius: 999px; }
.site-nav .platforms a[aria-current="page"] { background: var(--bg-sunken); }
.site-nav .platforms a[aria-current="page"]::after { display: none; }
.site-nav a[aria-current="page"]::after {
	content: ""; position: absolute; left: .7em; right: .7em; bottom: .1em; height: 2px;
	border-radius: 2px;
	background: linear-gradient(90deg, var(--accent), var(--accent-2));
	box-shadow: 0 0 10px var(--glow-a);
}

/* The toggle shows the icon for the state it is IN, not the one it would move
   to - a control that displays its own state is readable standing still; one
   that displays its next action is only readable to someone who already knows
   what it does. The title attribute names the state for the same reason. */
.theme-toggle {
	display: inline-flex; align-items: center; justify-content: center;
	width: 36px; height: 36px; margin-left: 6px;
	border: 1px solid var(--border); border-radius: 9px;
	background: transparent; color: var(--text-soft);
	cursor: pointer; padding: 0;
	transition: color .2s, background .2s, border-color .2s, box-shadow .2s;
}
.theme-toggle:hover {
	background: var(--bg-sunken); color: var(--text); border-color: var(--border-strong);
	box-shadow: 0 0 0 4px var(--spot);
}
.theme-toggle svg { width: 18px; height: 18px; display: block; }
/* Exactly one of the three is shown, chosen by the root's data-theme. */
.theme-toggle .i-dark, .theme-toggle .i-light { display: none; }
:root[data-theme="light"] .theme-toggle .i-system,
:root[data-theme="light"] .theme-toggle .i-dark { display: none; }
:root[data-theme="light"] .theme-toggle .i-light { display: block; }
:root[data-theme="dark"] .theme-toggle .i-system,
:root[data-theme="dark"] .theme-toggle .i-light { display: none; }
:root[data-theme="dark"] .theme-toggle .i-dark { display: block; }

/* --------------------------------------------------------------- hero ---- */

/* Everything in <main> is raised above .hero-atmos, which is positioned and
   would otherwise paint over the non-positioned sections that follow it. */
main { position: relative; }
main > * { position: relative; z-index: 1; }

/* The side padding has to be restated. This shorthand comes after .wrap in the
   file and at the same specificity, so writing `0` for the sides here is not
   "inherit the gutter", it is "throw the gutter away" - which put the headline
   and the product shot hard against the viewport edge on narrow screens. */
.hero {
	padding: clamp(56px, 9vw, 112px) var(--gutter) 0;
	text-align: center; perspective: 1600px;
}
.hero h1 { max-width: 18ch; margin-inline: auto; }
.hero p.lead {
	font-size: clamp(1.08rem, 2vw, 1.32rem);
	color: var(--text-soft); max-width: 62ch; margin: 0 auto 1.9em;
}
.eyebrow {
	display: inline-flex; align-items: center; gap: .55em; margin-bottom: 1.1em;
	font-size: .78rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
	color: var(--accent);
	background: var(--accent-soft);
	border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
	padding: .38em 1em; border-radius: 999px;
	box-shadow: 0 0 24px -6px var(--glow-a);
}
/* A live dot, because "For Windows 11" is a status as much as it is a label. */
.eyebrow::before {
	content: ""; width: 6px; height: 6px; border-radius: 50%;
	background: currentColor; box-shadow: 0 0 8px currentColor;
	animation: beacon 2.6s ease-in-out infinite;
}
@keyframes beacon { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* The home page's own weather. Positioned inside <main>, sized to sit behind
   the hero and masked so it fades out before the first section starts. */
.hero-atmos {
	position: absolute; inset: 0 0 auto 0; z-index: 0;
	height: clamp(560px, 92vh, 1100px);
	overflow: hidden; pointer-events: none;
	-webkit-mask-image: linear-gradient(180deg, #000 55%, transparent 100%);
	        mask-image: linear-gradient(180deg, #000 55%, transparent 100%);
}
/* Two shafts of light raked in from the top corners. Static, like the wash
   above and for the same reason - a 46px blur across half the viewport is an
   expensive raster to keep alive for the length of a visit. */
.hero-atmos .beam {
	position: absolute; top: -40%; width: 46%; height: 150%;
	filter: blur(46px);
}
.hero-atmos .beam-a {
	left: -6%; transform: rotate(18deg);
	background: linear-gradient(180deg, var(--glow-a), transparent 72%);
}
.hero-atmos .beam-b {
	right: -6%; transform: rotate(-18deg);
	background: linear-gradient(180deg, var(--glow-b), transparent 72%);
}
/* A horizon grid under the product shot. Faint, and masked down to a pool of
   light so it reads as depth rather than as graph paper. */
.hero-atmos .grid-floor {
	position: absolute; inset: 34% -20% -10% -20%;
	background-image:
		linear-gradient(to right,  var(--grid-line) 1px, transparent 1px),
		linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px);
	background-size: 62px 62px;
	-webkit-mask-image: radial-gradient(60% 60% at 50% 42%, #000, transparent 72%);
	        mask-image: radial-gradient(60% 60% at 50% 42%, #000, transparent 72%);
}

.hero-shot {
	position: relative;
	margin: clamp(36px, 5vw, 64px) auto 0; max-width: 1240px;
	border-radius: var(--radius-lg);
	border: 1px solid var(--border-strong);
	box-shadow: var(--shadow-lift), var(--shot-glow);
	overflow: hidden;
	transform-origin: 50% 100%;
}
/* A video in the shot's frame (the Mac home's hero) sized as an image is. */
.hero-shot video { display: block; width: 100%; height: auto; }
/* The inner bevel, so the shot reads as a lit panel rather than a picture with
   a stroke round it. */
.hero-shot::before {
	content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
	border-radius: inherit;
	box-shadow: inset 0 1px 0 var(--card-edge);
}
/* One slow pass of light across the shot as the page settles. Runs once. */
.hero-shot::after {
	content: ""; position: absolute; inset: 0; z-index: 3; pointer-events: none;
	background: linear-gradient(105deg, transparent 38%, var(--sheen) 50%, transparent 62%);
	transform: translateX(-120%);
	animation: sweep 2.4s cubic-bezier(.4, 0, .2, 1) .9s 1 both;
}
@keyframes sweep {
	to { transform: translateX(120%); }
}
/* Entrance: the shot tips up off the horizon into place. Gated on .js so that
   with scripting off it is simply there, flat, rather than stuck mid-tilt. */
.js .hero-shot { animation: rise 1.1s cubic-bezier(.16, .84, .28, 1) .1s both; }
@keyframes rise {
	from { opacity: 0; transform: perspective(1600px) rotateX(13deg) translateY(46px) scale(.955); }
	to   { opacity: 1; transform: perspective(1600px) rotateX(0)     translateY(0)    scale(1);    }
}

/* ------------------------------------------------------------ buttons ---- */

.btn {
	position: relative; overflow: hidden;
	display: inline-flex; align-items: center; gap: .5em;
	padding: .78em 1.6em; border-radius: var(--radius);
	font-weight: 650; font-size: 1rem; border: 1px solid transparent;
	cursor: pointer; text-decoration: none;
	transition: transform .18s cubic-bezier(.2, .8, .2, 1), box-shadow .25s,
	            background .25s, border-color .25s, color .25s;
}
.btn:hover { text-decoration: none; transform: translateY(-2px); }
.btn:active { transform: translateY(0); }
/* A highlight that crosses the button on hover. ::after, so it sits over the
   gradient but under the label, which is a real child node. */
.btn::after {
	content: ""; position: absolute; inset: 0;
	background: linear-gradient(100deg, transparent 35%, var(--sheen) 50%, transparent 65%);
	transform: translateX(-130%);
	transition: transform .7s cubic-bezier(.3, 0, .2, 1);
}
.btn:hover::after { transform: translateX(130%); }

.btn-primary {
	background: var(--btn-grad); color: var(--accent-text);
	box-shadow: var(--btn-glow), inset 0 1px 0 rgba(255, 255, 255, .25);
}
.btn-primary:hover {
	background: var(--btn-grad-hi); color: var(--accent-text);
	box-shadow: 0 12px 34px -8px var(--glow-a), inset 0 1px 0 rgba(255, 255, 255, .3);
}
.btn-secondary {
	background: color-mix(in srgb, var(--bg-raised) 55%, transparent);
	color: var(--text); border-color: var(--border-strong);
	backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.btn-secondary:hover {
	background: var(--bg-sunken); color: var(--text);
	border-color: var(--accent); box-shadow: 0 0 0 4px var(--spot);
}
.btn-row { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }

/* ----------------------------------------------------------- sections ---- */

section { position: relative; padding: clamp(52px, 7.5vw, 96px) 0; }

/* Sunken sections are lit at their seams rather than ruled: a hairline that is
   bright in the middle and fades to nothing at the page edges. */
section.sunken { background: var(--sunken-grad); }
section.sunken::before,
section.sunken::after {
	content: ""; position: absolute; left: 0; right: 0; height: 1px;
	background: linear-gradient(90deg, transparent, var(--border-strong) 20%,
	            color-mix(in srgb, var(--accent) 55%, transparent) 50%,
	            var(--border-strong) 80%, transparent);
	opacity: .8;
}
section.sunken::before { top: 0; }
section.sunken::after  { bottom: 0; }

.section-head { max-width: 62ch; margin: 0 auto clamp(32px, 4vw, 56px); text-align: center; }
.section-head p { color: var(--text-soft); margin: 0; }

.grid { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

/* Cards are lit glass: a gradient body, a bright top edge, and a spotlight
   that follows the pointer. --mx/--my are set by site.js; the fallback centre
   keeps the gradient sane before the first mousemove and without JS. */
.card {
	position: relative; isolation: isolate;
	background: var(--card-grad);
	border: 1px solid var(--border);
	border-radius: var(--radius-lg); padding: 24px;
	box-shadow: inset 0 1px 0 var(--card-edge);
	transition: transform .3s cubic-bezier(.2, .8, .2, 1), box-shadow .3s, border-color .3s;
}
.card::before {
	content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
	border-radius: inherit;
	background: radial-gradient(240px circle at var(--mx, 50%) var(--my, 0%), var(--spot), transparent 70%);
	opacity: 0; transition: opacity .35s;
}
.card:hover {
	transform: translateY(-4px);
	border-color: var(--border-strong);
	box-shadow: inset 0 1px 0 var(--card-edge), var(--shadow-lift);
}
.card:hover::before { opacity: 1; }
.card h3 { margin-bottom: .35em; }
.card p { color: var(--text-soft); margin: 0; font-size: .96rem; }
/* The tile shots are screenshots of real panels, so their aspect ratios are
   whatever shape the panel happened to be - anything from 578x168 to 272x1392.
   Left uncapped the tall ones scale to over 1300px and drag their whole grid
   row with them, so the height is bounded here. Anything shorter than the cap
   sits at its own size, centred; nothing is cropped by this rule. */
.card img {
	max-height: 200px; width: auto; margin: 0 auto 16px;
	border-radius: var(--radius);
	border: 1px solid var(--border); box-shadow: var(--shadow);
	transition: transform .4s cubic-bezier(.2, .8, .2, 1);
}
/* The two shots the cap alone cannot rescue, because they are extreme in one
   direction: letterboxing the 272x1392 monitoring bar to 200px tall leaves it
   39px wide, and the 2880x136 taskbar 12px tall. Both are framed and cropped
   to the part that carries the meaning instead, which is enough for a
   thumbnail to say what the tile is.

   Both offsets are tuned to the current screenshots, and are the only rules on
   the site that depend on what is inside an image rather than on its shape -
   so retune them if either shot is retaken:

     crop-top    6% rather than 0, because the very top of the monitoring bar
                 is a readout over empty grid. Dropping the window slightly
                 brings the first live graph trace into it.
     crop-start  26% rather than 0, because the left end of the taskbar shot is
                 the weather tile. The Start button and the pinned apps, which
                 are what that card is actually about, sit about a quarter of
                 the way along. */
.card img.crop-top,
.card img.crop-start { width: 100%; object-fit: cover; }
.card img.crop-top   { height: 200px; object-position: center 6%; }
.card img.crop-start { height: 96px;  object-position: 26% center; }

.card:hover img { transform: scale(1.015); }

/* Free vs premium is a real distinction in the product (PremiumComponentTypes),
   and someone deciding whether to buy needs it visible rather than buried. */
.tag {
	display: inline-block; font-size: .7rem; font-weight: 700;
	letter-spacing: .06em; text-transform: uppercase;
	padding: .25em .6em; border-radius: 999px; vertical-align: middle;
	margin-left: .5em;
}
.tag-free { background: var(--bg-sunken); color: var(--text-soft); border: 1px solid var(--border); }
.tag-pro  {
	background: linear-gradient(135deg,
	            color-mix(in srgb, var(--accent) 22%, transparent),
	            color-mix(in srgb, var(--accent-2) 22%, transparent));
	color: var(--accent);
	border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
	box-shadow: 0 0 16px -6px var(--glow-a);
}

/* ------------------------------------------------------------- reveal ---- */

/* Content lifts in as it enters the viewport. Gated on .js so that with
   scripting off nothing is ever left invisible - the class is stamped by the
   inline snippet in <head>, before first paint, so there is no flash of
   already-placed content jumping as the observer catches up. */
.js .reveal { opacity: 0; transform: translateY(18px); }
.js .reveal.is-in {
	opacity: 1; transform: none;
	transition: opacity .7s cubic-bezier(.2, .8, .2, 1), transform .7s cubic-bezier(.2, .8, .2, 1);
}

/* --------------------------------------------------------------- prose --- */

.prose { max-width: 76ch; margin: 0 auto; }
.prose h2 { margin-top: 2em; padding-bottom: .35em; border-bottom: 1px solid var(--border); }
.prose h3 { margin-top: 1.6em; }
.prose ul, .prose ol { padding-left: 1.3em; }
.prose li { margin-bottom: .4em; }
.prose img {
	border-radius: var(--radius); border: 1px solid var(--border);
	box-shadow: var(--shadow); margin: 1.5em 0;
}
.prose figure { margin: 1.8em 0; }
.prose figcaption { color: var(--text-faint); font-size: .88rem; text-align: center; margin-top: .6em; }

.note {
	position: relative;
	background: linear-gradient(90deg, var(--accent-soft), transparent 85%);
	border-left: 3px solid var(--accent);
	border-radius: 0 var(--radius) var(--radius) 0;
	padding: 14px 18px; margin: 1.5em 0;
	box-shadow: -6px 0 24px -14px var(--glow-a);
}
.note p:last-child { margin-bottom: 0; }

/* -------------------------------------------------------------- footer --- */

.site-footer {
	position: relative;
	border-top: 1px solid var(--border); background: var(--sunken-grad);
	padding: 44px 0; margin-top: clamp(48px, 7vw, 88px);
	color: var(--text-soft); font-size: .92rem;
}
.site-footer::before {
	content: ""; position: absolute; top: -1px; left: 0; right: 0; height: 1px;
	background: linear-gradient(90deg, transparent,
	            color-mix(in srgb, var(--accent) 50%, transparent) 50%, transparent);
}
.site-footer .wrap { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; }
.site-footer nav { margin-left: auto; display: flex; gap: 18px; flex-wrap: wrap; }
.site-footer a { color: var(--text-soft); }
.site-footer a:hover { color: var(--accent); }

/* --------------------------------------------------------------- small --- */

@media (max-width: 640px) {
	body { font-size: 16px; }
	.site-header .wrap { height: auto; padding-block: 10px; flex-wrap: wrap; }
	.site-nav { width: 100%; margin-left: 0; justify-content: center; flex-wrap: wrap; }
	.site-footer .wrap { flex-direction: column; align-items: flex-start; }
	.site-footer nav { margin-left: 0; }
	/* The tilt costs more than it is worth at this width, and the beams are
	   mostly off-screen anyway. */
	.js .hero-shot { animation: none; }
	.hero-atmos .beam { width: 72%; filter: blur(60px); }
}
