/* ==========================================================================
   ClassNavigator widget styles
   --------------------------------------------------------------------------
   See CLASSNAVIGATOR.md at the project root for the full picture.

   SCOPING RULE: every selector in this file must include at least one class
   token beginning with `classnav`. No bare element selectors (`footer`, `a`,
   `dialog`), no `:root`. This is enforced by a test -- src/test/frontend/
   scope.spec.js -- and it is not stylistic.

   Before this rule existed, the file ended with unscoped `footer`, `footer a`,
   and `:root` rules that measurably restyled the UNIVERSITY footer and changed
   document-wide scroll behavior on every page the widget appeared on:

       <footer>         display         block           -> flex
       <footer>         flex-direction  row             -> column
       footer link      color           rgb(29,117,204) -> rgb(109,109,109)
       <html>           scroll-behavior auto            -> smooth

   SPECIFICITY RULE: prefer a single BEM class (0,0,1,0) over descendant
   scoping like `.classnav .thing` (0,0,2,0). Not cosmetic either -- raising
   specificity silently flips cascade battles this widget currently LOSES to
   Castor, which would move the layout. Keeping one class per selector
   preserved rendering exactly through the rename. Where a battle genuinely
   needed preserving, it is pinned and marked PIN below.

   Task 3 will add a deliberate inbound reset and revisit the pins and the
   remaining `!important` rules. Until then this file is a faithful,
   encapsulated restatement of what already shipped.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Design tokens
   --------------------------------------------------------------------------
   These were on `:root`, which leaked them to the whole document. Declared on
   the widget root instead: descendants still inherit them, nothing outside can
   see them.

   COLOUR: values mirror ust-style/scss/_color.scss, which is the UST source of
   truth. tokens.spec.js parses that file and asserts these match, so drift is
   caught rather than discovered. The previous values were close but wrong
   (#59007b vs #510C76, #982eb4 vs #8348ad).

   TYPE: Lato, confirmed by measurement as the only brand font actually
   available on this page. Lora was requested by three `!important` rules but is
   registered nowhere, and Castor's Trueno is not registered either -- its
   @font-face lives in a stylesheet head.jspf never loads, so Castor's own
   typography falls back to Arial here.

   Only Lato 400 and 700 exist (self-hosted woff2 on capricorn-static-files).
   Asking for 100/500/600 gets a synthesized or snapped face, which is why the
   toolbar title measured as Arial-BoldMT. Weights are normalized to 400/700
   and tokens.spec.js enforces it.
   -------------------------------------------------------------------------- */

.classnav {
	/* Neutrals */
	--classnav-black: #000;
	--classnav-black-20: rgba(0, 0, 0, 0.2);
	--classnav-black-10: rgba(0, 0, 0, 0.10196);
	--classnav-white: #fff;
	--classnav-white-25: rgba(255, 255, 255, 0.1451);

	/* Greys, from ust-style _color.scss */
	--classnav-grey-lightest: #fdfdfd;
	--classnav-grey-lighter: #eeeeef;
	--classnav-grey-light: #e8e8e8;
	--classnav-grey-dark: #dededf;
	--classnav-grey-darker: #c8c8c9;
	--classnav-grey-darkest: #5a5858;
	--classnav-grey-border: #d8d8d8;

	/* Text greys. `--classnav-text-muted` is the disclaimer / helper-text
	   colour; measured at 4.54:1 on the frame background, which passes AA for
	   normal text but with little headroom. Do not lighten it, and re-measure
	   if the frame background changes. */
	--classnav-text: #212121;
	--classnav-text-muted: #6d6d6d;
	--classnav-text-subtle: #767676;

	/* UST purple, from ust-style _color.scss ($purple-primary and friends) */
	--classnav-purple: #510c76;
	--classnav-purple-light: #8348ad;
	--classnav-purple-lighter: #a97cca;
	--classnav-purple-lightest: #c1a0d9;
	--classnav-purple-dark: #410a5e;
	--classnav-purple-darker: #310748;
	--classnav-purple-darkest: #270539;
	--classnav-purple-light-15: rgba(131, 72, 173, 0.1451);

	/* Surfaces */
	--classnav-surface: #f0f0f0;
	--classnav-surface-raised: #fff;
	--classnav-border: #e9e9e9;

	/* Type. Arial is named explicitly because it is what currently renders when
	   a web font is missing, so degradation stays identical rather than
	   jumping to a different system stack. */
	--classnav-font: "Lato", Arial, Helvetica, sans-serif;
	--classnav-weight-normal: 400;
	--classnav-weight-bold: 700;

	/* Layout */
	--classnav-gutter: clamp(1rem, 3.333vw + 0.333rem, 3rem);
	--classnav-container: max(
		var(--classnav-gutter),
		calc((100% - 84rem) / 2)
	);
	--classnav-transition: 0.3s ease;
	--classnav-shadow: 0 1rem 1.5rem var(--classnav-black-20);
}

/* --------------------------------------------------------------------------
   Inbound reset
   --------------------------------------------------------------------------
   The widget renders inside `.main-body` on index.jsp, so Castor's body-copy
   rules reach into it. Every declaration below neutralizes a SPECIFIC named
   Castor rule -- this is deliberately an enumerated list, not `all: revert`.

   A blanket reset would strip focus indicators (WCAG 2.4.7) and forced-colors
   adaptations, trading one accessibility problem for a worse one. It would also
   encourage re-specifying everything in px, which breaks user text scaling
   (WCAG 1.4.4). Sizing here stays in rem/em.

   Scoped with `.classnav` as an ancestor so these win against Castor's
   `.main-body X` selectors (0,0,1,1) without needing !important.
   -------------------------------------------------------------------------- */

.classnav,
.classnav *,
.classnav *::before,
.classnav *::after {
	/* Redundant on ClassFinder: Foundation already sets this on `*` globally
	   (castor.css line ~71). Declared anyway so the widget does not depend on
	   the host page doing it -- the portability goal means it has to work on
	   pages with no Castor at all. */
	box-sizing: border-box;
}

.classnav {
	/* castor.css `.main-body { color: #212121; font-size: 1rem;
	   line-height: 1.5rem; margin-bottom: 1.25rem }` -- take ownership of
	   inherited typography rather than inheriting Castor's body copy. */
	font-family: var(--classnav-font);
	font-weight: var(--classnav-weight-normal);
	color: var(--classnav-text);
}

.classnav ul,
.classnav ol {
	/* castor.css `.main-body ul, .main-body ol { font-size: 1rem;
	   margin: 1.0625rem 0 1.25rem; padding: 0 0 0 2.8125rem }` -- the 45px
	   indent and list markers are wrong for a chat transcript. */
	margin: 0;
	padding: 0;
	list-style: none;
}

.classnav p {
	/* castor.css `.main-body p, .main-body th { margin: .375rem 0 1rem 0 }` */
	margin: 0;
}

.classnav h1,
.classnav h2,
.classnav h3,
.classnav h4,
.classnav h5,
.classnav h6 {
	/* castor.css `.main-body h1..h6 { color: #510C76;
	   font-family: "truenosemibold" }` plus per-level font-size and margin
	   rules. The widget sets its own heading scale. */
	margin: 0;
	color: inherit;
	font-family: inherit;
	font-size: inherit;
	font-weight: var(--classnav-weight-bold);
	line-height: inherit;
}

/* Castor's `button, .button` rule is broad:
     border-style, border-width, cursor, font-family, font-weight, line-height,
     margin, position, text-decoration, text-align, appearance, border-radius,
     display, padding, font-size, background-color, border-color, color,
     transition, text-transform
   Only the properties that actually cause harm and that no widget component
   needs to set are neutralized here. Resetting the rest (padding, background,
   border, border-radius, color) would push this rule to (0,0,1,1) authority
   over the widget's own single-class component rules at (0,0,1,0) and start
   overriding them -- so those are left to each component instead. */
.classnav button,
.classnav input,
.classnav textarea,
.classnav select {
	/* castor.css `button, .button { margin: 0 0 1.25rem }` and Foundation's
	   `textarea { margin: 0 0 1rem }` -- stray bottom margin on controls the
	   widget positions itself. */
	margin: 0;
	/* Form controls do not inherit font by default, and Castor sets
	   `font-family: "truenoregular"` which is not even loaded. */
	font-family: inherit;
	font-size: inherit;
	line-height: inherit;
	/* castor.css `button, .button { text-transform: uppercase }` -- this is why
	   "New Chat" in the markup was rendering as "NEW CHAT". */
	text-transform: none;
	/* castor.css `button, .button { text-align: center }` */
	text-align: inherit;
}

.classnav table {
	/* castor.css has a global `table { border-left/border-top: 1px solid
	   #dcd9d9 }` plus `.main-body th { background-color: #510C76; color: #fff }`
	   and `tr:nth-of-type(even)` striping. Markdown tables from the renderer
	   style themselves; see the renderer section. */
	width: auto;
	border: 0;
	border-collapse: collapse;
}

.classnav a {
	/* castor.css `a { color: #1d75cc }` */
	color: inherit;
}

/* Focus visibility is re-asserted rather than left to chance: Foundation-era
   stylesheets commonly zero out `outline`, and several widget controls set
   their own backgrounds, so the default ring can disappear. 3:1 against both
   the light and dark surfaces the widget uses (WCAG 1.4.11). */
.classnav :focus-visible {
	outline: 2px solid var(--classnav-purple-light);
	outline-offset: 2px;
}

/* Honour the user's motion preference for the typing dots, the error shake and
   any smooth scrolling. */
@media (prefers-reduced-motion: reduce) {
	.classnav *,
	.classnav *::before,
	.classnav *::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}

/* Offset scroll position so the widget isn't hidden behind the page header.
   Was `#classNavigatorBox`; the id is kept in the markup because it is a
   public anchor target, but styling now hangs off the class. */
.classnav {
	scroll-margin-top: 105px;
}

@media (max-width: 768px) {
	.classnav {
		scroll-margin-top: 88px;
	}
}

/* --------------------------------------------------------------------------
   Collapse disclosure (Task 7)
   --------------------------------------------------------------------------
   The chat used to be wrapped in Castor's `.faq` accordion on results pages:
   a `.faq-title` button toggled a `.faq-body`, and castor.js's
   sameSizeButtons() wrote an explicit inline `height: 273px` onto that body on
   load and on every resize. The stylesheet needed `height: auto !important` to
   fight that inline pixel height back off -- the only way to beat an inline
   style -- and the whole thing coupled the widget to Castor's JS.

   That wrapper is gone. The widget now owns the disclosure: a labelled
   `<button aria-expanded aria-controls>` and a `.classnav__panel` the button
   reveals. classnavigator.js flips `aria-expanded` and the panel's `hidden`
   attribute together; there is no inline height to fight, so the !important is
   gone with it. These are plain single-class rules (0,0,1,0); the panel is
   collapsed by the native `hidden` attribute, not by a CSS height hack, so the
   box grows with the conversation on its own.
   -------------------------------------------------------------------------- */

.classnav__disclosure {
	-webkit-appearance: none;
	appearance: none;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
	width: 100%;
	margin: 0 0 1rem;
	padding: 0.6rem 1rem;
	color: var(--classnav-white);
	font: inherit;
	font-weight: var(--classnav-weight-bold);
	line-height: 1.2;
	text-align: left;
	background: var(--classnav-purple-light);
	border: none;
	border-radius: 0.5rem;
	cursor: pointer;
}

.classnav__disclosure:hover,
.classnav__disclosure:focus {
	background: var(--classnav-purple);
}

.classnav__disclosure-icon {
	flex: 0 0 auto;
	width: 1.25em;
	height: 1.25em;
	color: var(--classnav-white);
	fill: none;
	pointer-events: none;
	/* Points down when collapsed; rotates to point up when expanded. The
	   transition is motion the user may have asked not to see -- the
	   prefers-reduced-motion block below neutralizes it. */
	transition: transform var(--classnav-transition);
}

/* Expanded: flip the chevron. Attribute selector keyed to the same
   aria-expanded the script maintains, so the glyph can never disagree with the
   announced state. */
.classnav__disclosure[aria-expanded="true"] .classnav__disclosure-icon {
	transform: rotate(180deg);
}

/* The panel's collapsed state is the native `hidden` attribute (display:none),
   not a CSS height, so there is nothing here to animate it shut and no inline
   height for Castor to write. Deliberately NO `display` rule on
   `.classnav__panel`: a <div> is block by default, and declaring
   `display: block` here would tie the UA `[hidden] { display: none }` rule
   (0,0,1,0) and win on source order, defeating the `hidden` attribute the
   whole collapse depends on. */

@media (forced-colors: active) {
	/* The purple fill is forced to a system colour; give the control a border so
	   its edge stays visible, and let the chevron track the system palette. */
	.classnav__disclosure {
		border: 1px solid ButtonText;
	}

	.classnav__disclosure-icon {
		forced-color-adjust: auto;
		color: ButtonText;
	}
}

/* --------------------------------------------------------------------------
   Chat frame
   -------------------------------------------------------------------------- */

/* Skin A (Task 8): a light card. The surface is white, the corners are rounded
   and a soft shadow lifts it off the page. The purple header bar below is a
   sticky child whose rounded top corners are clipped to the card's, so
   `overflow` cannot be `visible` here -- the header would spill past the corner.
   `overflow-y: auto` already clips, and the body scrolls inside the card.

   The horizontal padding that used to live here (`--classnav-container`) is
   gone: the header bar now spans edge to edge, and the body gets its own
   smaller, screen-independent padding so the card reads as a compact widget
   rather than a full-width page band. The toolbar's old negative-margin bleed
   (which cancelled that container padding) is gone with it. */
.classnav__frame {
	display: block;
	color: var(--classnav-text);
	font-size: 0.9625em;
	line-height: 1.6;
	background: var(--classnav-surface-raised);
	border: solid 1px var(--classnav-grey-border);
	border-radius: 0.9rem;
	box-shadow: 0 0.25rem 1rem var(--classnav-black-10);
	margin: 0 auto;
	margin-bottom: 1rem;
	max-height: 500px;
	overflow-y: auto;
	scroll-behavior: auto;
}

/* --------------------------------------------------------------------------
   Visually hidden utility
   --------------------------------------------------------------------------
   Replaces two near-duplicate implementations that both lived in this file:
   `.sr-only` (used once, in the fragment) and `.access-hidden` (referenced
   nowhere). Merged into one, taking the `clip-path` from the latter since
   `clip` alone is deprecated.

   Also carries `.classnav__status`, the live status region added in Task 4.
   That element has no rules of its own yet -- it is a hook for Task 8, which
   decides whether the redesign shows status on screen. Keeping it out of flow
   is what makes adding it pixel-neutral.
   -------------------------------------------------------------------------- */

.classnav__sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* --------------------------------------------------------------------------
   Toolbar
   -------------------------------------------------------------------------- */

/* Skin A (Task 8): a solid purple header bar. It is sticky at the top of the
   scrolling card, spans the card edge to edge, and its top corners are rounded
   to match the card so the band tucks cleanly under the border. The brand sits
   left, the actions sit right. */
.classnav__toolbar {
	position: sticky;
	top: 0;
	z-index: 3;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
	padding: 0.65rem 0.9rem;
	color: var(--classnav-white);
	background: var(--classnav-purple);
	border-top-left-radius: 0.9rem;
	border-top-right-radius: 0.9rem;
	flex-wrap: nowrap;
}

/* Left side of the header: the chat glyph, the title, and the BETA pill. */
.classnav__toolbar-brand {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	min-width: 0;
}

.classnav__toolbar-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
	width: 1.75rem;
	height: 1.75rem;
	color: var(--classnav-white);
	background: var(--classnav-white-25);
	border-radius: 0.45rem;
}

.classnav__toolbar-icon .classnav__icon {
	width: 1.1rem;
	height: 1.1rem;
	color: var(--classnav-white);
	fill: none;
	pointer-events: none;
}

.classnav__toolbar-title {
	font-size: 1rem;
	/* Was 600, which Lato has no face for -- it measured as synthesized
	   Arial-BoldMT. Only 400 and 700 are available. */
	font-weight: var(--classnav-weight-bold);
	color: var(--classnav-white);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* The "BETA" pill: a small light pill with purple text, immediately after the
   title. Letter-spaced small caps, so it reads as a label not a word. */
.classnav__beta {
	flex: 0 0 auto;
	padding: 0.1rem 0.45rem;
	font-size: 0.6rem;
	font-weight: var(--classnav-weight-bold);
	line-height: 1.4;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--classnav-purple);
	background: var(--classnav-white);
	border-radius: 999px;
}

/* Right side of the header: Expand (ghost) and New chat (solid white). */
.classnav__toolbar-actions {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	flex: 0 0 auto;
}

/* Compound selector retained to match the original
   `.chat__toolbar .chat__toolbar__btn` specificity (0,0,2,0), which is what
   lets these declarations beat Castor's `button, .button` rules. */
.classnav__toolbar .classnav__toolbar-btn {
	-webkit-appearance: none;
	appearance: none;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.3rem;
	white-space: nowrap;
	cursor: pointer;
	padding: 0.4rem 0.8rem;
	font: inherit;
	font-size: 0.85rem;
	font-weight: var(--classnav-weight-bold);
	line-height: 1;
	border-radius: 999px;
	border: 1px solid transparent;
	background: transparent;
	color: var(--classnav-white);
}

.classnav__toolbar-btn .classnav__icon {
	width: 1.05em;
	height: 1.05em;
	color: currentColor;
	fill: none;
	pointer-events: none;
}

/* Expand: a ghost/outlined button that reads against the purple band. Disabled
   until task 10 wires the modal, so it is dimmed and non-interactive but keeps
   its accessible name. */
.classnav__toolbar .classnav__toolbar-btn--expand {
	border-color: var(--classnav-white-25);
	background: transparent;
	color: var(--classnav-white);
}

.classnav__toolbar-btn--expand:hover:not(:disabled),
.classnav__toolbar-btn--expand:focus-visible {
	background: var(--classnav-white-25);
}

.classnav__toolbar-btn--expand:disabled {
	opacity: 0.6;
	cursor: default;
}

/* New chat: a solid white button with purple text, the primary header action. */
.classnav__toolbar .classnav__toolbar-btn--new {
	background: var(--classnav-white);
	color: var(--classnav-purple);
	border-color: var(--classnav-white);
}

.classnav__toolbar-btn--new:hover,
.classnav__toolbar-btn--new:focus-visible {
	background: var(--classnav-grey-lighter);
	color: var(--classnav-purple);
}

/* --------------------------------------------------------------------------
   Body padding (Skin A)
   --------------------------------------------------------------------------
   The frame no longer carries horizontal padding -- the header bar spans the
   card edge to edge. The scrolling body (the transcript region and the
   composer) gets its own compact, screen-independent padding instead, so the
   content clears the card edge without the header having to.
   -------------------------------------------------------------------------- */

.classnav__transcript-region {
	padding: 1.25rem 1.25rem 0;
}

.classnav__composer {
	padding: 0 1.25rem;
}

/* --------------------------------------------------------------------------
   Intro + suggestions (Skin A) -- the EMPTY conversation state
   --------------------------------------------------------------------------
   Shown when there is no conversation yet. classnavigator.js sets `hidden` on
   `.classnav__intro` the moment the transcript gains a message, so the
   populated conversation state is literally this block hidden with the
   transcript list full. The suggestions are real <button>s the script wires to
   the composer; styling them as cards is cosmetic, the semantics are a button.
   -------------------------------------------------------------------------- */

.classnav__intro {
	margin-bottom: 1rem;
}

.classnav__intro-heading {
	margin: 0 0 0.4rem;
	color: var(--classnav-purple);
	font-size: 1.15rem;
	font-weight: var(--classnav-weight-bold);
	line-height: 1.3;
}

.classnav__intro-lead {
	margin: 0 0 1rem;
	color: var(--classnav-text-muted);
	font-size: 0.9rem;
	line-height: 1.5;
}

/* 2x2 grid on anything wider than a phone; a single column when the card is
   narrow (task 12 owns the full mobile pass, this is the minimum that keeps the
   grid from overflowing at 320px). */
.classnav__suggestions {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 0.6rem;
}

@media (max-width: 29.99em) {
	.classnav__suggestions {
		grid-template-columns: 1fr;
	}
}

/* A suggestion card: a lavender tile with purple text, left-aligned, that reads
   as tappable. It is a real <button>; the reset already stripped Castor's
   uppercase and centring, so only the skin is declared here. */
.classnav__suggestion {
	-webkit-appearance: none;
	appearance: none;
	display: block;
	width: 100%;
	margin: 0;
	padding: 0.75rem 0.9rem;
	color: var(--classnav-purple);
	font: inherit;
	font-size: 0.85rem;
	font-weight: var(--classnav-weight-bold);
	line-height: 1.35;
	text-align: left;
	/* A solid light-lavender fill rather than a translucent one. Purple text on
	   $purple-primary-lightest measures ~5.4:1, comfortably past WCAG AA; a
	   translucent fill would read as its full saturation to a contrast tool and
	   would both measure and (on a non-white host) render worse. Hover keeps the
	   same fill -- so the measured contrast holds in every state -- and signals
	   itself with a border and a lift instead of darkening the background under
	   the text. */
	background: var(--classnav-purple-lightest);
	border: 1px solid transparent;
	border-radius: 0.6rem;
	cursor: pointer;
	transition: border-color var(--classnav-transition), box-shadow var(--classnav-transition);
}

.classnav__suggestion:hover,
.classnav__suggestion:focus-visible {
	border-color: var(--classnav-purple);
	box-shadow: 0 0.15rem 0.5rem var(--classnav-black-10);
}

@media (forced-colors: active) {
	/* The lavender fill is forced to a system colour; give the tile an edge so
	   it stays visibly a control. */
	.classnav__suggestion {
		border: 1px solid ButtonText;
	}
}

/* --------------------------------------------------------------------------
   Transcript
   -------------------------------------------------------------------------- */

.classnav__transcript {
	display: flex;
	flex-direction: column;
	align-items: flex-start;

	/* Pin removed in Task 3. The inbound reset now zeroes list margin and
	   padding, so Castor's 45px indent and 17px/20px margins no longer reach
	   in and the transcript can sit flush inside the frame's own padding.
	   The old declaration asked for `margin: 7em 0 12em`, vestigial from the
	   standalone full-page layout; those values are deliberately NOT restored.
	   Task 8 sets the final spacing as part of the redesign. */
	margin: 0;
	padding: 0;
}

/* Offset the sticky toolbar when scrolling a bubble into view. */
.classnav__transcript li {
	scroll-margin-top: var(--classnav-toolbar-h, 70px);
}

/* Skin A: the card is white now, so bot and system bubbles keep their white
   fill but gain a hairline border to read as distinct surfaces on it rather
   than disappearing. White was kept deliberately: the muted feedback label and
   links inside a bot bubble were measured at AA against white, and a greyer
   fill would drop them below 4.5:1. The user bubble (below) is purple on the
   right and needs no border. */
.classnav__msg--system {
	display: inline-block;
	word-wrap: break-word;
	word-break: break-word;
	padding: 1.25em 1.5em;
	color: var(--classnav-text);
	background: var(--classnav-surface-raised);
	border: 1px solid var(--classnav-grey-light);
	border-radius: 1em;
	margin-bottom: 1em;
	max-width: 51.5em;
}

.classnav__transcript .classnav__msg--bot,
.classnav__transcript .classnav__msg--user {
	display: inline-block;
	word-wrap: break-word;
	word-break: break-word;
	padding: 1.25em 1.5em 1em 1.5em;
	color: var(--classnav-text);
	background: var(--classnav-surface-raised);
	border: 1px solid var(--classnav-grey-light);
	border-radius: 1em;
	margin-bottom: 1em;
	max-width: 51.5em;
	white-space: pre-wrap;
}

.classnav__transcript .classnav__msg--bot {
	text-align: left;
}

.classnav__transcript .classnav__msg--user {
	align-self: flex-end;
	text-align: right;
	padding: 0.5em 1.5em;
	border-radius: 2em;
	color: var(--classnav-white);
	background: var(--classnav-purple);
	/* Match the border to the purple fill so the hairline the shared rule adds
	   for the white bot bubbles does not read as a grey ring here. */
	border-color: var(--classnav-purple);
}

/* Longer responses that follow a user bubble get squarer corners. */
.classnav__transcript .classnav__msg--user + .classnav__msg--bot {
	padding: 1.25em 1.5em;
	border-radius: 1em;
}

/* --------------------------------------------------------------------------
   Feedback controls (built in JS by createFeedbackDiv)
   -------------------------------------------------------------------------- */

.classnav__feedback {
	display: flex;
	margin-top: 20px;
	align-items: center;
}

/* "Rate Answer" label. */
.classnav__feedback p {
	display: block;
	margin-top: 0;
	margin-bottom: 0;
	color: var(--classnav-text-subtle);
	font-size: 0.7em;
	white-space: nowrap;
}

.classnav__feedback label {
	color: var(--classnav-text-muted);
}

.classnav__feedback-buttons {
	display: flex;
	align-items: center;
	gap: 13px;
	padding-bottom: 1em;
}

.classnav__rating {
	display: flex;
	position: relative;
	flex-direction: column;
	align-items: center;
	justify-content: left;
}

.classnav__rating-buttons {
	display: flex;
	position: relative;
	gap: 5px;
}

.classnav__thumb {
	display: flex;
	background: transparent;
	border: solid transparent;
	border-radius: 50%;
	width: 2.7em;
	height: 2.7em;
	/* Explicit because Castor's `button, .button { padding: .875rem .75rem }`
	   would otherwise apply, and with border-box that padding eats into the
	   fixed 2.7em box and squeezes the icon. */
	padding: 0;
	align-items: center;
	justify-content: left;
}

.classnav__thumb svg {
	display: flex;
	width: 24px;
	height: 24px;
	color: var(--classnav-text);
	overflow: visible;
	fill: none;
	justify-content: center;
	align-items: center;
}

.classnav__thumb:hover,
.classnav__thumb:focus {
	background: var(--classnav-black-10);
}

.classnav__thumb.is-active {
	background: var(--classnav-purple);
}

.classnav__thumb.is-active svg {
	color: var(--classnav-white);
}

.classnav__comment {
	display: flex;
	position: relative;
	width: 100%;
	align-items: end;
}

.classnav__comment-form {
	display: none;
	width: 100%;
	align-items: center;
	justify-content: left;
}

.classnav__comment-form textarea {
	resize: none;
	height: 3.2em;
	width: 100%;
	padding: 0.8em 9em 0.5em 1em;
	margin-left: 0.75em;
	color: var(--classnav-text);
	font: inherit;
	font-size: 0.8em;
	background: var(--classnav-surface-raised);
	border: 1px solid var(--classnav-grey-darker);
	border-radius: 9em;
	overflow: hidden;
}

.classnav__comment-form textarea::placeholder {
	opacity: 1;
	color: var(--classnav-text-muted);
}

.classnav__comment-submit {
	background: var(--classnav-purple-light);
	position: absolute;
	right: 0;
	height: 1em;
	padding: 1.1em 1.2em 2.1em 1.2em;
	color: var(--classnav-white);
	font-size: 0.75em;
	font-weight: var(--classnav-weight-bold);
	white-space: nowrap;
	border: 1px solid transparent;
	border-radius: 9em;
}

.classnav__comment-submit:hover,
.classnav__comment-submit:active {
	color: var(--classnav-white);
	background: var(--classnav-purple);
}

/* --------------------------------------------------------------------------
   Composer
   -------------------------------------------------------------------------- */

/* Skin A: the composer sits on the white card, so the pill is the input itself
   (bordered below) rather than a raised panel. This wrapper is now just
   positioning context for the typing dots and the send button. The horizontal
   padding is declared in the Body padding block above. */
.classnav__composer {
	position: relative;
	bottom: 0;
	top: auto;
	width: 100%;
	background: transparent;
	border: none;
	margin-bottom: 1em;
}

.classnav__dots {
	display: flex;
	flex-flow: row nowrap;
	justify-content: center;
	align-items: center;
	position: fixed;
	top: -1.5rem;
	left: 50%;
	transform: translateX(-50%);
	width: 100%;
	margin: 0;
	text-align: center;
	z-index: 2;
}

.classnav__dot {
	background-color: var(--classnav-purple-light);
	display: inline-block;
	width: 12px;
	height: 12px;
	border-radius: 50%;
	margin-right: 3px;
	animation: classnav-dots 1.3s linear infinite;
}

.classnav__dot:nth-child(2) {
	animation-delay: -1.1s;
}

.classnav__dot:nth-child(3) {
	animation-delay: -0.9s;
}

/* Keyframe names are global, so they carry the widget prefix too. */
@keyframes classnav-dots {
	0%,
	60%,
	100% {
		transform: initial;
	}
	30% {
		transform: translateY(-15px);
	}
}

.classnav__form {
	position: relative;
	color: var(--classnav-text);
	height: 4em;
	margin-top: 1em;
}

/* Height has to be set on both the form and the textarea.
   Compound selector retained because the original rule was id-based (0,1,0,1);
   these declarations need to beat Castor's form styles. */
.classnav__form .classnav__input {
	overflow: hidden;
	resize: none;
	width: 100%;
	height: 4em;
	line-height: 2;
	color: var(--classnav-text);
	padding: 1.25em 1.5em;
	/* Extra right padding keeps text clear of the send button on mobile. */
	padding-right: 4em;
	background: var(--classnav-surface-raised);
	border-radius: 50vh;
	/* Skin A: a visible pill edge, since the input sits directly on the white
	   card now rather than inside a raised panel. */
	border: 1px solid var(--classnav-grey-darker);
	font: inherit;
}

.classnav__form .classnav__input:focus {
	border-color: var(--classnav-purple-light);
	outline: none;
}

.classnav__form .classnav__input:focus-visible {
	border-color: var(--classnav-purple-light);
}

.classnav__form .classnav__input::placeholder {
	opacity: 1;
	/* Deliberately the full text colour, not a muted grey: this placeholder is
	   the only prompt the composer shows. It no longer carries status text --
	   Task 4 moved "Thinking..." to the persistent `.classnav__status` live
	   region, since a placeholder is not reliably announced and vanishes as soon
	   as the field has content. */
	color: var(--classnav-text);
}

/* Readonly while a query is in flight. Was an inline
   `inputEl.style.background = "#e5e5e5"` written by disable_textbox(), paired
   with `= "#fff"` to undo it; presentation belongs here, and a class makes the
   state inspectable and themeable by the later skins.

   Same specificity (0,0,2,0) as the base rule above and declared after it, so
   it wins without !important. Removing the class restores the base background,
   which is why there is no matching "enabled" rule.

   The hex moved onto the palette in the process: #e5e5e5 -> #e8e8e8,
   UST's $grey-primary-light. */
.classnav__form .classnav__input--busy {
	background: var(--classnav-grey-light);
}

.classnav__form .classnav__submit {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	right: 0.5em;
	width: 4em;
	height: 4em;
	margin: 0 auto;
	padding: 0;
	color: var(--classnav-white);
	font-size: 0.825em;
	font-weight: var(--classnav-weight-bold);
	letter-spacing: 1px;
	border: none;
	border-radius: 9em;
	background-color: var(--classnav-purple-light);
	/* Task 6: the Castor sprite is gone. This used to paint a glyph by offsetting
	   into castor-2-icons-white.svg
	   (`background-position: -2.5rem -27rem; background-size: 600%`), a hardcoded
	   offset that made the widget depend on Castor shipping that exact sprite at
	   that exact layout. The icon is now an inline <svg> child (.classnav__icon)
	   that inherits `currentColor`, so the control is self-contained and themeable.

	   The old markup was an <input type="submit"> whose "Send" value was pushed
	   off-screen with `text-indent: -9em`. An <input> cannot hold an <svg>, so it
	   is now a <button type="submit">; the accessible name moved to aria-label and
	   the text-indent hack is gone. Flex-centres the icon in the round button. */
	display: flex;
	align-items: center;
	justify-content: center;
}

.classnav__form .classnav__submit:hover {
	cursor: pointer;
	background-color: var(--classnav-purple);
}

.classnav__form .classnav__submit .classnav__icon {
	/* Inherits the button's white `color`, drawn via `stroke="currentColor"` on
	   the path. Sized in em so it tracks the composer's font-size. */
	width: 1.6em;
	height: 1.6em;
	color: var(--classnav-white);
	fill: none;
	pointer-events: none;
}

/* --------------------------------------------------------------------------
   Forced colors / high contrast (Task 6)
   --------------------------------------------------------------------------
   In forced-colors mode the OS replaces the palette with a limited system set,
   so the widget's purple button backgrounds collapse to the system `Canvas`
   colour and a `currentColor` icon would otherwise inherit whatever the button
   text colour resolves to. These rules keep every icon-only control legible and
   keep focus and pressed state distinguishable WITHOUT colour as the only cue.

   Every selector stays scoped under `.classnav` -- the scope lint applies inside
   @media just as it does outside it. No blanket resets; each icon is pinned to a
   system colour keyword so it tracks the user's chosen high-contrast theme. */
@media (forced-colors: active) {
	/* Let the icons take their colour from the system palette rather than the
	   now-meaningless purple. ButtonText pairs with the ButtonFace the button
	   background is forced to. */
	.classnav__form .classnav__submit .classnav__icon,
	.classnav__thumb svg {
		forced-color-adjust: auto;
		color: ButtonText;
	}

	/* The send button's background is forced to a system colour; give it a
	   border so its edge is visible against the composer. */
	.classnav__form .classnav__submit {
		border: 1px solid ButtonText;
	}

	/* Pressed state cannot rely on the purple fill in forced colors, and the
	   `.is-active` background is ignored. Mark the active thumb with a border and
	   flip the icon to the Highlight colour so pressed is visible by shape and
	   by the system accent, not by colour alone. aria-pressed carries the state
	   to assistive technology regardless. */
	.classnav__thumb.is-active {
		border-color: Highlight;
	}

	.classnav__thumb.is-active svg {
		color: Highlight;
	}

	/* Keep the focus ring visible: a system-palette outline instead of the
	   purple token, which forced-colors would otherwise discard. */
	.classnav :focus-visible {
		outline-color: Highlight;
	}
}

/* Compound selector preserves the original class+attribute specificity
   (0,0,2,0). */
.classnav__input.classnav__input--error {
	position: relative;
	animation: classnav-shake 0.1s linear;
	animation-iteration-count: 3;
}

@keyframes classnav-shake {
	0% {
		left: -5px;
	}
	100% {
		right: -5px;
	}
}

/* --------------------------------------------------------------------------
   Widget footer
   --------------------------------------------------------------------------
   Was a bare `footer` element selector, which matched the university footer in
   footer.jspf as well. The markup now carries an explicit class.
   -------------------------------------------------------------------------- */

.classnav__footer {
	bottom: 0;
	display: flex;
	flex-direction: column;
	justify-content: center;
}

.classnav__footer a {
	text-decoration: underline;
	color: var(--classnav-text-muted);
}

/* Pin removed in Task 3: the reset zeroes `p` margins, so the intended values
   now apply instead of Castor's `.main-body p { margin: .375rem 0 1rem 0 }`. */
.classnav__disclaimer {
	display: block;
	margin-top: 1em;
	margin-bottom: 0;
	font-size: 0.8em;
	color: var(--classnav-text-muted);
	text-align: center;
}

/* --------------------------------------------------------------------------
   Rendered markdown (Task 5)
   --------------------------------------------------------------------------
   Model output is turned into a token tree by the vendored tokenizer and walked
   into DOM nodes by classnavigator.js (renderBlocks / renderInline). None of it
   is ever an HTML string -- that is the XSS hole being closed. These rules style
   the nodes that walk produces; every emitted element carries a `classnav__md-*`
   class so the scope lint stays satisfied and nothing leaks into Castor.

   Spacing stays in em so it tracks the bubble's font-size and respects user text
   scaling (WCAG 1.4.4). The inbound reset above zeroed list markers and margins
   for the transcript itself; markdown lists want them back, so they are
   re-stated here on the scoped classes rather than globally.
   -------------------------------------------------------------------------- */

.classnav__md-heading {
	margin: 0.8em 0 0.3em;
	font-weight: var(--classnav-weight-bold);
	line-height: 1.3;
}

/* A heading at the very top of a bubble should not add leading space. */
.classnav__md-heading:first-child {
	margin-top: 0;
}

.classnav__md-p {
	margin: 0 0 0.75em;
}

.classnav__md-p:last-child {
	margin-bottom: 0;
}

/* Markers and indent, re-asserted after the inbound reset stripped them from
   `.classnav ul, ol`. */
.classnav__md-list {
	margin: 0 0 0.75em;
	padding-left: 1.5em;
}

.classnav__md-list ul,
.classnav__md-list ol {
	/* Nested lists: list-style is set per level by the browser default once the
	   marker type is restored on the parent. Keep nested spacing tight. */
	margin: 0.2em 0 0.2em;
	padding-left: 1.5em;
}

ul.classnav__md-list {
	list-style: disc outside;
}

ol.classnav__md-list {
	list-style: decimal outside;
}

.classnav__md-list li {
	margin: 0.15em 0;
}

/* Inline code span. */
.classnav__md-code {
	padding: 0.1em 0.35em;
	font-family: ui-monospace, "SF Mono", "Cascadia Mono", "Consolas",
		"Liberation Mono", monospace;
	font-size: 0.9em;
	background: var(--classnav-grey-lighter);
	border: 1px solid var(--classnav-grey-dark);
	border-radius: 0.3em;
}

/* Fenced code block. */
.classnav__md-pre {
	margin: 0 0 0.75em;
	padding: 0.75em 1em;
	overflow-x: auto;
	color: var(--classnav-text);
	background: var(--classnav-grey-lighter);
	border: 1px solid var(--classnav-grey-dark);
	border-radius: 0.5em;
}

.classnav__md-pre code {
	font-family: ui-monospace, "SF Mono", "Cascadia Mono", "Consolas",
		"Liberation Mono", monospace;
	font-size: 0.9em;
	white-space: pre;
}

.classnav__md-quote {
	margin: 0 0 0.75em;
	padding: 0.25em 0 0.25em 1em;
	color: var(--classnav-text-muted);
	border-left: 0.25em solid var(--classnav-purple-lighter);
}

.classnav__md-quote > :last-child {
	margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   Markdown tables
   --------------------------------------------------------------------------
   The renderer wraps every table in `.classnav__md-tablewrap`. The wrapper is
   the scroll container, so at 320px a wide table scrolls horizontally inside
   the bubble instead of forcing the whole transcript -- and the composer, the
   toolbar, the page -- to scroll sideways (Task 12 cares about this; the seam
   is set here). The wrapper is focusable and labelled as a region so a
   keyboard-only user can reach and scroll it.

   Header cells get `scope="col"` from the renderer; these rules give them the
   UST purple header the course tables are expected to use.
   -------------------------------------------------------------------------- */

.classnav__md-tablewrap {
	max-width: 100%;
	margin: 0 0 0.75em;
	overflow-x: auto;
	/* Momentum scrolling on iOS, and a visible focus affordance handled by the
	   shared :focus-visible rule above. */
	-webkit-overflow-scrolling: touch;
}

.classnav__md-table {
	border-collapse: collapse;
	/* Natural width up to the wrapper; wider tables overflow and the wrapper
	   scrolls. min-width keeps columns legible rather than crushing them at
	   320px. */
	width: auto;
	min-width: 100%;
	font-size: 0.95em;
}

.classnav__md-table th,
.classnav__md-table td {
	padding: 0.4em 0.75em;
	text-align: left;
	border: 1px solid var(--classnav-grey-dark);
	/* Keep cells from collapsing to a single wrapped character at 320px. */
	white-space: nowrap;
}

.classnav__md-table th {
	color: var(--classnav-white);
	font-weight: var(--classnav-weight-bold);
	background: var(--classnav-purple);
}

.classnav__md-table tbody tr:nth-of-type(even) {
	background: var(--classnav-grey-lightest);
}

/* --------------------------------------------------------------------------
   Course-data seam (Task 5)
   --------------------------------------------------------------------------
   A registered `classnavigator.renderers.course` renders structured course
   data into this region, appended AFTER the prose in the same bubble -- not a
   payload-shape switch, an additive layer (design.md). The structured
   component itself is deferred, so this only reserves the hook and a little
   separation from the prose above it.
   -------------------------------------------------------------------------- */

.classnav__course-data {
	margin-top: 0.75em;
}

/* --------------------------------------------------------------------------
   Bubble width under reflow
   --------------------------------------------------------------------------
   A bot bubble is `display: inline-block` and sizes to its widest unbreakable
   content. A wide markdown table (its cells are `white-space: nowrap`) would
   therefore stretch the bubble past the frame at 320px, and the table wrapper's
   own `overflow-x: auto` cannot engage because its containing block grew with
   it. Capping the bubble at the available width gives the wrapper a definite
   box to scroll inside.

   `min(51.5em, 100%)` is deliberately render-neutral on desktop: 100% of the
   frame content box is far wider than 51.5em there, so the cap stays 51.5em --
   exactly today's value. It only tightens once the frame is narrower than
   51.5em, i.e. on small screens, which is where the reflow matters. Same
   specificity (0,0,2,0) as the base rule and declared later, so it wins without
   !important. Final small-screen layout is a Task 12 concern; this is the
   minimum that keeps a table from breaking 320px reflow today.
   -------------------------------------------------------------------------- */
.classnav__transcript .classnav__msg--bot {
	max-width: min(51.5em, 100%);
}

/* --------------------------------------------------------------------------
   Print (Task 7)
   --------------------------------------------------------------------------
   Castor's _print-layout.scss supplied the widget's only print behaviour, via
   `.faq-body { display: block !important }` -- it force-expanded the collapsed
   accordion so a results page would print the chat rather than an empty
   collapsed stub. Removing the `.faq` wrapper removes that rule's reach, so the
   widget has to own the same behaviour now.

   This reveals the collapse panel for print regardless of its live collapsed
   state: a reader printing a results page should see the conversation, not a
   closed disclosure. `.classnav__panel[hidden]` is (0,0,2,0), which outranks
   the user-agent `[hidden] { display: none }` rule at (0,0,1,0), so it wins
   without needing `!important` -- unlike the Castor rule it replaces, which
   needed !important only because it was fighting castor.js's inline height. The
   disclosure button itself is a screen affordance with nothing to toggle on
   paper, so it is hidden.

   Scoped under `.classnav`, like every other selector in this file and inside
   @media exactly as outside it (scope.spec.js enforces this).
   -------------------------------------------------------------------------- */
@media print {
	.classnav .classnav__panel[hidden] {
		display: block;
	}

	.classnav .classnav__disclosure {
		display: none;
	}
}

/* ==========================================================================
   SKIN B -- the hero variant (Task 9)
   --------------------------------------------------------------------------
   Mockup 2: the whole card is a purple gradient, with an eyebrow label and a
   large heading instead of a solid header band, and the suggestions reflow as
   inline chips rather than a 2x2 grid of cards.

   THE CONSTRAINT THAT SHAPED THIS BLOCK: it is a class SWAP, not a rewrite.
   Skin A ships on `.classnav`; Skin B is reached by adding `.classnav--hero`
   to that same root, exactly as `.classnav--modal` is planned for task 10. The
   markup, the ids, the JS, and every component class are identical between the
   two skins -- the only difference on the page is one token in the root's class
   list. So every rule here is a MODIFIER keyed on the `.classnav--hero`
   ancestor; none of it restates a component, it only overrides the handful of
   Skin A declarations the hero look changes.

   Because the override is scoped under `.classnav--hero` (0,0,2,0 where it
   pairs the modifier with a component class), it beats the Skin A base rules
   (0,0,1,0) on specificity and never touches a `.classnav` root that lacks the
   modifier. Skin A is completely unaffected -- the render-equivalence reference
   is measured against the non-hero no-results page and does not move.

   CONTRAST IS THE HARD CASE HERE. Skin A put light text on a solid purple band
   and dark text on a white card -- both easy to measure, because the background
   is a single flat colour. The hero's text sits on a GRADIENT, so a contrast
   tool that samples one background colour is not enough: the ratio has to hold
   at BOTH ends of the gradient. skin-b.spec.js measures every text pair against
   the darkest AND the lightest stop and requires both to pass. The stops are
   chosen so the lighter end is still dark enough for white body text:
   `--classnav-purple-dark` (#410a5e) at the bright corner, not one of the
   genuinely light purples.
   ========================================================================== */

/* The card itself: a diagonal purple gradient instead of the white Skin A card.
   Dark in the top-left, lighter toward the bottom-right, matching the mockup.
   The lighter stop is deliberately `--classnav-purple-dark`, not a pale purple,
   so white text keeps a large contrast margin across the whole fill (see the
   spec's two-stop check). The soft shadow and rounded corners carry over. */
.classnav--hero .classnav__frame {
	color: var(--classnav-white);
	background: linear-gradient(
		135deg,
		var(--classnav-purple-darkest) 0%,
		var(--classnav-purple-darker) 42%,
		var(--classnav-purple-dark) 100%
	);
	border-color: var(--classnav-purple-darker);
}

/* No header BAND. The toolbar element stays (it holds the Expand control the
   mockup shows top-right, and keeps its role="toolbar" and label), but it loses
   the solid purple fill, the rounded band corners and the sticky behaviour --
   it becomes a transparent row floating over the gradient. */
.classnav--hero .classnav__toolbar {
	position: static;
	background: transparent;
	border-top-left-radius: 0;
	border-top-right-radius: 0;
	padding: 1.25rem 1.5rem 0;
	align-items: flex-start;
}

/* The brand collapses to an EYEBROW: small, uppercase, letter-spaced label text
   ("CLASSNAVIGATOR · BETA" in the mockup). The chat glyph is dropped -- the
   eyebrow is text only. */
.classnav--hero .classnav__toolbar-icon {
	display: none;
}

.classnav--hero .classnav__toolbar-title {
	font-size: 0.72rem;
	font-weight: var(--classnav-weight-bold);
	letter-spacing: 0.12em;
	text-transform: uppercase;
	/* Lavender rather than pure white so it reads as a label, not a second
	   heading. Measured against both gradient stops in the spec. */
	color: var(--classnav-purple-lightest);
}

/* The BETA pill becomes a dotted-separator word in the eyebrow rather than a
   filled pill: "· BETA". A ::before carries the middot so the markup is
   unchanged. */
.classnav--hero .classnav__beta {
	padding: 0;
	background: transparent;
	border-radius: 0;
	color: var(--classnav-purple-lightest);
	letter-spacing: 0.12em;
}

.classnav--hero .classnav__beta::before {
	content: "·\00a0";
}

/* New chat is not part of the hero chrome -- the mockup's only top-right control
   is Expand. Hidden here; a hero conversation is reset from the modal (task 10)
   or by reload. Kept in the DOM (not removed) so the class stays a pure swap and
   the shared markup is untouched. */
.classnav--hero .classnav__toolbar-btn--new {
	display: none;
}

/* Expand is the one header action the hero keeps, top-right, as a ghost pill
   that reads against the gradient. It is still disabled until task 10; the
   dimming from the base rule carries over. */
.classnav--hero .classnav__toolbar-actions {
	align-items: flex-start;
}

.classnav--hero .classnav__toolbar .classnav__toolbar-btn--expand {
	border-color: var(--classnav-white-25);
	color: var(--classnav-white);
}

/* --------------------------------------------------------------------------
   Hero intro: eyebrow is in the toolbar, so the intro leads with the LARGE
   heading and a muted subline, then the composer, then inline chips.
   -------------------------------------------------------------------------- */

/* The body padding tightens its top gap because the eyebrow already sits above
   with its own padding. */
.classnav--hero .classnav__transcript-region {
	padding: 0.75rem 1.5rem 0;
}

.classnav--hero .classnav__composer {
	padding: 0 1.5rem;
}

/* The big hero heading: white, large, tight leading. This is the mockup's
   "Describe the class you want. We'll find open sections." */
.classnav--hero .classnav__intro-heading {
	color: var(--classnav-white);
	font-size: 1.6rem;
	line-height: 1.2;
	margin-bottom: 0.6rem;
}

/* The subline under the heading: a lighter lavender, not the Skin A grey (which
   would vanish on the gradient). Measured against both stops. */
.classnav--hero .classnav__intro-lead {
	color: var(--classnav-purple-lightest);
	font-size: 0.95rem;
}

/* Chips, not cards. The suggestions stop being a 2x2 grid and reflow inline as
   pill-shaped chips that wrap, matching the mockup's single row that spills to a
   second line when it has to. */
.classnav--hero .classnav__suggestions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem;
}

/* A chip: translucent-looking but painted as a SOLID colour on purpose. A real
   rgba() fill over the gradient would sample as its own low-contrast value to a
   tool and would shift with the gradient underneath it; a solid lightish-purple
   lets the chip's own white text be measured against one known colour. The chip
   text is white, sized small, pill-radius, auto width (shrink to content). */
.classnav--hero .classnav__suggestion {
	display: inline-block;
	width: auto;
	padding: 0.5rem 0.95rem;
	color: var(--classnav-white);
	font-size: 0.8rem;
	/* Solid, so the white chip label measures against one colour. `--classnav-
	   purple-light` (#8348ad) gives white text ~4.6:1, past AA for this size. */
	background: var(--classnav-purple-light);
	border: 1px solid var(--classnav-white-25);
	border-radius: 999px;
}

.classnav--hero .classnav__suggestion:hover,
.classnav--hero .classnav__suggestion:focus-visible {
	/* The fill is deliberately UNCHANGED on hover. Lightening it to
	   `--classnav-purple-lighter` would drop the white chip label to ~3.3:1 and
	   fail AA, so hover is signalled by the border (and the shared focus ring)
	   alone, exactly as the Skin A cards signal with a border rather than a
	   darker fill. This keeps the measured 6:1 holding in every state. */
	background: var(--classnav-purple-light);
	border-color: var(--classnav-white);
	box-shadow: none;
}

@media (forced-colors: active) {
	/* The solid fill is forced to a system colour; the base Skin A forced-colors
	   rule already gives the chip a ButtonText border, so nothing hero-specific
	   is needed here -- the border survives and the shape stays legible. */
	.classnav--hero .classnav__suggestion {
		border: 1px solid ButtonText;
	}
}

/* --------------------------------------------------------------------------
   Hero composer + disclaimer on the gradient
   -------------------------------------------------------------------------- */

/* The pill input stays white (the mockup's composer is a white pill on the
   gradient), so its placeholder and typed text keep the dark colour they have
   in Skin A -- no override needed there. Only the focus ring needs to read
   against the gradient rather than against a white card. */
.classnav--hero .classnav__form .classnav__input {
	border-color: transparent;
}

.classnav--hero .classnav__form .classnav__input:focus,
.classnav--hero .classnav__form .classnav__input:focus-visible {
	border-color: var(--classnav-white);
}

/* The disclaimer ("Terms of use" in the mockup) is bottom-left and must read on
   the gradient: lavender text, left-aligned rather than Skin A's centred grey. */
.classnav--hero .classnav__disclaimer {
	color: var(--classnav-purple-lightest);
	text-align: left;
}

.classnav--hero .classnav__footer {
	align-items: flex-start;
}

.classnav--hero .classnav__footer a {
	color: var(--classnav-white);
}

/* --------------------------------------------------------------------------
   Hero conversation bubbles
   --------------------------------------------------------------------------
   Neither mockup shows the hero in its populated state, same gap Skin A had.
   The bubbles have to stay legible on the gradient: the white Skin A bot/system
   bubbles already carry dark text, so they read fine sitting on the purple --
   they only need their hairline border dropped to a soft translucent one so they
   do not show a grey ring against the gradient. The user bubble was purple on
   white in Skin A; on the gradient a purple bubble would disappear, so the hero
   user bubble is a solid light purple with white text, still right-aligned.
   -------------------------------------------------------------------------- */
.classnav--hero .classnav__transcript .classnav__msg--bot,
.classnav--hero .classnav__msg--system {
	border-color: var(--classnav-white-25);
}

.classnav--hero .classnav__transcript .classnav__msg--user {
	color: var(--classnav-white);
	background: var(--classnav-purple-light);
	border-color: var(--classnav-purple-light);
}
