/**
 * TiorAI entity navigation — the canonical shared "navigate by identity" grid.
 *
 * A circular avatar with a name under it and a quiet count beneath that. Reads
 * as a set of identities rather than as a set of labels, which is the whole
 * reason it is not `tr-catnav`: the two sit on the same page and must NOT look
 * alike, or the visitor reads them as one long list of the same thing.
 *
 * The distinction is carried by shape, not by decoration. Category boxes are
 * wide, left-aligned and text-led; these are narrow, centred and avatar-led.
 * Nothing here is heavier than the category grid — it is lighter, and smaller.
 *
 * Self-contained: declares its own custom properties, reads only theme-unified
 * tokens.
 *
 * @package TiorAI
 */

.tr-entitynav {
	/* Five across on desktop, set by the consumer where five is wrong. */
	--tiorai-entitynav-columns: 5;

	--tiorai-entitynav-ease: cubic-bezier(0.22, 1, 0.36, 1);

	--tiorai-entitynav-box: transparent;
	--tiorai-entitynav-box-hover: var(--color-bg-secondary);
	--tiorai-entitynav-border: transparent;

	/* Brand, so the three navigation components on one archive answer a pointer
	   the same way: `tr-discovery` already hovered to `--color-primary` and
	   `tr-catnav` now does too. The token is theme-paired, so one reference is
	   right in both. */
	--tiorai-entitynav-border-hover: var(--color-primary);

	/* The RESTING frame of a carousel card. It used to borrow the hover token,
	   which was invisible while both were neutral and would have painted every
	   card brand the moment the hover token moved. Two states, two tokens. */
	--tiorai-entitynav-frame: var(--border-default);

	--tiorai-entitynav-size: 44px;

	/* The grid's row gap, as a token, so a consumer whose rows carry framed
	   cards can space them differently from one whose cards are borderless. */
	--tiorai-entitynav-row-gap: var(--space-2);

	/* The carousel's track width. A consumer whose labels are longer than a
	   model name sets it wider: the audience row runs at 148px because a role is
	   a phrase where a model is a name, and the name is clipped to one line in
	   both. */
	--tiorai-entitynav-track: 124px;
}

[data-theme="dark"] .tr-entitynav {
	--tiorai-entitynav-box-hover: rgba(255, 255, 255, 0.06);

	/* 14% instead of 8%, so the edge survives on black. */
	--tiorai-entitynav-frame: rgba(255, 255, 255, 0.14);
}

/*
 * Avatar colours are declared on the ITEM, not on the root.
 *
 * This is load-bearing and was a real bug when it sat on `.tr-entitynav`. A
 * custom property is substituted where it is DECLARED, not where it is finally
 * used: `--tiorai-entitynav-hue` is an inline style on each <li>, so a rule that
 * resolves `hsl(var(--tiorai-entitynav-hue, 200) ...)` up on the <nav> computes
 * once, against the 200 fallback, and every child inherits that one finished
 * colour. Measured: all fifteen discs rendered the same blue-grey.
 *
 * Declared here, each item substitutes its OWN hue.
 *
 * Chroma and lightness are fixed and only the hue rotates, so fifteen discs read
 * as one family in fifteen shades rather than as fifteen unrelated colours.
 */
.tr-entitynav__item {
	--tiorai-entitynav-avatar-bg: hsl(var(--tiorai-entitynav-hue, 200) 62% 94%);
	--tiorai-entitynav-avatar-ink: hsl(var(--tiorai-entitynav-hue, 200) 55% 28%);
	--tiorai-entitynav-avatar-ring: hsl(var(--tiorai-entitynav-hue, 200) 45% 84%);
}

[data-theme="dark"] .tr-entitynav__item {
	/* 22% lightness holds the hue without glowing — the neon-on-black treatment
	   is an explicit anti-reference for this brand, so the disc is a muted tint
	   with bright ink, not a lit chip. Ink at 88% clears AA on it comfortably. */
	--tiorai-entitynav-avatar-bg: hsl(var(--tiorai-entitynav-hue, 200) 38% 22%);
	--tiorai-entitynav-avatar-ink: hsl(var(--tiorai-entitynav-hue, 200) 70% 88%);
	--tiorai-entitynav-avatar-ring: hsl(var(--tiorai-entitynav-hue, 200) 35% 32%);
}

.tr-entitynav__head {
	max-width: 60ch;
	margin: 0 auto var(--space-5);
	text-align: center;
}

/* Same size and tone as `.tr-catnav__heading` and `.tr-discovery__heading`:
   three section labels on one page, one treatment. */
.tr-entitynav__heading {
	margin: 0;
	font-size: var(--font-size-sm);
	font-weight: var(--font-weight-semibold);
	letter-spacing: 0.02em;
	color: var(--color-text-secondary);
}

.tr-entitynav__deck {
	margin: var(--space-2) 0 0;
	font-size: var(--font-size-xs);
	line-height: var(--line-height-relaxed);
	color: var(--color-text-tertiary);
}

.tr-entitynav__grid {
	display: grid;
	grid-template-columns: repeat(var(--tiorai-entitynav-columns), minmax(0, 1fr));
	column-gap: var(--space-2);
	row-gap: var(--tiorai-entitynav-row-gap);
	margin: 0;
	padding: 0;
	list-style: none;
}

/* ==========================================================================
   Item
   ========================================================================== */

/* Borderless at rest, on purpose. Fifteen bordered boxes beside twelve bordered
   category boxes is twenty-seven outlines stacked down the page; dropping the
   resting border is what makes this section read as lighter than the one above
   it while staying on the same grid. The border appears on interaction, so the
   hit area is still explicit the moment it matters. */
.tr-entitynav__item {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-2);

	padding: var(--space-3) var(--space-2);

	border: 1px solid var(--tiorai-entitynav-border);
	border-radius: var(--radius-lg);
	background: var(--tiorai-entitynav-box);
	text-align: center;

	transition:
		background-color 0.18s var(--tiorai-entitynav-ease),
		border-color 0.18s var(--tiorai-entitynav-ease);
}

.tr-entitynav__item:hover,
.tr-entitynav__item:focus-within {
	background: var(--tiorai-entitynav-box-hover);
	border-color: var(--tiorai-entitynav-border-hover);
}

/* ==========================================================================
   Avatar
   ========================================================================== */

.tr-entitynav__avatar {
	display: inline-flex;
	align-items: center;
	justify-content: center;

	/* flex: none so a long name below can never squash the disc into an oval. */
	flex: none;
	width: var(--tiorai-entitynav-size);
	height: var(--tiorai-entitynav-size);

	border-radius: 50%;
	background: var(--tiorai-entitynav-avatar-bg);
	box-shadow: inset 0 0 0 1px var(--tiorai-entitynav-avatar-ring);

	overflow: hidden;
	transition: transform 0.18s var(--tiorai-entitynav-ease);
}

.tr-entitynav__item:hover .tr-entitynav__avatar,
.tr-entitynav__item:focus-within .tr-entitynav__avatar {
	transform: scale(1.06);
}

.tr-entitynav__initials {
	font-size: var(--font-size-sm);
	font-weight: var(--font-weight-bold);
	letter-spacing: 0.01em;
	line-height: 1;
	color: var(--tiorai-entitynav-avatar-ink);

	/* The disc is the shape; the initials must not be able to break it. */
	user-select: none;
}

/* A real logo, when one is supplied, occupies the same disc at the same size.
   `contain` rather than `cover`: a wordmark cropped to a circle is unreadable,
   and a square glyph is unaffected either way. */
.tr-entitynav__logo {
	width: 100%;
	height: 100%;
	object-fit: contain;
	padding: 6px;
}

/* A registry glyph, when one is supplied. It takes the same ink the initials
   would have taken, so the icon and the disc it sits in are two values of one
   hue rather than a coloured shape on a coloured field — which is what keeps a
   grid of forty different glyphs reading as one set.

   Sized in `em` off the disc rather than in fixed pixels: `--tiorai-entitynav-size`
   drops to 40px at 480px wide, and a 22px glyph inside a 40px disc has lost its
   optical margin. The width/height attributes on the element are the no-CSS
   fallback and are deliberately left at their intrinsic 22. */
.tr-entitynav__icon {
	width: 50%;
	height: 50%;
	color: var(--tiorai-entitynav-avatar-ink);
}

/* ==========================================================================
   Name and count
   ========================================================================== */

.tr-entitynav__name {
	font-size: var(--font-size-sm);
	font-weight: var(--font-weight-semibold);
	line-height: var(--line-height-tight);
	color: var(--color-text-primary);

	/* Two lines maximum, so one long name ("Stable Diffusion") cannot make its
	   row taller than the rest of the grid. */
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;

	/* ...and two lines MINIMUM, which is what actually aligns the row.
	   Clamping alone caps the tall ones; it does not lift the short ones. With
	   only the clamp, a row reading "Ideogram / Stable Diffusion / Adobe Firefly"
	   put two counts on one baseline and the third a line lower, because the
	   two-line name pushed its own count down. Measured at 390px, where three
	   names in a row wrap differently. Reserving the second line costs nothing
	   visually — the space is empty either way — and makes every count in the
	   grid sit on one line.

	   2.5em against this element's own tight line-height is exactly two lines;
	   the `lh` unit would say so more directly but is too new to rely on. */
	min-height: 2.5em;
}

.tr-entitynav__link {
	color: inherit;
	text-decoration: none;
}

/* One anchor, stretched over the whole card. */
.tr-entitynav__link::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: inherit;
}

.tr-entitynav__link:focus-visible::after {
	outline: var(--tiorai-focus-ring-width, 2px) solid var(--tiorai-focus-ring-color, var(--color-primary));
	outline-offset: 2px;
}

/* Secondary to the name by size AND tone — the brief's requirement, and the same
   subordination the category component applies to its count. */
.tr-entitynav__count {
	font-size: var(--font-size-xs);
	font-variant-numeric: tabular-nums;
	color: var(--tiorai-entitynav-count, var(--color-text-tertiary));
}

[data-theme="dark"] .tr-entitynav {
	/* --color-text-tertiary and --color-text-secondary resolve to the SAME value
	   in dark (#CBD5E1), so the count cannot be subordinate by colour without a
	   component-local step down. Same measurement, and the same value, as the
	   category component uses. */
	--tiorai-entitynav-count: #94a3b8;
}

/* ==========================================================================
   Carousel layout
   The scroll shell is the shared `tr-carousel` (assets/css/carousel.css). What
   is here is only what changes about the LIST and the CARD when the section
   scrolls instead of wrapping.
   ========================================================================== */

/* The arrows are flanking overlays on the track itself (assets/css/carousel.css)
   rather than a control row above it, so this layout adds no vertical chrome at
   all — and because they are absolutely positioned inside the carousel, nothing
   shifts when the script reveals them. No CLS either way. */

/* From grid to a single scrolling row. Columns become a fixed track width per
   card, which is what makes snapping predictable and stops the last card being
   a half-width sliver. */
.tr-entitynav__grid--carousel {
	display: flex;
	flex-wrap: nowrap;
	gap: var(--space-3);

	/* End padding so the final card is not flush against the viewport edge when
	   scrolled fully right. */
	padding-inline-end: var(--space-1);
}

.tr-entitynav__grid--carousel .tr-entitynav__item {
	flex: 0 0 auto;

	/* 124px, down from 148px. The card holds an avatar, one or two words and a
	   number — at 148px it was mostly padding, and the extra width bought no
	   legibility while costing a whole card off the visible row. Narrower also
	   means more models are in view before anyone has to scroll, which is the
	   point of the section. */
	width: var(--tiorai-entitynav-track);
	scroll-snap-align: start;

	/* A card, not a bare cell. In grid mode these are borderless so twenty-seven
	   outlines do not stack down the page; in a scrolling row the border is what
	   makes each item read as a discrete, swipeable object and shows where one
	   card ends and the next begins. */
	border-color: var(--tiorai-entitynav-frame);
	background: var(--tiorai-entitynav-box-card, var(--surface-base));
	padding: var(--space-3) var(--space-2);

	/* 4px rather than the grid's 8px between avatar, name and count: inside a
	   frame the three read as one object top to bottom, not as three separate
	   things. */
	gap: var(--space-1);
}

[data-theme="dark"] .tr-entitynav__grid--carousel .tr-entitynav__item {
	--tiorai-entitynav-box-card: rgba(255, 255, 255, 0.045);
}

.tr-entitynav__grid--carousel .tr-entitynav__item:hover,
.tr-entitynav__grid--carousel .tr-entitynav__item:focus-within {
	border-color: var(--color-primary);
}

/* Still the anchor, and still the largest thing in the card, but scaled with it:
   a 52px disc in a 124px card leaves no margin around itself. */
.tr-entitynav__grid--carousel .tr-entitynav__avatar {
	width: 46px;
	height: 46px;
}

/*
 * Tighter stack in the carousel.
 *
 * The name's `min-height: 2.5em` reserves a second line so that counts line up
 * across a WRAPPING grid — where a row can mix "Ideogram" with "Stable
 * Diffusion" and there is no other mechanism to align them. In a single
 * scrolling row that reservation buys nothing and costs a full empty line
 * between every one-word name and its number, which is most of them.
 *
 * Released here only. The grid layout keeps it, and the 5-row alignment it
 * fixes is unaffected.
 */
/*
 * One line, never two.
 *
 * The base component clamps to two lines and reserves both, which is right for a
 * wrapping grid. In the scrolling row it produced exactly the ragged result the
 * reservation was meant to prevent: "Stable Diffusion" and "Microsoft Copilot"
 * took two lines while every neighbour took one, so their counts sat a line
 * lower than the rest of the row.
 *
 * Truncating instead keeps every card structurally identical, which is what
 * makes a scrolling row scannable — the eye learns one shape and stops
 * re-reading it. `display: block` is required to undo the base rule's
 * `-webkit-box`, which ignores text-overflow.
 *
 * The full name stays in the DOM and is what assistive tech and search engines
 * read; only the pixels are clipped.
 */
.tr-entitynav__grid--carousel .tr-entitynav__name {
	min-height: 0;
	display: block;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;

	/* The clipped name is the card's whole label, so it gets the full inner
	   width — no centring inset that would truncate it earlier than necessary. */
	width: 100%;
}

@media (max-width: 640px) {
	/* Narrower again so a phone shows two cards and a HINT of the third — the
	   clearest possible signal that the row scrolls, and cheaper than any arrow
	   or dot. */
	.tr-entitynav__grid--carousel .tr-entitynav__item {
		width: min(var(--tiorai-entitynav-track), 112px);
	}
}

/* ==========================================================================
   `carousel-narrow` — the row becomes a grid again above 1024px

   ONE markup, two presentations. The template emits the carousel's shell and
   card classes at every width, and this block undoes them on a desktop, because
   the alternative — printing a grid and a carousel and hiding one — puts every
   entity in the document twice.

   ONLY THE CONTAINER CHANGES. The card keeps the carousel's treatment at every
   width — its border, its fill, its 46px disc, its one-line name — so a card is
   one object that happens to sit in a row on a phone and in a grid on a desktop.
   An earlier revision undid the frame above 1024px and got two visibly different
   cards for one list, which is the thing a shared component exists to prevent.

   `min-width: 1025px` and not `max-width: 1024px`, so the pair is N / N+1 per
   CLAUDE.md's breakpoint rule and nothing double-applies at exactly 1024.
   ========================================================================== */

@media (min-width: 1025px) {
	.tr-entitynav__grid--adaptive {
		display: grid;
		grid-template-columns: repeat(var(--tiorai-entitynav-columns), minmax(0, 1fr));
		column-gap: var(--space-2);
		row-gap: var(--tiorai-entitynav-row-gap);
	}

	/* The one card property the grid must take back: a fixed track width in a
	   grid column would leave a ragged right edge. Everything else stays. */
	.tr-entitynav__grid--adaptive .tr-entitynav__item {
		width: auto;
	}

	/*
	 * THE OVERFLOW COLLAPSE — desktop only, and only once the script has said it
	 * can be undone.
	 *
	 * `.is-collapsible` is added by assets/js/entity-nav.js at the same moment it
	 * unhides the button. Without the script neither exists, so the full grid
	 * renders and nothing is unreachable. That is the whole reason the hiding
	 * hangs off a JS-added class rather than off the markup.
	 */
	.tr-entitynav--adaptive.is-collapsible:not(.is-expanded) .tr-entitynav__item--overflow {
		display: none;
	}

	.tr-entitynav--adaptive .tr-entitynav__reveal {
		display: flex;
	}
}

/* ==========================================================================
   The overflow button

   Same treatment and the same two-label contract as `tr-catnav`'s disclosure,
   because it is the same class of action — only the mechanism differs, since
   this one has to leave its items in a list a phone turns into a carousel.
   ========================================================================== */

/* Hidden at every width by default; the desktop block above turns it on. Below
   1025px the row IS the reveal — swiping shows the tail — so a button there
   would be a second control for a gesture already used. */
.tr-entitynav__reveal {
	display: none;
	justify-content: center;
	margin: var(--space-4) 0 0;
}

.tr-entitynav__reveal-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-1);

	/* 44px is the touch-target floor, and it applies to a pointer target too. */
	min-height: 44px;
	padding: var(--space-2) var(--space-3);
	border: 0;
	border-radius: var(--radius-md);
	background: none;
	font-family: inherit;
	font-size: var(--font-size-sm);
	font-weight: var(--font-weight-medium);

	/* `--color-primary` directly: the token is theme-paired since the DS lime
	   migration, and its light value measures 4.97:1 on the white this band
	   paints, so one reference clears AA in both themes. */
	color: var(--color-primary);
	cursor: pointer;
}

.tr-entitynav__reveal-btn:hover .tr-entitynav__reveal-label {
	text-decoration: underline;
}

.tr-entitynav__reveal-btn:focus-visible {
	outline: var(--tiorai-focus-ring-width, 2px) solid var(--tiorai-focus-ring-color, var(--color-primary));
	outline-offset: 2px;
}

/* One label per state, chosen by the SAME attribute that announces it. "Show all
   40 audiences" is a promise already kept once the grid is open, so the open
   state offers the way back instead. */
.tr-entitynav__reveal-label--open,
.tr-entitynav__reveal-btn[aria-expanded="false"] .tr-entitynav__reveal-label--open {
	display: none;
}

.tr-entitynav__reveal-btn[aria-expanded="true"] .tr-entitynav__reveal-label--open {
	display: inline;
}

.tr-entitynav__reveal-btn[aria-expanded="true"] .tr-entitynav__reveal-label--closed {
	display: none;
}

.tr-entitynav__reveal-icon {
	flex: none;
	transition: transform 0.18s var(--tiorai-entitynav-ease);
}

.tr-entitynav__reveal-btn[aria-expanded="true"] .tr-entitynav__reveal-icon {
	transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
	.tr-entitynav__reveal-icon {
		transition-duration: 0.01ms;
	}
}

@media (prefers-reduced-motion: reduce) {
	.tr-entitynav__item,
	.tr-entitynav__avatar {
		transition: none;
	}

	.tr-entitynav__item:hover .tr-entitynav__avatar,
	.tr-entitynav__item:focus-within .tr-entitynav__avatar {
		transform: none;
	}
}

/* ==========================================================================
   Responsive
   ========================================================================== */

/*
 * The narrow-width ladder sets `grid-template-columns` on the GRID, not
 * `--tiorai-entitynav-columns` on the root — and that difference is the whole
 * rule, not a style preference.
 *
 * The component's `columns` argument writes an INLINE style on the root
 * (`style="--tiorai-entitynav-columns:5"`). An inline declaration outranks any
 * stylesheet rule on the same element, media query or not, so while these
 * blocks redefined that property the ladder was dead for every consumer that
 * passed `columns` — the property was simply re-read from the inline value.
 *
 * Measured when the audience index (40 items, `columns => 5`) shipped: five
 * columns at 768px and five at 390px, cards ~62px wide with names broken across
 * four lines. The model row never showed it because a carousel ignores the
 * column count, and `tr-catnav` never showed it because its own ladder already
 * sets `grid-template-columns` directly — which is what this now matches.
 *
 * The custom property still sets the DESKTOP count, from the root or from the
 * consumer. Below 1024px the count is the component's to decide: a caller
 * asking for five across is describing a desktop rhythm, not requesting five
 * 62px cards on a phone.
 *
 * Single-class selectors, winning on source order rather than on specificity —
 * these sit at the end of the file, after the base grid rule. The carousel
 * layout needs no exclusion: it is `display: flex`, so `grid-template-columns`
 * is inert on it.
 */
/* The 1200 rung exists for consumers that ask for MORE than the default five —
   the audience index asks for seven. Without it the ladder went 7 → 4 in one
   step at 1024, which drops three columns at once and reflows the whole block.
   A consumer on the default five is unaffected: this rung also resolves to five
   for them. */
@media (max-width: 1200px) {
	.tr-entitynav__grid {
		grid-template-columns: repeat(5, minmax(0, 1fr));
	}
}

@media (max-width: 1024px) {
	.tr-entitynav__grid {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}
}

@media (max-width: 768px) {
	.tr-entitynav__grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}

@media (max-width: 480px) {
	/* Three across still fits at 390px because the card is avatar-width plus
	   padding, not label-width — which is the layout advantage of the avatar
	   treatment over the wide category box. */
	.tr-entitynav {
		--tiorai-entitynav-size: 40px;
	}

	.tr-entitynav__head {
		text-align: left;
	}
}
