/*
Theme Name:   AI Explorex
Theme URI:    https://aiexplorex.com
Description:  Directory interface for aiexplorex.com — dark-first, search-led, faceted. Brand identity preserved from the original; palette, type and layout rebuilt to a system. Child of Astra.
Author:       Alvion
Template:     astra
Version:      0.2.0
Text Domain:  aiexplorex
*/

/* ==========================================================================
   TOKENS

   The original site used nine unrelated hues decoratively — red search text,
   pale-yellow headings, mint on purple, green in the header. Here colour is
   functional: brand, one accent, and a semantic set where the colour IS the
   information (pricing tier, dead link).

   Every foreground/background pair below was measured, not guessed. Two of the
   original brand colours failed WCAG AA on dark and are handled explicitly:
     #24249A as text on surface = 1.56:1  -> demoted to a surface/button colour
     #EB181B as text on surface = 3.66:1  -> replaced by --c-danger (5.93:1)
   ========================================================================== */

:root {
	/* --- surface scale: near-black indigo, four steps ------------------- */
	--c-bg:        #070A1F;
	--c-s1:        #0E1230;
	--c-s2:        #161B45;
	--c-s3:        #1F2557;
	--c-line:      rgba(142, 142, 247, 0.18);
	--c-line-soft: rgba(142, 142, 247, 0.10);

	/* --- brand -------------------------------------------------------- */
	--c-brand:      #8E8EF7; /* tonal variant for text/icons — 6.41:1 on s1 */
	--c-brand-deep: #24249A; /* the original brand indigo — surfaces only   */
	--c-brand-mid:  #49399E; /* the original card-button purple             */

	/* --- accent ------------------------------------------------------- */
	--c-accent:     #12F0E2; /* the original cyan — 12.71:1 on s1          */
	--c-accent-ink: #04121A; /* text ON accent — 13.18:1                   */
	--c-mint:       #76FAE0; /* the original mint — 14.39:1 on s1          */

	/* --- text --------------------------------------------------------- */
	--c-text:  #E7E9FF; /* 15.24:1 */
	--c-muted: #A9AEDA; /*  8.50:1 */
	--c-dim:   #8B92C4; /*  6.10:1 on card, 4.80:1 on the tag surface — the
	                        floor. The earlier #7E85B8 cleared the card at
	                        5.17 but only reached 4.06 on --c-s3. */

	/* --- semantic: the pricing tier IS the colour ---------------------- */
	--c-free:    #3DDC84; /*  9.22:1 */
	--c-trial:   #FFC24B; /* 10.24:1 */
	--c-paid:    #8E8EF7;
	--c-danger:  #FF6B6B; /*  5.93:1 */

	/* --- type: Inter. Roboto reads generic-Android; Inter is the register
	       AI tooling actually uses, and its tabular figures matter here
	       because this interface is full of counts and prices. ---------- */
	--f-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

	--t-xs: 0.75rem;   /* 12 */
	--t-sm: 0.8125rem; /* 13 */
	--t-md: 0.875rem;  /* 14 */
	--t-base: 1rem;    /* 16 — body floor */
	--t-lg: 1.125rem;
	--t-xl: 1.375rem;
	--t-2xl: 1.75rem;
	--t-3xl: clamp(2rem, 4.5vw, 3.25rem);

	/* --- space: 4px base, density 7/10 --------------------------------- */
	--sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
	--sp-5: 20px; --sp-6: 24px; --sp-8: 32px; --sp-10: 40px;
	--sp-12: 48px; --sp-16: 64px;

	--r-sm: 8px; --r-md: 12px; --r-lg: 16px; --r-full: 999px;

	--shadow:      0 4px 16px rgba(0, 0, 0, 0.32);
	--shadow-lift: 0 12px 32px rgba(0, 0, 0, 0.44);

	--ease: cubic-bezier(0.2, 0, 0.2, 1);
	--dur:  180ms;

	--w-page: 1280px;
	--w-prose: 68ch;
	--h-header: 68px;

	/* --- derived surfaces -------------------------------------------------
	   These were ten loose rgba() values scattered through the file. Each one
	   silently assumed a dark backdrop, so each one was a light-mode bug
	   waiting to happen. Naming them is what makes the second theme possible
	   at all — a colour that is not a token cannot be re-themed. */
	--c-header-bg:  rgba(7, 10, 31, 0.88);
	--c-glow-a:     rgba(18, 240, 226, 0.16);
	--c-glow-b:     rgba(142, 142, 247, 0.20);
	--c-glow-c:     rgba(73, 57, 158, 0.28);
	--c-cta-glow:   rgba(73, 57, 158, 0.45);
	--c-dots:       rgba(231, 233, 255, 0.5);
	--c-dots-op:    0.18;
	--c-chip-bg:    rgba(22, 27, 69, 0.6);
	--c-free-line:  rgba(61, 220, 132, 0.28);
	--c-trial-line: rgba(255, 194, 75, 0.28);
	--c-accent-line:rgba(18, 240, 226, 0.28);
	--c-focus:      var(--c-accent);
	--c-scrim:      rgba(7, 10, 31, 0.6);
}

/* ==========================================================================
   LIGHT THEME

   Not an inversion. Every brand and semantic colour was re-measured against
   white: reusing the dark values failed WCAG AA on 9 of 9 pairs — cyan
   #12F0E2 on white is 1.35:1, amber #FFC24B is 1.39:1. Each token therefore
   gets a light-mode twin, verified at >= 4.5:1 on every surface it can land on
   (`tools/check_contrast.py`).

   Three states, deliberately: no attribute = follow the operating system;
   data-theme="light" and data-theme="dark" = the reader has chosen, and their
   choice outranks the system in both directions.
   ========================================================================== */

@media (prefers-color-scheme: light) {
	:root:not([data-theme="dark"]) {
		--c-bg:        #F4F5FB;
		--c-s1:        #FFFFFF;
		--c-s2:        #ECEEF8;
		--c-s3:        #E0E3F5;
		--c-line:      rgba(58, 58, 184, 0.20);
		--c-line-soft: rgba(58, 58, 184, 0.11);

		--c-brand:      #3A3AB8;
		--c-accent:     #05706A;
		--c-accent-ink: #FFFFFF;
		--c-mint:       #05605A;

		--c-text:  #101233;
		--c-muted: #4A4F73;
		--c-dim:   #5E6489;

		--c-free:   #0C6E3C;
		--c-trial:  #8A5A00;
		--c-paid:   #3A3AB8;
		--c-danger: #C0342E;

		--c-header-bg:  rgba(255, 255, 255, 0.88);
		--c-glow-a:     rgba(6, 122, 114, 0.10);
		--c-glow-b:     rgba(58, 58, 184, 0.10);
		--c-glow-c:     rgba(73, 57, 158, 0.12);
		--c-cta-glow:   rgba(73, 57, 158, 0.12);
		--c-dots:       rgba(16, 18, 51, 0.45);
		--c-dots-op:    0.12;
		--c-chip-bg:    rgba(255, 255, 255, 0.75);
		--c-free-line:  rgba(14, 122, 67, 0.30);
		--c-trial-line: rgba(138, 90, 0, 0.30);
		--c-accent-line:rgba(6, 122, 114, 0.30);
		--c-scrim:      rgba(16, 18, 51, 0.45);

		--shadow:      0 4px 16px rgba(16, 18, 51, 0.08);
		--shadow-lift: 0 12px 32px rgba(16, 18, 51, 0.14);
	}
}

:root[data-theme="light"] {
	--c-bg:        #F4F5FB;
	--c-s1:        #FFFFFF;
	--c-s2:        #ECEEF8;
	--c-s3:        #E0E3F5;
	--c-line:      rgba(58, 58, 184, 0.20);
	--c-line-soft: rgba(58, 58, 184, 0.11);

	--c-brand:      #3A3AB8;
	--c-accent:     #05706A;
	--c-accent-ink: #FFFFFF;
	--c-mint:       #05605A;

	--c-text:  #101233;
	--c-muted: #4A4F73;
	--c-dim:   #5E6489;

	--c-free:   #0C6E3C;
	--c-trial:  #8A5A00;
	--c-paid:   #3A3AB8;
	--c-danger: #C0342E;

	--c-header-bg:  rgba(255, 255, 255, 0.88);
	--c-glow-a:     rgba(6, 122, 114, 0.10);
	--c-glow-b:     rgba(58, 58, 184, 0.10);
	--c-glow-c:     rgba(73, 57, 158, 0.12);
	--c-cta-glow:   rgba(73, 57, 158, 0.12);
	--c-dots:       rgba(16, 18, 51, 0.45);
	--c-dots-op:    0.12;
	--c-chip-bg:    rgba(255, 255, 255, 0.75);
	--c-free-line:  rgba(14, 122, 67, 0.30);
	--c-trial-line: rgba(138, 90, 0, 0.30);
	--c-accent-line:rgba(6, 122, 114, 0.30);
	--c-scrim:      rgba(16, 18, 51, 0.45);

	--shadow:      0 4px 16px rgba(16, 18, 51, 0.08);
	--shadow-lift: 0 12px 32px rgba(16, 18, 51, 0.14);
}

/* ==========================================================================
   BASE — a zero-specificity layer, scoped to our own three roots

   Two collision rules, and both matter more than they look:

   1. SCOPE. Base element rules are confined to `.aix-header`, `#aix-main` and
      `.aix-footer` — the only markup this theme owns. They are NOT hung off
      `body`, because WordPress and its plugins inject siblings into body:
      the admin bar, cookie notices, consent banners, chat widgets. A bare
      `* { box-sizing }` or `.aix a { color }` reaches all of them and silently
      restyles software we do not control.

   2. SPECIFICITY ZERO. Everything here is wrapped in `:where()`, which scores
      0,0,0. A component class (0,1,0) therefore ALWAYS wins against the base
      layer — no ordering luck, no specificity arms race, and no `!important`
      anywhere in this file except the reduced-motion override at the end,
      where overriding is the entire point.

   The practical consequence: adding a rule to a component can never be
   defeated by a rule further out, and can never leak further out either.
   ========================================================================== */

:where(.aix-header, #aix-main, .aix-footer),
:where(.aix-header, #aix-main, .aix-footer) :where(*, *::before, *::after) {
	box-sizing: border-box;
}

/* The admin bar sets its own box model and sizes itself in content-box.
   Explicitly handing it back is cheaper than excluding it from every rule. */
#wpadminbar, #wpadminbar * { box-sizing: content-box; }

body.aix {
	margin: 0;
	background: var(--c-bg);
	color: var(--c-text);
	font-family: var(--f-sans);
	font-size: var(--t-base);
	line-height: 1.55;
	-webkit-font-smoothing: antialiased;
	font-feature-settings: "cv05" 1, "ss01" 1;
	/* Tells the browser which way form controls, scrollbars and the
	   default canvas should render — without it, native widgets stay
	   light-mode while everything around them is dark. */
	color-scheme: dark;
}

:root[data-theme="light"] body.aix { color-scheme: light; }

:where(.aix-header, #aix-main, .aix-footer) :where(img) {
	max-width: 100%;
	height: auto;
	display: block;
}

:where(.aix-header, #aix-main, .aix-footer) :where(a) {
	color: var(--c-accent);
	text-decoration: none;
}
:where(.aix-header, #aix-main, .aix-footer) :where(a:hover) { text-decoration: underline; }

:where(.aix-header, #aix-main, .aix-footer) :where(h1, h2, h3, h4) {
	line-height: 1.2;
	letter-spacing: -0.015em;
	margin: 0;
}

:where(.aix-header, #aix-main, .aix-footer) :where(button, input, select, textarea) {
	font: inherit;
	color: inherit;
}

/* One focus style, everywhere. The original removed focus rings entirely. */
:where(.aix-header, #aix-main, .aix-footer) :focus-visible {
	outline: 2px solid var(--c-focus);
	outline-offset: 2px;
	border-radius: var(--r-sm);
}

.aix-wrap { width: 100%; max-width: var(--w-page); margin: 0 auto; padding: 0 var(--sp-5); }

.aix-sr {
	position: absolute; width: 1px; height: 1px;
	padding: 0; margin: -1px; overflow: hidden;
	clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.aix-sr:focus {
	position: fixed; top: var(--sp-3); left: var(--sp-3);
	width: auto; height: auto; clip: auto;
	padding: var(--sp-3) var(--sp-4);
	background: var(--c-accent); color: var(--c-accent-ink);
	border-radius: var(--r-sm); font-weight: 600; z-index: 999;
}

.aix-icon { width: 1em; height: 1em; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* ==========================================================================
   HEADER
   ========================================================================== */

.aix-header {
	position: sticky; top: 0; z-index: 100;
	background: var(--c-header-bg);
	backdrop-filter: blur(14px) saturate(140%);
	border-bottom: 1px solid var(--c-line-soft);
}

.aix-header__inner { display: flex; align-items: center; gap: var(--sp-5); min-height: var(--h-header); }

.aix-logo { display: flex; align-items: center; gap: var(--sp-3); flex: 0 0 auto; }
.aix-logo img { height: 38px; width: auto; }
.aix-logo__text { font-weight: 700; font-size: var(--t-lg); color: var(--c-text); letter-spacing: -0.02em; }

/* Compact search that lives in the bar on inner pages — a directory's
   primary action should never be more than one element away. */
.aix-header__search { flex: 1 1 auto; max-width: 420px; margin-left: var(--sp-2); }
@media (max-width: 1100px) { .aix-header__search { display: none; } }

.aix-nav { margin-left: auto; }
.aix-nav__list { display: flex; align-items: center; gap: var(--sp-1); list-style: none; margin: 0; padding: 0; }

.aix-nav__link {
	display: inline-flex; align-items: center; gap: var(--sp-2);
	min-height: 44px; padding: 0 var(--sp-4);
	border-radius: var(--r-sm);
	color: var(--c-muted); font-size: var(--t-md); font-weight: 500;
	transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
	cursor: pointer;
}
.aix-nav__link:hover { background: var(--c-s2); color: var(--c-text); text-decoration: none; }
.aix-nav__link[aria-current="page"] { color: var(--c-text); }

.aix-nav__link--cta {
	background: var(--c-accent); color: var(--c-accent-ink); font-weight: 600;
}
.aix-nav__link--cta:hover { background: var(--c-mint); color: var(--c-accent-ink); }

.aix-nav__caret { transition: transform var(--dur) var(--ease); }

/* --- mega menu ----------------------------------------------------------- */

.aix-has-mega { position: static; }

.aix-mega {
	position: absolute; left: 0; right: 0; top: 100%;
	background: var(--c-s1);
	border-top: 1px solid var(--c-line-soft);
	box-shadow: var(--shadow-lift);
	opacity: 0; visibility: hidden; transform: translateY(-6px);
	transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility var(--dur);
	max-height: min(72vh, 620px); overflow-y: auto;
}
.aix-has-mega:hover .aix-mega,
.aix-has-mega:focus-within .aix-mega { opacity: 1; visibility: visible; transform: translateY(0); }
.aix-has-mega:hover .aix-nav__caret,
.aix-has-mega:focus-within .aix-nav__caret { transform: rotate(180deg); }

.aix-mega__grid {
	display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
	gap: var(--sp-2) var(--sp-8); padding: var(--sp-8) var(--sp-5) var(--sp-10);
}
.aix-mega__group > a {
	display: block; font-size: var(--t-xs); font-weight: 700;
	letter-spacing: 0.08em; text-transform: uppercase; color: var(--c-accent);
	padding-bottom: var(--sp-2); margin-bottom: var(--sp-2);
	border-bottom: 1px solid var(--c-line);
}
.aix-mega__group ul { list-style: none; margin: 0 0 var(--sp-6); padding: 0; }
.aix-mega__group li a {
	display: flex; justify-content: space-between; align-items: center; gap: var(--sp-2);
	padding: var(--sp-2) var(--sp-2) var(--sp-2) 0;
	font-size: var(--t-md); color: var(--c-muted); border-radius: var(--r-sm);
	transition: color var(--dur) var(--ease);
}
.aix-mega__group li a:hover { color: var(--c-text); text-decoration: none; }
.aix-mega__count {
	font-size: var(--t-xs); color: var(--c-dim);
	font-variant-numeric: tabular-nums;
	background: var(--c-s2); padding: 1px var(--sp-2); border-radius: var(--r-full);
}

/* --- burger -------------------------------------------------------------- */

.aix-burger {
	display: none; margin-left: auto;
	width: 44px; height: 44px; padding: 0;
	align-items: center; justify-content: center;
	border: 1px solid var(--c-line); border-radius: var(--r-sm);
	background: transparent; color: var(--c-text); cursor: pointer;
}

@media (max-width: 991px) {
	.aix-burger { display: inline-flex; }
	.aix-nav {
		position: fixed; inset: var(--h-header) 0 0; margin-left: 0;
		background: var(--c-bg); overflow-y: auto;
		padding: var(--sp-4) var(--sp-5) var(--sp-16);
		transform: translateX(100%); transition: transform 240ms var(--ease);
	}
	.aix-nav[data-open="true"] { transform: translateX(0); }
	.aix-nav__list { flex-direction: column; align-items: stretch; gap: var(--sp-1); }
	.aix-nav__link { justify-content: space-between; font-size: var(--t-base); }
	.aix-mega {
		position: static; opacity: 1; visibility: visible; transform: none;
		box-shadow: none; background: transparent; border: 0;
		max-height: none; display: none;
	}
	.aix-has-mega[data-open="true"] .aix-mega { display: block; }
	.aix-mega__grid { grid-template-columns: 1fr; padding: 0 0 var(--sp-4); }
}

/* ==========================================================================
   SEARCH — the primary action of a directory, styled as such
   ========================================================================== */

.aix-search { position: relative; display: flex; align-items: center; }

.aix-search__icon {
	position: absolute; left: var(--sp-4);
	width: 18px; height: 18px; color: var(--c-dim); pointer-events: none;
}

.aix-search input[type="search"] {
	width: 100%; height: 48px;
	padding: 0 var(--sp-4) 0 calc(var(--sp-4) * 2 + 18px);
	border: 1px solid var(--c-line);
	border-radius: var(--r-full);
	background: var(--c-s2);
	color: var(--c-text);
	font-size: var(--t-md);
	transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
	-webkit-appearance: none;
}
.aix-search input::placeholder { color: var(--c-dim); }
.aix-search input:focus { background: var(--c-s3); border-color: var(--c-brand); outline-offset: 0; }
.aix-search input::-webkit-search-cancel-button { filter: invert(0.7); }

.aix-search--hero input[type="search"] { height: 60px; font-size: var(--t-lg); padding-left: 58px; }
.aix-search--hero .aix-search__icon { left: var(--sp-5); width: 22px; height: 22px; }
.aix-search--hero { max-width: 660px; margin: 0 auto; }

.aix-search__submit {
	position: absolute; right: var(--sp-2);
	height: 44px; padding: 0 var(--sp-5);
	border: 0; border-radius: var(--r-full);
	background: var(--c-accent); color: var(--c-accent-ink);
	font-size: var(--t-md); font-weight: 600; cursor: pointer;
	transition: background var(--dur) var(--ease);
}
.aix-search__submit:hover { background: var(--c-mint); }
.aix-search:not(.aix-search--hero) .aix-search__submit { display: none; }

/* ==========================================================================
   HERO
   ========================================================================== */

.aix-hero {
	position: relative; overflow: hidden;
	padding: var(--sp-16) 0 var(--sp-12);
	text-align: center;
	border-bottom: 1px solid var(--c-line-soft);
}

/* The original leaned on a stock space photograph. This is the same idea —
   depth, a sense of field — rendered in CSS, so it costs nothing, never
   pixelates, and carries no stock-licence question.

   It also MOVES, and the two layers move by different means on purpose:

   - the aurora drifts by `transform`, which the compositor handles on the GPU
     without repainting. Animating `background-position` on a 140%-tall stack of
     three radial gradients would repaint that whole area every frame, which is
     the cheap-looking way to do this and the expensive way to run it.
   - the dot grid drifts by `background-position`, because a transform would
     drag its edges into view. The tile is 44px, so shifting exactly 44px and
     starting over is seamless — the grid appears to travel forever.

   Both keyframe sets open AND close on the resting state. The site-wide
   reduced-motion block (see REDUCED MOTION) collapses animations to 0.01ms and
   one iteration, so whatever sits at 100% is what a reduced-motion reader is
   left looking at. Ending anywhere else would leave them with a shifted
   background rather than the composition as designed. */
.aix-hero::before {
	content: ""; position: absolute; inset: -20% -10% auto;
	height: 140%;
	background:
		radial-gradient(ellipse 60% 50% at 50% 0%,  var(--c-glow-a), transparent 70%),
		radial-gradient(ellipse 50% 45% at 15% 30%, var(--c-glow-b), transparent 70%),
		radial-gradient(ellipse 45% 40% at 85% 20%, var(--c-glow-c), transparent 70%);
	pointer-events: none;
	will-change: transform;
	/* Not var(--ease): that curve is 180ms UI easing, and its sharp start reads
	   as a twitch over 32 seconds. This one is symmetric and slow at both ends. */
	animation: aix-aurora 32s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
.aix-hero::after {
	content: ""; position: absolute; inset: 0;
	background-image: radial-gradient(circle, var(--c-dots) 0.6px, transparent 0.6px);
	background-size: 44px 44px;
	opacity: var(--c-dots-op); pointer-events: none;
	animation: aix-hero-drift 72s linear infinite;
}

/* Deliberately small numbers. The overscan on ::before is -20%/-10%, so a 6%
   travel and a 1.08 scale never bring an edge into frame. Anything larger stops
   reading as depth and starts reading as a screensaver behind a search box. */
@keyframes aix-aurora {
	0%,
	100% { transform: translate3d(0, 0, 0)        scale(1);    }
	33%  { transform: translate3d(-3.5%, 2%, 0)   scale(1.08); }
	66%  { transform: translate3d(3%, -2.5%, 0)   scale(1.04); }
}

/* One tile, once. 44px in 72s is about 0.6px a second — below the speed at
   which the eye tracks it, which is the point: it should register as the page
   being alive, not as something to watch. */
@keyframes aix-hero-drift {
	from { background-position: 0 0;       }
	to   { background-position: 44px 44px; }
}

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

   Layer order inside .aix-hero, bottom to top:
     ::before  aurora gradients   (always painted — the fallback AND the poster)
     ::after   dot grid
     .aix-hero__film             the video, faded in by aix.js once it can play
     .aix-hero__scrim            the 58% scrim that makes the text legible
     .aix-hero__inner            the actual content

   58% is measured, not guessed. The clip's brightest second averages luma 135
   of 255; at 0.42 opacity-complement that lands near 55, which holds the title
   (#E7E9FF) at about 11.5:1 and the lede (#A9AEDA) at about 5.4:1 — both past
   WCAG AA at the WORST frame, not the average one. Lighten this and the lede
   is the first thing to fail, because it is the lower-contrast colour. */
.aix-hero__film {
	position: absolute; inset: 0; z-index: 1;
	overflow: hidden; pointer-events: none;
}

.aix-hero__video {
	position: absolute; inset: 0;
	width: 100%; height: 100%;
	object-fit: cover;
	opacity: 0;
	transition: opacity 900ms ease;
}
/* aix.js sets this once the file can actually play, so a slow or blocked
   download leaves the aurora on screen instead of flashing an empty element. */
.aix-hero__video[data-ready="1"] { opacity: 1; }

.aix-hero__scrim {
	position: absolute; inset: 0;
	background:
		linear-gradient(180deg,
			rgba(7, 10, 31, 0.50) 0%,
			rgba(7, 10, 31, 0.62) 45%,
			rgba(7, 10, 31, 0.58) 100%);
}

/* Light theme runs the scrim lighter and tinted to its own background, or the
   hero would be a dark rectangle in the middle of a pale page. The text is
   dark here (#101233), so the scrim has the opposite job — it has to lift the
   video's shadows rather than crush its highlights. */
:root[data-theme="light"] .aix-hero__scrim {
	background:
		linear-gradient(180deg,
			rgba(244, 245, 251, 0.74) 0%,
			rgba(244, 245, 251, 0.82) 45%,
			rgba(244, 245, 251, 0.78) 100%);
}
@media (prefers-color-scheme: light) {
	:root:not([data-theme="dark"]) .aix-hero__scrim {
		background:
			linear-gradient(180deg,
				rgba(244, 245, 251, 0.74) 0%,
				rgba(244, 245, 251, 0.82) 45%,
				rgba(244, 245, 251, 0.78) 100%);
	}
}

/* Once the film is up it covers the aurora completely, so leaving those
   animations running is compositor work for pixels nobody can see. aix.js
   stamps data-film="1" on the section when the video starts. */
.aix-hero[data-film="1"]::before,
.aix-hero[data-film="1"]::after { animation-play-state: paused; }

.aix-hero__inner { position: relative; z-index: 2; }

.aix-eyebrow {
	display: inline-flex; align-items: center; gap: var(--sp-2);
	padding: var(--sp-2) var(--sp-4); margin-bottom: var(--sp-5);
	border: 1px solid var(--c-line); border-radius: var(--r-full);
	background: var(--c-chip-bg);
	font-size: var(--t-sm); font-weight: 500; color: var(--c-muted);
}
.aix-eyebrow strong { color: var(--c-accent); font-variant-numeric: tabular-nums; }

.aix-hero__title {
	font-size: var(--t-3xl); font-weight: 700; letter-spacing: -0.03em;
	margin: 0 0 var(--sp-4); color: var(--c-text);
}
.aix-hero__title em {
	font-style: normal;
	background: linear-gradient(100deg, var(--c-accent), var(--c-brand));
	-webkit-background-clip: text; background-clip: text; color: transparent;
}

.aix-hero__lede {
	max-width: 56ch; margin: 0 auto var(--sp-8);
	font-size: var(--t-lg); color: var(--c-muted);
}

/* Suggested searches — the pattern guidance is explicit that a directory's
   hero should lower search friction, not just present an empty box. */
.aix-suggest {
	display: flex; flex-wrap: wrap; justify-content: center;
	gap: var(--sp-2); margin-top: var(--sp-5);
	font-size: var(--t-sm); color: var(--c-dim);
}
.aix-suggest a {
	padding: var(--sp-1) var(--sp-3);
	border-radius: var(--r-full); background: var(--c-s2);
	color: var(--c-muted); border: 1px solid transparent;
}
.aix-suggest a:hover { color: var(--c-text); border-color: var(--c-line); text-decoration: none; }

/* ==========================================================================
   TRUST BAND — the directory pattern's named anti-pattern is "no trust cues".
   The live site has none at all: no counts, no dates, no method.
   ========================================================================== */

.aix-trust {
	display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
	gap: var(--sp-4); padding: var(--sp-8) 0;
	border-bottom: 1px solid var(--c-line-soft);
}
.aix-trust__item { display: flex; gap: var(--sp-3); align-items: flex-start; }
.aix-trust__icon {
	flex: 0 0 auto; width: 38px; height: 38px;
	display: grid; place-items: center;
	border-radius: var(--r-md); background: var(--c-s2); color: var(--c-accent);
}
.aix-trust__icon .aix-icon { width: 18px; height: 18px; }
.aix-trust__item strong { display: block; font-size: var(--t-md); color: var(--c-text); }
.aix-trust__item span { font-size: var(--t-sm); color: var(--c-dim); }

/* ==========================================================================
   SECTIONS
   ========================================================================== */

.aix-section { padding: var(--sp-12) 0; }
.aix-section--tight { padding-top: var(--sp-8); }

.aix-section__head {
	display: flex; align-items: flex-end; justify-content: space-between;
	flex-wrap: wrap; gap: var(--sp-3); margin-bottom: var(--sp-6);
}
.aix-section__title { font-size: var(--t-2xl); font-weight: 700; }
.aix-section__sub { margin: var(--sp-1) 0 0; font-size: var(--t-md); color: var(--c-dim); }
.aix-section__link { font-size: var(--t-md); font-weight: 500; white-space: nowrap; }

/* ==========================================================================
   CATEGORY TILES
   ========================================================================== */

.aix-cats {
	display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
	gap: var(--sp-3);
}
.aix-cat {
	display: flex; align-items: center; gap: var(--sp-3);
	padding: var(--sp-4);
	border: 1px solid var(--c-line-soft); border-radius: var(--r-md);
	background: var(--c-s1); color: var(--c-text);
	transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.aix-cat:hover {
	background: var(--c-s2); border-color: var(--c-brand);
	transform: translateY(-2px); text-decoration: none;
}
.aix-cat__icon {
	width: 34px; height: 34px; flex: 0 0 auto;
	display: grid; place-items: center;
	border-radius: var(--r-sm); background: var(--c-s3); color: var(--c-brand);
}
.aix-cat__icon .aix-icon { width: 17px; height: 17px; }
.aix-cat__name { font-size: var(--t-md); font-weight: 600; line-height: 1.3; }
.aix-cat__n { font-size: var(--t-xs); color: var(--c-dim); font-variant-numeric: tabular-nums; }

/* ==========================================================================
   TOOL CARD
   Named anti-pattern: "text-heavy cards". Fixed slots, clamped lines, one
   scannable row of facts — the card answers what/how much/how good at a glance.
   ========================================================================== */

.aix-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(282px, 1fr));
	gap: var(--sp-4);
}

.aix-card {
	position: relative;
	display: flex; flex-direction: column;
	padding: var(--sp-5);
	border: 1px solid var(--c-line-soft); border-radius: var(--r-lg);
	background: var(--c-s1);
	transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.aix-card:hover { background: var(--c-s2); border-color: var(--c-line); transform: translateY(-3px); }
.aix-card:focus-within { border-color: var(--c-brand); }

.aix-card__top { display: flex; align-items: flex-start; gap: var(--sp-3); margin-bottom: var(--sp-3); }

.aix-card__logo {
	width: 44px; height: 44px; flex: 0 0 auto;
	display: grid; place-items: center;
	border-radius: var(--r-md);
	background: var(--c-s3);
	object-fit: contain; padding: 6px;
	font-size: var(--t-lg); font-weight: 700; color: var(--c-brand);
	overflow: hidden;
}

.aix-card__title { font-size: var(--t-lg); font-weight: 600; letter-spacing: -0.01em; }
.aix-card__title a { color: var(--c-text); }
.aix-card__title a:hover { color: var(--c-accent); text-decoration: none; }
/* Whole card clickable, but only the title is a real link — so the tab order
   stays one stop per card instead of four. */
.aix-card__title a::after { content: ""; position: absolute; inset: 0; }

.aix-card__tagline {
	font-size: var(--t-md); color: var(--c-muted); margin: 0 0 var(--sp-4);
	display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
	overflow: hidden; min-height: calc(var(--t-md) * 1.55 * 2);
}

.aix-card__cats { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: 0 0 var(--sp-4); padding: 0; list-style: none; }

.aix-tag {
	display: inline-block; padding: 2px var(--sp-2);
	border-radius: var(--r-sm); background: var(--c-s3);
	font-size: var(--t-xs); font-weight: 500; color: var(--c-muted);
}
a.aix-tag { position: relative; z-index: 1; }
a.aix-tag:hover { background: var(--c-brand-mid); color: var(--c-text); text-decoration: none; }

.aix-card__foot {
	margin-top: auto; padding-top: var(--sp-3);
	border-top: 1px solid var(--c-line-soft);
	display: flex; align-items: center; gap: var(--sp-3);
	font-size: var(--t-sm);
}

/* --- pricing pill: the colour carries the meaning ------------------------ */

.aix-price {
	display: inline-flex; align-items: center; gap: var(--sp-1);
	padding: 3px var(--sp-3); border-radius: var(--r-full);
	font-size: var(--t-xs); font-weight: 600; letter-spacing: 0.01em;
	background: var(--c-s3); color: var(--c-paid);
	border: 1px solid transparent;
}
.aix-price--free    { color: var(--c-free);   border-color: var(--c-free-line); }
.aix-price--trial   { color: var(--c-trial);  border-color: var(--c-trial-line); }
.aix-price--freemium{ color: var(--c-accent); border-color: var(--c-accent-line); }
/* Never colour alone — a dot plus the word, so the tier survives greyscale
   and colour-blindness. */
.aix-price::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

.aix-card__from { color: var(--c-dim); font-variant-numeric: tabular-nums; }

.aix-card__rating {
	margin-left: auto; display: inline-flex; align-items: center; gap: var(--sp-1);
	color: var(--c-trial); font-weight: 600; font-variant-numeric: tabular-nums;
}
.aix-card__rating .aix-icon { width: 13px; height: 13px; fill: currentColor; stroke: none; }

.aix-card__out {
	position: relative; z-index: 1;
	display: inline-flex; align-items: center; justify-content: center;
	width: 32px; height: 32px; border-radius: var(--r-sm);
	background: var(--c-s3); color: var(--c-muted);
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.aix-card__out:hover { background: var(--c-accent); color: var(--c-accent-ink); text-decoration: none; }
.aix-card__out .aix-icon { width: 15px; height: 15px; }

.aix-card__flag {
	position: absolute; top: var(--sp-3); right: var(--sp-3);
	font-size: var(--t-xs); font-weight: 600; color: var(--c-danger);
}

/* Motion: 300-450ms staggered entry, and nothing at all if the user asked
   for less. CSS-only so no animation library ships for six cards. */
@media (prefers-reduced-motion: no-preference) {
	.aix-grid > * { animation: aix-rise 380ms var(--ease) backwards; }
	.aix-grid > :nth-child(1) { animation-delay: 0ms; }
	.aix-grid > :nth-child(2) { animation-delay: 45ms; }
	.aix-grid > :nth-child(3) { animation-delay: 90ms; }
	.aix-grid > :nth-child(4) { animation-delay: 135ms; }
	.aix-grid > :nth-child(5) { animation-delay: 180ms; }
	.aix-grid > :nth-child(6) { animation-delay: 225ms; }
	.aix-grid > :nth-child(n+7) { animation-delay: 260ms; }
}
@keyframes aix-rise {
	from { opacity: 0; transform: translateY(12px) scale(0.98); }
	to   { opacity: 1; transform: none; }
}

/* ==========================================================================
   ARCHIVE — filters are VISIBLE. Hidden filters is the third named
   anti-pattern for this pattern, and the live site has no filtering at all.
   ========================================================================== */

.aix-listing { display: grid; grid-template-columns: 258px minmax(0, 1fr); gap: var(--sp-8); align-items: start; }

.aix-facets {
	position: sticky; top: calc(var(--h-header) + var(--sp-4));
	max-height: calc(100vh - var(--h-header) - var(--sp-8));
	overflow-y: auto;
	padding: var(--sp-5);
	border: 1px solid var(--c-line-soft); border-radius: var(--r-lg);
	background: var(--c-s1);
}

.aix-facet + .aix-facet { margin-top: var(--sp-6); padding-top: var(--sp-6); border-top: 1px solid var(--c-line-soft); }
.aix-facet__title {
	font-size: var(--t-xs); font-weight: 700; letter-spacing: 0.08em;
	text-transform: uppercase; color: var(--c-dim); margin-bottom: var(--sp-3);
}
.aix-facet ul { list-style: none; margin: 0; padding: 0; }
.aix-facet li a {
	display: flex; justify-content: space-between; align-items: center; gap: var(--sp-2);
	padding: var(--sp-2) var(--sp-3);
	border-radius: var(--r-sm);
	font-size: var(--t-md); color: var(--c-muted);
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.aix-facet li a:hover { background: var(--c-s2); color: var(--c-text); text-decoration: none; }
.aix-facet li a[aria-current="true"] { background: var(--c-brand-mid); color: var(--c-text); font-weight: 600; }
.aix-facet__n { font-size: var(--t-xs); color: var(--c-dim); font-variant-numeric: tabular-nums; }
.aix-facet li a[aria-current="true"] .aix-facet__n { color: var(--c-mint); }

/* "+n more" disclosure rather than a clipped list */
.aix-facet__more {
	display: block; width: 100%; margin-top: var(--sp-2);
	padding: var(--sp-2) var(--sp-3);
	border: 0; border-radius: var(--r-sm); background: transparent;
	color: var(--c-accent); font-size: var(--t-sm); font-weight: 500;
	text-align: left; cursor: pointer;
}
.aix-facet__more:hover { background: var(--c-s2); }
.aix-facet li[hidden] { display: none; }

.aix-results__bar {
	display: flex; align-items: center; justify-content: space-between;
	flex-wrap: wrap; gap: var(--sp-3); margin-bottom: var(--sp-5);
}
.aix-results__count { font-size: var(--t-md); color: var(--c-muted); }
.aix-results__count strong { color: var(--c-text); font-variant-numeric: tabular-nums; }

.aix-facets__toggle { display: none; }

@media (max-width: 960px) {
	.aix-listing { grid-template-columns: 1fr; }
	.aix-facets { position: static; max-height: none; display: none; }
	.aix-facets[data-open="true"] { display: block; }
	.aix-facets__toggle {
		display: inline-flex; align-items: center; gap: var(--sp-2);
		min-height: 44px; padding: 0 var(--sp-4); margin-bottom: var(--sp-4);
		border: 1px solid var(--c-line); border-radius: var(--r-sm);
		background: var(--c-s1); color: var(--c-text);
		font-size: var(--t-md); font-weight: 500; cursor: pointer;
	}
}

/* --- breadcrumbs, pagination, empty -------------------------------------- */

.aix-crumbs { font-size: var(--t-sm); color: var(--c-dim); margin-bottom: var(--sp-5); }
.aix-crumbs a { color: var(--c-muted); }
.aix-crumbs span { margin: 0 var(--sp-2); opacity: 0.5; }

.aix-pagination { display: flex; justify-content: center; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-10); }
.aix-pagination .page-numbers {
	display: inline-flex; align-items: center; justify-content: center;
	min-width: 44px; height: 44px; padding: 0 var(--sp-3);
	border-radius: var(--r-sm); background: var(--c-s1);
	border: 1px solid var(--c-line-soft);
	color: var(--c-muted); font-size: var(--t-md); font-weight: 500;
}
.aix-pagination .page-numbers:hover { background: var(--c-s2); color: var(--c-text); text-decoration: none; }
.aix-pagination .page-numbers.current { background: var(--c-accent); border-color: var(--c-accent); color: var(--c-accent-ink); font-weight: 700; }

/* A dead end is a design failure: always offer the next move. */
.aix-empty-state {
	padding: var(--sp-12) var(--sp-5); text-align: center;
	border: 1px dashed var(--c-line); border-radius: var(--r-lg);
	background: var(--c-s1);
}
.aix-empty-state h3 { font-size: var(--t-xl); margin-bottom: var(--sp-2); }
.aix-empty-state p { color: var(--c-muted); margin: 0 0 var(--sp-5); }

/* ==========================================================================
   SINGLE TOOL
   ========================================================================== */

.aix-single { padding: var(--sp-8) 0 var(--sp-12); }
.aix-single__grid { display: grid; grid-template-columns: minmax(0, 1fr) 336px; gap: var(--sp-8); align-items: start; }

.aix-panel {
	padding: var(--sp-8);
	border: 1px solid var(--c-line-soft); border-radius: var(--r-lg);
	background: var(--c-s1);
}
.aix-panel--aside { position: sticky; top: calc(var(--h-header) + var(--sp-4)); padding: var(--sp-6); }

.aix-single__head { display: flex; align-items: center; gap: var(--sp-4); margin-bottom: var(--sp-4); }
.aix-single__logo {
	width: 64px; height: 64px; flex: 0 0 auto;
	border-radius: var(--r-md); background: var(--c-s3);
	object-fit: contain; padding: var(--sp-2);
}
.aix-single__title { font-size: var(--t-2xl); font-weight: 700; }
.aix-single__tagline { font-size: var(--t-lg); color: var(--c-muted); margin: 0 0 var(--sp-6); max-width: var(--w-prose); }

.aix-prose { font-size: var(--t-base); color: var(--c-muted); max-width: var(--w-prose); }
.aix-prose h2, .aix-prose h3 { color: var(--c-text); margin: var(--sp-8) 0 var(--sp-3); font-size: var(--t-xl); }
.aix-prose > * + * { margin-top: var(--sp-4); }

.aix-notice {
	display: flex; gap: var(--sp-3); align-items: flex-start;
	padding: var(--sp-4) var(--sp-5);
	border: 1px solid var(--c-line); border-left: 3px solid var(--c-brand);
	border-radius: var(--r-md); background: var(--c-s2);
	font-size: var(--t-md); color: var(--c-muted);
}
.aix-notice--warn { border-left-color: var(--c-danger); }
.aix-notice .aix-icon { width: 18px; height: 18px; color: var(--c-brand); margin-top: 2px; }
.aix-notice--warn .aix-icon { color: var(--c-danger); }

.aix-facts { list-style: none; margin: 0 0 var(--sp-5); padding: 0; }
.aix-facts li {
	display: flex; justify-content: space-between; align-items: center; gap: var(--sp-4);
	padding: var(--sp-3) 0; border-bottom: 1px solid var(--c-line-soft);
	font-size: var(--t-md);
}
.aix-facts li:last-child { border-bottom: 0; }
.aix-facts .k { color: var(--c-dim); }
.aix-facts .v { color: var(--c-text); font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; }

.aix-source { margin: var(--sp-4) 0 0; font-size: var(--t-xs); line-height: 1.5; color: var(--c-dim); }
.aix-source a { color: var(--c-dim); text-decoration: underline; }
.aix-source a:hover { color: var(--c-accent); }

@media (max-width: 900px) {
	.aix-single__grid { grid-template-columns: 1fr; }
	.aix-panel--aside { position: static; order: -1; }
}

/* ==========================================================================
   BUTTONS
   ========================================================================== */

.aix-btn {
	display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
	min-height: 44px; padding: 0 var(--sp-5);
	border: 1px solid transparent; border-radius: var(--r-sm);
	background: var(--c-brand-mid); color: var(--c-text);
	font-size: var(--t-md); font-weight: 600; cursor: pointer;
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.aix-btn:hover { background: var(--c-brand-deep); color: #fff; text-decoration: none; }

.aix-btn--primary { background: var(--c-accent); color: var(--c-accent-ink); }
.aix-btn--primary:hover { background: var(--c-mint); color: var(--c-accent-ink); }

.aix-btn--ghost { background: transparent; border-color: var(--c-line); color: var(--c-muted); }
.aix-btn--ghost:hover { background: var(--c-s2); color: var(--c-text); border-color: var(--c-brand); }

.aix-btn--block { width: 100%; }
.aix-btn .aix-icon { width: 16px; height: 16px; }

/* ==========================================================================
   CTA BAND
   ========================================================================== */

.aix-cta {
	position: relative; overflow: hidden;
	padding: var(--sp-12) var(--sp-8); text-align: center;
	border: 1px solid var(--c-line); border-radius: var(--r-lg);
	background:
		radial-gradient(ellipse 70% 120% at 50% 0%, var(--c-cta-glow), transparent 70%),
		var(--c-s1);
}
.aix-cta h2 { font-size: var(--t-2xl); margin-bottom: var(--sp-3); }
.aix-cta p { color: var(--c-muted); max-width: 52ch; margin: 0 auto var(--sp-6); }

/* ==========================================================================
   FOOTER
   ========================================================================== */

.aix-footer {
	margin-top: var(--sp-16);
	padding: var(--sp-12) 0 var(--sp-6);
	border-top: 1px solid var(--c-line-soft);
	background: var(--c-s1);
}
.aix-footer__grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: var(--sp-8); margin-bottom: var(--sp-10); }
.aix-footer h3 {
	font-size: var(--t-xs); font-weight: 700; letter-spacing: 0.08em;
	text-transform: uppercase; color: var(--c-dim); margin-bottom: var(--sp-4);
}
.aix-footer ul { list-style: none; margin: 0; padding: 0; }
.aix-footer li { margin-bottom: var(--sp-2); }
.aix-footer li a { color: var(--c-muted); font-size: var(--t-md); }
.aix-footer li a:hover { color: var(--c-text); }
.aix-footer p { color: var(--c-muted); font-size: var(--t-md); margin: 0 0 var(--sp-3); max-width: 42ch; }

.aix-social { display: flex; gap: var(--sp-2); }
.aix-social a {
	display: inline-flex; align-items: center; justify-content: center;
	width: 40px; height: 40px; border-radius: var(--r-sm);
	background: var(--c-s3); color: var(--c-muted);
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.aix-social a:hover { background: var(--c-accent); color: var(--c-accent-ink); }
.aix-social .aix-icon { width: 17px; height: 17px; }

.aix-footer__base {
	display: flex; justify-content: space-between; flex-wrap: wrap; gap: var(--sp-3);
	padding-top: var(--sp-5); border-top: 1px solid var(--c-line-soft);
	font-size: var(--t-sm); color: var(--c-dim);
}

/* The developer credit sits at the same weight as the copyright line — a
   credit, not an advert. It inherits --c-dim from .aix-footer__base and only
   lifts the link itself, so it stays quiet in both themes without a second
   colour being defined anywhere. */
.aix-footer__credit a {
	color: var(--c-muted); text-decoration: none;
	border-bottom: 1px solid var(--c-line-soft);
}
.aix-footer__credit a:hover,
.aix-footer__credit a:focus-visible { color: var(--c-text); border-bottom-color: currentColor; }

@media (max-width: 860px) { .aix-footer__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .aix-footer__grid { grid-template-columns: 1fr; gap: var(--sp-6); } }

/* ==========================================================================
   BACK TO TOP
   ========================================================================== */

.aix-top {
	position: fixed; right: var(--sp-5); bottom: var(--sp-5); z-index: 90;
	width: 44px; height: 44px;
	display: grid; place-items: center;
	border: 1px solid var(--c-line); border-radius: var(--r-sm);
	background: var(--c-s2); color: var(--c-text);
	cursor: pointer; opacity: 0; visibility: hidden; transform: translateY(8px);
	transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility var(--dur);
}
.aix-top[data-show="true"] { opacity: 1; visibility: visible; transform: none; }
.aix-top:hover { background: var(--c-accent); color: var(--c-accent-ink); border-color: var(--c-accent); }

/* ==========================================================================
   REDUCED MOTION
   ========================================================================== */

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

/* ==========================================================================
   THEME TOGGLE
   ========================================================================== */

.aix-theme {
	display: inline-flex; align-items: center; justify-content: center;
	width: 44px; height: 44px; padding: 0;
	border: 1px solid var(--c-line); border-radius: var(--r-sm);
	background: transparent; color: var(--c-muted); cursor: pointer;
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.aix-theme:hover { background: var(--c-s2); color: var(--c-text); border-color: var(--c-brand); }

/* Only one glyph shows, and which one is decided by the resolved theme rather
   than by JavaScript — so the button is correct on the very first paint. */
.aix-theme__sun  { display: none; }
.aix-theme__moon { display: block; }
@media (prefers-color-scheme: light) {
	:root:not([data-theme="dark"]) .aix-theme__sun  { display: block; }
	:root:not([data-theme="dark"]) .aix-theme__moon { display: none; }
}
:root[data-theme="light"] .aix-theme__sun  { display: block; }
:root[data-theme="light"] .aix-theme__moon { display: none; }
:root[data-theme="dark"]  .aix-theme__sun  { display: none; }
:root[data-theme="dark"]  .aix-theme__moon { display: block; }

@media (max-width: 991px) { .aix-theme { margin-left: var(--sp-2); } }

/* ==========================================================================
   UTILITIES

   These exist to retire twelve inline `style=` attributes. An inline style
   scores 1,0,0,0 — it outranks every rule in this file, cannot be overridden
   by a theme, and is invisible to anyone reading the stylesheet. A one-off
   margin is not worth a permanent hole in the cascade.
   ========================================================================== */

.aix-measure     { max-width: 62ch; }
.aix-mt-8        { margin-top: var(--sp-8); }
.aix-mt-12       { margin-top: var(--sp-12); }
.aix-mb-4        { margin-bottom: var(--sp-4); }
.aix-title--sm   { font-size: var(--t-xl); }
.aix-panel--prose{ max-width: var(--w-prose); margin-left: auto; margin-right: auto; }
.aix-push        { margin-left: auto; }

.aix-search--inline { max-width: 420px; margin: 0 auto var(--sp-6); }
.aix-search--page   { max-width: 560px; margin-bottom: var(--sp-8); }
.aix-suggest--center{ justify-content: center; margin-bottom: var(--sp-6); }

/* ===================================================================
 * BROWSE, rebuilt 2026-09-25.
 *
 * The problem was height. The mega menu printed all 85 category links
 * in one grid and the archive rail printed 102 filter rows in one
 * column, so both stood taller than the viewport: every choice cost a
 * scroll away from the results. Two levels fix it - ten groups, then
 * only the open group children, which is at most twenty.
 * =================================================================== */

/* --- the mega menu: a fixed-height two-pane browser ---------------- */

.aix-mega { max-height: none; overflow: visible; }
.aix-mega__inner { padding: var(--sp-5) var(--sp-5) var(--sp-4); }

.aix-mega__head {
	display: flex; align-items: center; gap: var(--sp-3);
	padding-bottom: var(--sp-4); margin-bottom: var(--sp-4);
	border-bottom: 1px solid var(--c-line-soft);
	position: relative;
}
.aix-mega__qicon {
	position: absolute; left: var(--sp-3); top: 50%;
	transform: translateY(-60%); color: var(--c-dim);
	pointer-events: none; display: flex;
}
.aix-mega__q {
	flex: 1 1 auto; min-width: 0;
	padding: var(--sp-3) var(--sp-4) var(--sp-3) var(--sp-10);
	border: 1px solid var(--c-line); border-radius: var(--r-full);
	background: var(--c-bg); color: var(--c-text);
	font: inherit; font-size: var(--t-md);
}
.aix-mega__q:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 1px; }
.aix-mega__everything {
	display: inline-flex; align-items: center; gap: var(--sp-2);
	padding: var(--sp-2) var(--sp-4); border-radius: var(--r-full);
	background: var(--c-brand-mid); color: var(--c-text);
	font-size: var(--t-sm); font-weight: 600; white-space: nowrap;
}
.aix-mega__everything:hover { text-decoration: none; filter: brightness(1.12); }

/* The height is CAPPED here rather than left to the content. Each pane
   scrolls inside itself if it ever must, so the PAGE never does. */
.aix-mega__panes {
	display: grid; grid-template-columns: 232px minmax(0, 1fr);
	gap: var(--sp-5);
	height: min(46vh, 372px);
}
.aix-mega__rail {
	list-style: none; margin: 0; padding: 0 var(--sp-2) 0 0;
	overflow-y: auto; border-right: 1px solid var(--c-line-soft);
}
.aix-mega__tab {
	display: flex; justify-content: space-between; align-items: center; gap: var(--sp-2);
	padding: var(--sp-2) var(--sp-3); border-radius: var(--r-sm);
	font-size: var(--t-md); color: var(--c-muted);
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.aix-mega__tab:hover { background: var(--c-s2); color: var(--c-text); text-decoration: none; }
.aix-mega__tab[data-active="true"] { background: var(--c-brand-mid); color: var(--c-text); font-weight: 600; }
.aix-mega__tab[data-active="true"] .aix-mega__count { color: var(--c-mint); background: rgba(0, 0, 0, 0.25); }

.aix-mega__panels { position: relative; overflow-y: auto; padding-right: var(--sp-2); }
.aix-mega__panel { display: none; }
.aix-mega__panel[data-active="true"] { display: block; }
.aix-mega__lead {
	display: inline-flex; align-items: center; gap: var(--sp-2);
	font-size: var(--t-xs); font-weight: 700; letter-spacing: 0.08em;
	text-transform: uppercase; color: var(--c-accent); margin-bottom: var(--sp-3);
}
.aix-mega__children {
	list-style: none; margin: 0; padding: 0;
	display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
	gap: var(--sp-1) var(--sp-5);
}
.aix-mega__children li a {
	display: flex; justify-content: space-between; align-items: center; gap: var(--sp-2);
	padding: var(--sp-2) var(--sp-2) var(--sp-2) 0;
	font-size: var(--t-md); color: var(--c-muted); border-radius: var(--r-sm);
}
.aix-mega__children li a:hover { color: var(--c-text); text-decoration: none; }
.aix-mega__empty { color: var(--c-dim); font-size: var(--t-md); padding: var(--sp-4) 0; }

.aix-mega__foot {
	display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2);
	padding-top: var(--sp-4); margin-top: var(--sp-4);
	border-top: 1px solid var(--c-line-soft);
}
.aix-mega__footlabel {
	font-size: var(--t-xs); font-weight: 700; letter-spacing: 0.08em;
	text-transform: uppercase; color: var(--c-dim); margin-right: var(--sp-2);
}

/* --- chips: one control shape for groups, children and prices ------ */

.aix-chip {
	display: inline-flex; align-items: center; gap: var(--sp-2);
	padding: var(--sp-2) var(--sp-3);
	border: 1px solid var(--c-line); border-radius: var(--r-full);
	background: var(--c-s1); color: var(--c-muted);
	font-size: var(--t-sm); line-height: 1.2; white-space: nowrap;
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
	            border-color var(--dur) var(--ease);
}
.aix-chip:hover { background: var(--c-s2); color: var(--c-text); text-decoration: none; }
.aix-chip[aria-current="true"] {
	background: var(--c-brand-mid); border-color: transparent;
	color: var(--c-text); font-weight: 600;
}
.aix-chip--sm { font-size: var(--t-xs); padding: var(--sp-1) var(--sp-3); }
.aix-chip--ghost { background: transparent; }
.aix-chip__n, .aix-mega__count { font-variant-numeric: tabular-nums; }
.aix-chip__n {
	font-size: var(--t-xs); color: var(--c-dim);
	background: var(--c-s2); padding: 1px var(--sp-2); border-radius: var(--r-full);
}
.aix-chip[aria-current="true"] .aix-chip__n { color: var(--c-mint); background: rgba(0, 0, 0, 0.25); }

/* The pricing tier IS the colour. These tokens already existed in the
   palette and the old filter rail never used one of them. */
.aix-chip--price[data-price*="free"]:not([data-price*="trial"])[aria-current="true"] { background: var(--c-free); color: #04180E; }
.aix-chip--price[data-price*="trial"][aria-current="true"] { background: var(--c-trial); color: #1B1300; }
.aix-chip--price[data-price*="paid"][aria-current="true"] { background: var(--c-paid); color: #0B0A24; }
.aix-chip--price[aria-current="true"] .aix-chip__n { color: inherit; background: rgba(255, 255, 255, 0.25); }

/* --- the archive bar ----------------------------------------------- */

.aix-browse__bar {
	position: sticky; top: var(--h-header); z-index: 5;
	background: color-mix(in srgb, var(--c-bg) 88%, transparent);
	backdrop-filter: blur(10px);
	border: 1px solid var(--c-line-soft); border-radius: var(--r-lg);
	padding: var(--sp-3) var(--sp-4);
	margin-bottom: var(--sp-6);
	display: grid; gap: var(--sp-3);
}
.aix-browse__row { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; }
.aix-browse__row--tools {
	padding-top: var(--sp-3); border-top: 1px solid var(--c-line-soft);
	justify-content: space-between; row-gap: var(--sp-3);
}

.aix-browse__sort { display: flex; align-items: center; gap: var(--sp-2); }
.aix-browse__sort select {
	padding: var(--sp-2) var(--sp-3); border-radius: var(--r-full);
	border: 1px solid var(--c-line); background: var(--c-s1);
	color: var(--c-text); font: inherit; font-size: var(--t-sm);
}
.aix-browse__go {
	padding: var(--sp-2) var(--sp-3); border-radius: var(--r-full);
	border: 1px solid var(--c-line); background: var(--c-s2);
	color: var(--c-text); font: inherit; font-size: var(--t-sm); cursor: pointer;
}
/* With JS the select submits itself, so the button is redundant. Hidden
   only once JS has said so, never in the markup. */
.js .aix-browse__go { display: none; }

.aix-browse__count { margin: 0; font-size: var(--t-sm); color: var(--c-muted); }
.aix-browse__count strong { color: var(--c-text); font-variant-numeric: tabular-nums; }


/* --- the browse layout: selection at the side, results beside it --------
   Onkar, 2026-09-26: the tool selection moved out of the bar under the header
   and into a sidebar.

   min-width: 0 on the main column is load-bearing. A grid item defaults to
   min-width: auto, so .aix-grid's 282px minimum would push the column wider
   than its track and the whole page would scroll sideways. */

.aix-browse {
	display: grid;
	grid-template-columns: 252px minmax(0, 1fr);
	gap: var(--sp-5);
	align-items: start;
}
.aix-browse__main { min-width: 0; }

/* Sticky, own scroll, capped to the viewport. The original rail put 102 rows
   in one column with no cap and was taller than any screen; a sidebar repeats
   that mistake unless it is bounded. */
.aix-browse__side {
	position: sticky; top: calc(var(--h-header) + var(--sp-4));
	max-height: calc(100vh - var(--h-header) - var(--sp-8));
	overflow-y: auto;
	display: grid; gap: var(--sp-5);
	padding: var(--sp-4);
	border: 1px solid var(--c-line-soft); border-radius: var(--r-lg);
	background: var(--c-s1);
	scrollbar-width: thin;
}

.aix-side__label {
	margin: 0 0 var(--sp-2);
	font-size: var(--t-xs); font-weight: 600; letter-spacing: 0.06em;
	text-transform: uppercase; color: var(--c-dim);
}

.aix-side__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }

.aix-side__item,
.aix-side__subitem {
	display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
	padding: var(--sp-2) var(--sp-3);
	border-radius: var(--r-md);
	font-size: var(--t-sm); color: var(--c-muted);
	transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.aix-side__item > span:first-child,
.aix-side__subitem > span:first-child { min-width: 0; }

.aix-side__item:hover,
.aix-side__subitem:hover { background: var(--c-s2); color: var(--c-text); text-decoration: none; }

.aix-side__item[aria-current="true"] {
	background: var(--c-brand-mid); color: var(--c-text); font-weight: 600;
}
.aix-side__item[aria-current="true"] .aix-chip__n { color: var(--c-mint); background: rgba(0, 0, 0, 0.25); }

/* The open group's children, indented under it and marked by a rule rather
   than by indentation alone, so the nesting survives a narrow column. */
.aix-side__sub {
	list-style: none; margin: 2px 0 var(--sp-2) var(--sp-3); padding: 0 0 0 var(--sp-3);
	border-left: 1px solid var(--c-line-soft);
	display: grid; gap: 1px;
}
.aix-side__subitem { font-size: var(--t-xs); padding: var(--sp-1) var(--sp-2); }
.aix-side__subitem[aria-current="true"] { background: var(--c-s3, var(--c-s2)); color: var(--c-text); font-weight: 600; }

.aix-side__prices { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

.aix-side__clear {
	display: inline-flex; align-items: center; gap: var(--sp-2);
	font-size: var(--t-sm); color: var(--c-dim);
}
.aix-side__clear:hover { color: var(--c-danger); text-decoration: none; }

/* The bar is now the RESULTS bar only — count and sort. It is the first and
   only row, so the divider the old stacked layout needed would be a line
   above nothing. */
.aix-browse__row--tools:first-child { padding-top: 0; border-top: 0; }

@media (max-width: 991px) {
	/* One column, sidebar above the results — and it STAYS vertical and stays
	   capped. Two things were tried and rejected here:
	   · un-capping it, which is how a filter list ends up taller than the
	     screen and pushes the results out of sight — the original failure;
	   · turning the lists into horizontal chip scrollers, which cannot work
	     while the open group's children are nested inside its own <li>: that
	     one item becomes a single very wide column and the nesting is lost.
	   So: a short scrollable box. The results begin just under it. */
	.aix-browse { grid-template-columns: 1fr; gap: var(--sp-4); }
	.aix-browse__side {
		position: static;
		max-height: 44vh;
		gap: var(--sp-3);
	}
}

@media (max-width: 991px) {
	.aix-mega__panes { grid-template-columns: 1fr; height: auto; max-height: 60vh; overflow-y: auto; }
	.aix-mega__rail { border-right: 0; border-bottom: 1px solid var(--c-line-soft); padding: 0 0 var(--sp-3); }
	.aix-browse__bar { position: static; }
	.aix-browse__row { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: thin; }
	.aix-browse__row--tools { flex-wrap: wrap; overflow: visible; }
}

/* ==========================================================================
   BROWSE FILM — the full-page background on the tool archive and taxonomies
   ========================================================================== */

/* z-index -1 paints behind body's background, so body gives its colour up and
   <html> takes it. That keeps a solid canvas under the video for the moment
   before it loads, for reduced-motion readers who never load it, and for the
   overscroll area above and below the page. */
html { background: var(--c-bg); }
body.aix.aix-filmed { background: transparent; }

.aix-pagefilm {
	position: fixed; inset: 0; z-index: -1;
	overflow: hidden; pointer-events: none;
}

.aix-pagefilm__video {
	position: absolute; inset: 0;
	width: 100%; height: 100%;
	object-fit: cover;
	opacity: 0;
	transition: opacity 1200ms ease;
}
.aix-pagefilm__video[data-ready="1"] { opacity: 1; }

/* 80%, where the hero runs 58%, and the difference is the job each one does.
   The hero is a band with three lines of large type over it. This sits under
   an entire archive — breadcrumbs, a heading, filter labels, pagination — all
   of it small text, and the clip peaks at luma 127. At 80% that lands near 25,
   which keeps the muted text above 8:1 and leaves the film as a field the
   cards sit in rather than something competing with them.

   The cards themselves are opaque (--c-s1), so the video is only ever seen in
   the gutters and behind bare headings. That is the intended effect. */
.aix-pagefilm__scrim {
	position: absolute; inset: 0;
	background: rgba(7, 10, 31, 0.80);
}

:root[data-theme="light"] .aix-pagefilm__scrim {
	background: rgba(244, 245, 251, 0.88);
}
@media (prefers-color-scheme: light) {
	:root:not([data-theme="dark"]) .aix-pagefilm__scrim {
		background: rgba(244, 245, 251, 0.88);
	}
}

/* A fixed, permanently decoding video behind a list the reader scrolls through
   is work the phone pays for in battery the whole time it is open. The hero
   film is a band that scrolls away; this one never does. Below the sidebar
   breakpoint the archive is a single column of cards and the film would be
   almost entirely hidden behind them anyway. */
@media (max-width: 767px) {
	.aix-pagefilm { display: none; }
	body.aix.aix-filmed { background: var(--c-bg); }
}
