﻿/* wwwroot/css/vx-formulalens.css
   FormulaLens.

   The upload screen inherits the site's light theme. The result surface goes
   dark - deliberately: it is the only screen on the site that switches, so it
   reads as the engine coming on. The header inside it keeps the site gradient
   so the page still belongs here.

   Dark is scoped, not global: the results container carries
   data-bs-theme="dark", so Bootstrap's own badges, buttons and tables follow
   without being overridden one by one. Everything below only adds the brand
   layer on top.

   Colours are the site's existing palette - #34d399 is the LIVE badge green,
   #fbbf24 the version-badge amber. No new colour enters the system.

   TWO LAYOUT RULES THAT MATTER
   ----------------------------
   1. Never put height:100% on .vx-lens-card. Cards stack inside a column, and
      each one resolving 100% of the column height overflows into whatever sits
      below. Use .vx-lens-card-fill on the single card that should fill.
   2. Flex and grid children default to min-width:auto, which defeats
      text-overflow:ellipsis. Anything that truncates needs min-width:0 on
      itself AND on its parent. Bootstrap 5.3 ships no min-w-0 utility, so
      .vx-lens-min0 exists for it. */

.vx-lens-surface {
	--vx-lens-bg: #0b1220;
	--vx-lens-card: #131f36;
	--vx-lens-line: rgba(255, 255, 255, .09);
	--vx-lens-line-soft: rgba(255, 255, 255, .06);
	--vx-lens-text: #e2e8f0;
	--vx-lens-dim: #94a3b8;
	--vx-lens-faint: #64748b;
	--vx-lens-ok: #34d399;
	--vx-lens-warn: #fbbf24;
	--vx-lens-err: #f87171;
	background: var(--vx-lens-bg);
	border-radius: 1rem;
	padding: 1rem;
	color: var(--vx-lens-text);
}

@media (min-width: 768px) {
	.vx-lens-surface {
		padding: 1.25rem;
	}
}

.vx-lens-min0 {
	min-width: 0;
}

/* ---- hero ---------------------------------------------------------------

   Light section, above the upload card. Sits on the page theme, not the dark
   result surface, so it uses Bootstrap's own colours rather than the
   --vx-lens-* set.
   -------------------------------------------------------------------------*/

.vx-lens-hero {
	padding-top: .5rem;
}

.vx-lens-hero-title {
	font-size: 1.75rem;
	font-weight: 600;
	line-height: 1.2;
	letter-spacing: -.01em;
	color: #0f172a;
	margin: 0 0 .875rem;
}

@media (min-width: 768px) {
	.vx-lens-hero-title {
		font-size: 2.125rem;
	}
}

/* Grey on purpose. The headline is a contrast - "trust this, not that" - and
   the half being dismissed is dismissed typographically too. */
.vx-lens-hero-muted {
	color: #94a3b8;
}

.vx-lens-hero-rule {
	display: inline-block;
	width: 36px;
	height: 2px;
	background: var(--vx-gradient-start);
	margin-bottom: .875rem;
}

.vx-lens-hero-lead {
	font-size: .9375rem;
	line-height: 1.7;
	color: #475569;
	max-width: 52ch;
	margin: 0 auto .5rem;
}


.vx-lens-hero-claim {
	font-size: .9375rem;
	font-weight: 500;
	line-height: 1.5;
	color: #1e3a5f;
	max-width: 44ch;
	border-top: 1px solid #dbe4f0;
	border-bottom: 1px solid #dbe4f0;
	padding: .7rem 0;
	margin: 1.1rem auto 1rem;
}

/* Footnote weight. A .NET buyer needs "no COM" to place the product; nobody
   else does, so it sits well below the lead. */
.vx-lens-hero-note {
	font-size: .75rem;
	color: #94a3b8;
	margin: 0;
}

@media (max-width: 575.98px) {
	.vx-lens-hero-title {
		font-size: 1.5rem;
	}

	.vx-lens-hero-lead {
		font-size: .875rem;
	}

	.vx-lens-hero-claim {
		font-size: .875rem;
	}
}

/* ---- controls strip -----------------------------------------------------

   Rendered into the layout's SubStrip section, so it sits between the promo
   strip and <main> on this page only.

   Uses the same dark navy as .vx-promo-strip and the same amber accent as the
   version badge, so it reads as a third member of the existing strip family
   rather than a new component.
   -------------------------------------------------------------------------*/

.vx-lens-controls {
	/* A shade off .vx-promo-strip (#1e3a5f). Identical navy made the two strips
	   read as one block with an invisible seam. */
	background-color: #16304f;
	color: #fff;
	/* The boundary. A hairline amber rule rather than a gap: it separates this
	   strip from the promo bar above without a pale band between them, and it
	   ties the strip to its own framework row below. */
	border-top: 1px solid #fbbf24;
}

.vx-lens-controls-body {
	padding: 1.1rem 0 1rem;
}

.vx-lens-controls-eyebrow {
	font-size: .8125rem;
	color: #fbbf24;
	margin: 0 0 .6rem;
}

.vx-lens-controls-head {
	font-size: .875rem;
	font-weight: 500;
	color: #fff;
	margin: 0 0 .15rem;
}

.vx-lens-controls-text {
	font-size: .8125rem;
	color: rgba(255, 255, 255, .62);
	line-height: 1.55;
	margin: 0;
}

/* Acronym row. Context, not claims - hence the quieter treatment and the
   dot separators borrowed from the footer nav.

   The band is full-bleed: this element sits outside .container so its
   background reaches both edges, and .vx-lens-controls-row re-centres the
   content on the grid. */
.vx-lens-controls-foot {
	/* Darker than the body, which is separation enough - a second amber rule
	   60px below the first reads as a repeated device rather than a deliberate
	   one. The strip is framed once, at the top. */
	background: #0e2440;
	border-top: 1px solid rgba(255, 255, 255, .1);
	padding: .75rem 0;
}

.vx-lens-controls-row {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-wrap: wrap;
	gap: .5rem 1rem;
}

/* "EVIDENCE FOR" leads the row; "EUC / IPE" closes it. Both stay small and
   muted - they are framing, and the framework names are the point. */
/* "EVIDENCE FOR" leads the row: small, uppercase, framing only. */
.vx-lens-controls-lead {
	font-size: .625rem;
	color: rgba(255, 255, 255, .45);
	letter-spacing: .08em;
	text-transform: uppercase;
}

	/* "EUC / IPE" closes it at the same scale as the frameworks but muted - it is
   the name of the work rather than a standard, so it reads as a peer in size
   and a subordinate in weight. */
	.vx-lens-controls-lead.vx-lens-controls-work {
		font-size: .9375rem;
		letter-spacing: .02em;
		text-transform: none;
		color: rgba(255, 255, 255, .45);
	}

/* Emphasis by scale, not decoration. SOX 404 is recognised instantly by anyone
   who needs it - the job is to give the acronyms room, not to box them. */
.vx-lens-controls-tag {
	font-size: .9375rem;
	color: #fbbf24;
	letter-spacing: .04em;
	white-space: nowrap;
}

/* Hidden at every width. At 15px with 1rem gaps the acronyms space themselves,
   and the dots start reading as punctuation between headings. Kept in the
   markup so the separator can be restored without touching the view. */
.vx-lens-controls-dot {
	display: none;
}

@media (max-width: 575.98px) {
	.vx-lens-controls-body {
		padding: .85rem 0 .8rem;
	}

	.vx-lens-controls-foot {
		padding: .65rem 0;
	}

	.vx-lens-controls-tag,
	.vx-lens-controls-lead.vx-lens-controls-work {
		font-size: .875rem;
	}

	.vx-lens-controls-row {
		gap: .4rem .75rem;
	}
}

/* ---- privacy and scope -------------------------------------------------

   Both sit on the light band between the compliance strip and the dark result
   surface, and only once results exist.

   The privacy line also appears on the upload screen, but it disappears the
   moment results render - which is exactly when it matters most, because by
   then the person has actually handed over a workbook. Repeating it here is
   the point, not an oversight.

   The scope statement is framed with hairlines rather than tinted or given a
   left rule: the hero claim already uses this device on the way in, so a
   reader meets the same treatment twice for the same kind of sentence. A tint
   would borrow a status colour, and none of them mean "here is the limit of
   what this page claims".
   ------------------------------------------------------------------------- */

.vx-lens-privacy {
	font-size: .8125rem;
	color: var(--bs-secondary-color);
	text-align: center;
	margin: 0 0 1rem;
}

/* Left-aligned inside a centred block. Four sentences centred under a
   one-line centred privacy line is harder to read than it looks. */
.vx-lens-scope {
	font-size: .875rem;
	line-height: 1.65;
	color: #475569;
	max-width: 64ch;
	border-top: 1px solid #dbe4f0;
	border-bottom: 1px solid #dbe4f0;
	padding: .85rem 0;
	margin: 0 auto 1.25rem;
}

/* The claim reads first, the qualification follows. Same navy as the hero
   claim, so the two statements of scope look like one voice. */
.vx-lens-scope-lead {
	font-weight: 500;
	color: #1e3a5f;
}

@media (max-width: 575.98px) {
	.vx-lens-privacy {
		font-size: .75rem;
		margin-bottom: .875rem;
	}

	.vx-lens-scope {
		font-size: .8125rem;
	}
}

/* ---- header ------------------------------------------------------------

   The brand gradient is reduced to a 3px rule across the top. A full gradient
   bar was the loudest element on a page of dark cards while carrying the least
   information, and it left the file name crammed into a pill it never fitted.

   Product colour is carried by the mark instead - a tinted tile behind the
   icon, matching the tinted icon treatment on the card headings.
   -------------------------------------------------------------------------*/

.vx-lens-head {
	border-radius: .75rem;
	overflow: hidden;
}

.vx-lens-head-rule {
	height: 3px;
	background: linear-gradient(90deg, var(--vx-gradient-start) 0%, var(--vx-gradient-end) 100%);
}

.vx-lens-head-body {
	background: var(--vx-lens-card);
	border: 1px solid var(--vx-lens-line);
	border-top: 0;
	padding: .7rem .875rem;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: .75rem;
	min-width: 0;
}

.vx-lens-mark {
	width: 34px;
	height: 34px;
	border-radius: .5rem;
	background: rgba(52, 211, 153, .12);
	color: var(--vx-lens-ok);
	display: flex;
	align-items: center;
	justify-content: center;
	flex: none;
	font-size: 1rem;
}

.vx-lens-brand {
	font-size: .9375rem;
	font-weight: 500;
	color: var(--vx-lens-text);
	line-height: 1.25;
}

.vx-lens-file {
	font-family: var(--bs-font-monospace);
	font-size: .75rem;
	color: var(--vx-lens-faint);
}

/* Status as a dot plus label, not a filled pill. The card below states the
   same thing in full, so this is a glance, not an announcement. */
.vx-lens-status {
	display: inline-flex;
	align-items: center;
	gap: .35rem;
	font-size: .75rem;
	white-space: nowrap;
}

	.vx-lens-status::before {
		content: "";
		width: 6px;
		height: 6px;
		border-radius: 50%;
		background: currentColor;
		flex: none;
	}

.vx-lens-btn {
	background: none;
	border: 1px solid rgba(255, 255, 255, .15);
	border-radius: .375rem;
	color: var(--vx-lens-dim);
	font-size: .75rem;
	padding: .3rem .7rem;
	white-space: nowrap;
}

	.vx-lens-btn:hover {
		background: rgba(255, 255, 255, .06);
		color: var(--vx-lens-text);
		border-color: rgba(255, 255, 255, .25);
	}

	.vx-lens-btn:focus-visible {
		outline: 2px solid var(--vx-lens-ok);
		outline-offset: 2px;
	}

/* ---- inspector actions -------------------------------------------------

   Two weights on purpose. Trace is solid blue in the header because it is the
   only control that leads somewhere; Copy is a ghost button under the formula
   because it is a convenience. Blue is the action colour across this surface -
   green and amber are reserved for status.
   ------------------------------------------------------------------------- */

.vx-lens-action {
	background: var(--vx-gradient-start);
	border: 1px solid var(--vx-gradient-start);
	border-radius: .375rem;
	color: #fff;
	font-size: .75rem;
	padding: .3rem .75rem;
	white-space: nowrap;
	cursor: pointer;
	transition: background-color .15s ease, border-color .15s ease;
}

	.vx-lens-action:hover {
		background: #0a58ca;
		border-color: #0a58ca;
		color: #fff;
	}

	.vx-lens-action:focus-visible {
		outline: 2px solid var(--vx-lens-ok);
		outline-offset: 2px;
	}

.vx-lens-ghost {
	background: none;
	border: 1px solid rgba(255, 255, 255, .18);
	border-radius: .375rem;
	color: var(--vx-lens-dim);
	font-size: .75rem;
	padding: .3rem .75rem;
	white-space: nowrap;
	cursor: pointer;
	transition: border-color .15s ease, color .15s ease;
}

	.vx-lens-ghost:hover {
		border-color: rgba(255, 255, 255, .35);
		color: var(--vx-lens-text);
	}

	/* Confirmed state. A copy button that gives no feedback gets pressed twice
	   and still leaves the person unsure. */
	.vx-lens-ghost.is-done {
		border-color: rgba(52, 211, 153, .45);
		color: var(--vx-lens-ok);
	}

	.vx-lens-ghost:focus-visible {
		outline: 2px solid var(--vx-lens-ok);
		outline-offset: 2px;
	}

/* Computed array elements. The <table> already lays these out as a matrix
   without any of this; the rules below only set spacing and alignment, so a
   stale stylesheet degrades the look and never the meaning. */
.vx-lens-matrix {
	border-collapse: collapse;
	margin: 0;
}

	.vx-lens-matrix td {
		padding: .1rem 1.25rem .1rem 0;
		text-align: right;
		white-space: nowrap;
	}

		.vx-lens-matrix td:last-child {
			padding-right: 0;
		}

/* ---- badges ------------------------------------------------------------ */

.vx-lens-chip {
	font-size: .75rem;
	padding: .15rem .55rem;
	border-radius: 999px;
	white-space: nowrap;
	background: rgba(255, 255, 255, .08);
	color: var(--vx-lens-dim);
}

.vx-lens-chip-ok {
	background: rgba(52, 211, 153, .15);
	color: var(--vx-lens-ok);
}

.vx-lens-chip-warn {
	background: rgba(251, 191, 36, .18);
	color: var(--vx-lens-warn);
}

/* ---- cards ------------------------------------------------------------- */

.vx-lens-card {
	background: var(--vx-lens-card);
	border: 1px solid var(--vx-lens-line);
	border-radius: .75rem;
	padding: .75rem .875rem;
	min-width: 0;
}

.vx-lens-card-fill {
	height: 100%;
}

.vx-lens-card-warn {
	border-color: rgba(251, 191, 36, .3);
}

.vx-lens-card-ok {
	border-color: rgba(52, 211, 153, .3);
}

.vx-lens-inset {
	background: #0b1220;
	border-radius: .375rem;
	padding: .5rem .625rem;
}

.vx-lens-tint-warn {
	background: rgba(251, 191, 36, .08);
	border-radius: .375rem;
	padding: .5rem .625rem;
}

.vx-lens-tint-ok {
	background: rgba(52, 211, 153, .08);
	border-radius: .375rem;
	padding: .5rem .625rem;
}

.vx-lens-tint-soft {
	background: rgba(255, 255, 255, .04);
	border-radius: .375rem;
	padding: .5rem .625rem;
}

/* ---- type -------------------------------------------------------------- */

.vx-lens-title {
	font-size: .875rem;
	font-weight: 500;
	color: var(--vx-lens-text);
	margin: 0;
}

.vx-lens-sub {
	font-size: .75rem;
	color: var(--vx-lens-dim);
	line-height: 1.6;
	margin: 0;
}

.vx-lens-ok {
	color: var(--vx-lens-ok);
}

/* Partial coverage. Blue, not amber: amber means "review required" on this
   surface, and a chain that cannot be resolved before evaluation is a fact
   about the formula, not a fault in the workbook. */
.vx-lens-info {
	color: #6ea8fe;
}

.vx-lens-err {
	color: #f87171;
}

.vx-lens-blank {
	color: var(--vx-lens-faint);
	font-style: italic;
}

.vx-lens-warn {
	color: var(--vx-lens-warn);
}

.vx-lens-dim {
	color: var(--vx-lens-dim);
}

.vx-lens-faint {
	color: var(--vx-lens-faint);
}

.vx-lens-mono {
	font-family: var(--bs-font-monospace);
	font-size: .75rem;
}

.vx-lens-truncate {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	min-width: 0;
	max-width: 100%;
}

.vx-lens-break {
	word-break: break-all;
}

/* ---- rows -------------------------------------------------------------- */

.vx-lens-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: .5rem;
	padding: .28rem 0;
	font-size: .8125rem;
	min-width: 0;
	border-bottom: 1px solid var(--vx-lens-line-soft);
}

	.vx-lens-row > * {
		min-width: 0;
	}

	.vx-lens-row:last-child {
		border-bottom: 0;
	}

.vx-lens-check {
	display: flex;
	align-items: center;
	gap: .5rem;
	padding: .2rem 0;
	font-size: .8125rem;
}

/* Sheet status dot. Never the only signal - the sheet name is coloured too. */
.vx-lens-dot {
	width: 6px;
	height: 6px;
	border-radius: 50%;
	flex: none;
	display: inline-block;
}

.vx-lens-dot-ok {
	background: var(--vx-lens-ok);
}

.vx-lens-dot-warn {
	background: var(--vx-lens-warn);
}

/* ---- figure ------------------------------------------------------------ */

.vx-lens-figure {
	font-size: 2rem;
	line-height: 1.1;
	font-weight: 500;
	color: var(--vx-lens-ok);
}

.vx-lens-bar {
	height: 4px;
	background: rgba(255, 255, 255, .07);
	border-radius: 2px;
	overflow: hidden;
}

	.vx-lens-bar > span {
		display: block;
		height: 100%;
		background: var(--vx-lens-ok);
		border-radius: 2px;
	}

.vx-lens-bar-warn > span {
	background: var(--vx-lens-warn);
}

/* ---- tabs -------------------------------------------------------------- */

.vx-lens-tabs {
	display: flex;
	gap: .125rem;
	border-bottom: 1px solid var(--vx-lens-line);
	flex-wrap: wrap;
}

.vx-lens-tab {
	background: none;
	border: 0;
	border-bottom: 2px solid transparent;
	color: var(--vx-lens-faint);
	font-size: .8125rem;
	padding: .45rem .7rem;
}

	.vx-lens-tab:hover {
		color: var(--vx-lens-text);
	}

	.vx-lens-tab.active {
		color: var(--vx-lens-ok);
		border-bottom-color: var(--vx-lens-ok);
	}

	.vx-lens-tab:focus-visible {
		outline: 2px solid var(--vx-lens-ok);
		outline-offset: -2px;
	}

.vx-lens-filter {
	background: rgba(255, 255, 255, .07);
	border: 0;
	color: var(--vx-lens-text);
	font-size: .75rem;
	padding: .25rem .65rem;
	border-radius: 999px;
}

	.vx-lens-filter.active {
		background: var(--vx-lens-ok);
		color: #06281f;
	}

	.vx-lens-filter:focus-visible {
		outline: 2px solid var(--vx-lens-ok);
		outline-offset: 2px;
	}

/* ---- affordances -------------------------------------------------------

   Everything clickable has to look clickable before it is touched. Two rules
   carry that:

   1. cursor:pointer on every interactive element. These are all <button>, and
      browsers give buttons cursor:default - so without this the entire surface
      feels inert no matter how it is styled.
   2. A chevron on any row that opens something. Drawn with borders rather than
      an icon font, so it needs no glyph and stays crisp at any zoom.
   ------------------------------------------------------------------------- */

.vx-lens-tab,
.vx-lens-filter,
.vx-lens-item,
.vx-lens-stack,
.vx-lens-leaf,
.vx-lens-crumb,
.vx-lens-btn,
button.vx-lens-row {
	cursor: pointer;
}

.vx-lens-item,
.vx-lens-stack,
button.vx-lens-row {
	position: relative;
	padding-right: 1.5rem;
}

	.vx-lens-item::after,
	.vx-lens-stack::after,
	button.vx-lens-row::after {
		content: "";
		position: absolute;
		right: .6rem;
		top: 50%;
		width: 6px;
		height: 6px;
		border-right: 1.5px solid currentColor;
		border-bottom: 1.5px solid currentColor;
		transform: translateY(-50%) rotate(-45deg);
		opacity: .3;
		transition: opacity .15s ease, right .15s ease;
	}

	.vx-lens-item:hover::after,
	.vx-lens-stack:hover::after,
	button.vx-lens-row:hover::after {
		opacity: .9;
		right: .45rem;
	}

/* Rows that open something, styled as .vx-lens-row but interactive. */
button.vx-lens-row {
	width: 100%;
	background: none;
	border: 0;
	border-bottom: 1px solid var(--vx-lens-line-soft);
	color: inherit;
	text-align: left;
}

	button.vx-lens-row:hover {
		background: rgba(255, 255, 255, .03);
	}

	button.vx-lens-row:focus-visible {
		outline: 2px solid var(--vx-lens-ok);
		outline-offset: -2px;
	}

/* Placeholder where a detail panel will appear. Says what a click will do
   before anything has been clicked, so the panel is expected rather than
   discovered. */
.vx-lens-placeholder {
	border: 1px dashed var(--vx-lens-line);
	border-radius: .75rem;
	padding: 1.5rem 1rem;
	text-align: center;
	color: var(--vx-lens-faint);
	font-size: .8125rem;
}

/* Marks a constraint that belongs to this page rather than to the engine.

   Amber as a pill, never as a whole band. Amber is the review colour on this
   surface, so an amber band would tell a reader their workbook has a problem
   when the truth is this demo has a limit. As a pill it reads as a label
   instead of a status.

   General purpose: reusable anywhere a demo-only constraint needs marking,
   such as the upload size cap or the tracing threshold. */

.vx-lens-demo-tag {
	background: rgba(251, 191, 36, .18);
	color: var(--vx-lens-warn);
	font-size: .625rem;
	letter-spacing: .05em;
	padding: .1rem .5rem;
	border-radius: 999px;
	white-space: nowrap;
	flex: none;
}

/* Small uppercase label introducing a short list inside a card. */
.vx-lens-eyebrow {
	font-size: .625rem;
	letter-spacing: .05em;
	color: var(--vx-lens-faint);
}

/* Small figure for a count grid. The overview's .vx-lens-figure is 2rem and
   built for a single headline number; four of those in a row would dominate the
   tab they sit in. */
.vx-lens-figure-sm {
	font-size: 1.125rem;
	line-height: 1.15;
	color: var(--vx-lens-text);
}

/* Four-column table of disagreements. Its own class rather than the formula
   list's, which reserves a gutter for the click cue. */
.vx-lens-list-gap .vx-lens-list-head,
.vx-lens-list-gap .vx-lens-item {
	grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) minmax(0, 1fr) 7rem;
	padding-right: .7rem;
}

@media (max-width: 575.98px) {
	.vx-lens-list-gap .vx-lens-list-head {
		display: none;
	}

	.vx-lens-list-gap .vx-lens-item {
		grid-template-columns: minmax(0, 1fr) auto;
		grid-template-areas:
			"cell     gap"
			"computed computed"
			"saved    saved";
		row-gap: .15rem;
	}

		.vx-lens-list-gap .vx-lens-item > :nth-child(1) {
			grid-area: cell;
		}

		.vx-lens-list-gap .vx-lens-item > :nth-child(2) {
			grid-area: computed;
		}

		.vx-lens-list-gap .vx-lens-item > :nth-child(3) {
			grid-area: saved;
		}

		.vx-lens-list-gap .vx-lens-item > :nth-child(4) {
			grid-area: gap;
		}

		.vx-lens-list-gap .vx-lens-item > :nth-child(2)::before {
			content: "computed ";
			color: var(--vx-lens-faint);
		}

		.vx-lens-list-gap .vx-lens-item > :nth-child(3)::before {
			content: "saved ";
			color: var(--vx-lens-faint);
		}
}

/* ---- truncation notices ------------------------------------------------

   Neutral grey with a scissors icon, never amber. Amber means "review
   required" everywhere else on this surface; using it for a cap would tell a
   reader their workbook has a problem when the truth is this demo has a limit.
   The notice is about us, not about their file.
   ------------------------------------------------------------------------- */

.vx-lens-notice {
	background: rgba(255, 255, 255, .05);
	border: 1px solid var(--vx-lens-line);
	border-radius: .5rem;
	padding: .6rem .8rem;
}

.vx-lens-notice-title {
	font-size: .8125rem;
	color: var(--vx-lens-text);
	margin-bottom: .1rem;
}

.vx-lens-notice-text {
	font-size: .75rem;
	color: var(--vx-lens-dim);
	line-height: 1.55;
}

/* Same message at the foot of a list, where it needs less weight. */
.vx-lens-list-foot {
	border-top: 1px solid var(--vx-lens-line-soft);
	background: rgba(255, 255, 255, .02);
	color: var(--vx-lens-faint);
	font-size: .75rem;
	text-align: center;
	padding: .5rem .7rem;
}

/* ---- lists ------------------------------------------------------------- */

.vx-lens-list {
	background: var(--vx-lens-card);
	border: 1px solid var(--vx-lens-line);
	border-radius: .625rem;
	overflow: hidden;
}

.vx-lens-list-head,
.vx-lens-item {
	display: grid;
	grid-template-columns: 4.5rem minmax(0, 1fr) 8rem 3.5rem;
	gap: .5rem;
	padding: .45rem .7rem;
	align-items: center;
}

/* Tablet: drop the depth column. The computed value is worth more than the
   depth on a narrow screen, and four monospace columns do not fit.
   Phones get a stacked layout instead - see the mobile block below. */
@media (min-width: 576px) and (max-width: 767.98px) {
	.vx-lens-list-head,
	.vx-lens-item {
		grid-template-columns: 3.5rem minmax(0, 1fr) 6rem;
	}

		.vx-lens-list-head > :nth-child(4),
		.vx-lens-item > :nth-child(4) {
			display: none;
		}
}

/* Grid children default to min-width:auto, so a long formula would size its
   column and push the row wider than the card. */
.vx-lens-list-head > *,
.vx-lens-item > * {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.vx-lens-list-head {
	background: rgba(255, 255, 255, .03);
	font-size: .6875rem;
	color: var(--vx-lens-faint);
}

/* Guidance banner, above the list.

   Brand blue on purpose. Green means "passed" and amber means "review
   required" everywhere else on this surface; a help box in either would read
   as a status the user has to interpret. Blue carries no status meaning here,
   so it can only be read as instruction.

   The raw --vx-gradient-start is too dark to read as text on #0b1220, so the
   lead uses a lightened blue while the rule and tint keep the brand value. */
.vx-lens-banner {
	background: rgba(13, 110, 253, .12);
	border: 1px solid rgba(13, 110, 253, .28);
	border-left: 3px solid var(--vx-gradient-start);
	border-radius: .375rem;
	padding: .6rem .8rem;
}

.vx-lens-banner-lead {
	font-size: .8125rem;
	font-weight: 500;
	color: #6ea8fe;
	margin-bottom: .15rem;
}

.vx-lens-banner-text {
	font-size: .75rem;
	color: var(--vx-lens-dim);
	line-height: 1.55;
}

/* The instruction lives here rather than in a banner above the list. A tinted
   bar over the content reads as a system notice and gets skipped; the same
   words in the header of the thing they describe are read while scanning it.

   The header tints green only when it carries a cue, so a header without one
   stays neutral. */
/* Cued lists reserve a fixed 7rem gutter on the header AND every row, so the
   column labels line up with the values beneath them.

   It has to be a fixed width. Each row is its own grid container, so an `auto`
   column in the header sizes to the cue while an `auto` column in a row sizes
   to nothing - which is exactly how the header drifted out of alignment. */
.vx-lens-cued .vx-lens-list-head,
.vx-lens-cued .vx-lens-item {
	grid-template-columns: 4.5rem minmax(0, 1fr) 8rem 3.5rem 7rem;
	padding-right: .7rem;
}

/* The cue carries the highlight on its own - the header behind it stays
   neutral. Tinting the whole header made the column labels harder to read for
   the sake of emphasising four words at the far right.

   Blue, matching the guidance banner and the Trace button: on this surface
   green means "passed", amber means "review required", and blue means "here is
   something you can do". The raw brand blue is too dark to read on #0b1220, so
   the text is lightened while the tint keeps the brand value. */
.vx-lens-cue {
	display: inline-flex;
	align-items: center;
	gap: .3rem;
	background: rgba(13, 110, 253, .2);
	color: #6ea8fe;
	font-size: .6875rem;
	letter-spacing: .04em;
	text-transform: uppercase;
	white-space: nowrap;
	overflow: visible;
	padding: .18rem .55rem;
	border-radius: 999px;
}

/* Evaluation and function lists have their own column counts. */
.vx-lens-list-eval.vx-lens-cued .vx-lens-list-head,
.vx-lens-list-eval.vx-lens-cued .vx-lens-item {
	grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr) 7rem;
}

.vx-lens-list-fn .vx-lens-list-head {
	grid-template-columns: minmax(0, 1fr) 5rem;
}

/* Function rows are stacked blocks rather than a grid, so the header
	   carries one label and the cue - nothing to align against. */
.vx-lens-list-fn.vx-lens-cued .vx-lens-list-head {
	grid-template-columns: minmax(0, 1fr) 7rem;
}

.vx-lens-item {
	border-top: 1px solid var(--vx-lens-line-soft);
	border-left: 2px solid transparent;
	width: 100%;
	background: none;
	text-align: left;
	color: var(--vx-lens-dim);
}

	.vx-lens-item:hover {
		background: rgba(255, 255, 255, .03);
	}

	.vx-lens-item.active {
		background: rgba(251, 191, 36, .08);
		border-left-color: var(--vx-lens-warn);
	}

	.vx-lens-item:focus-visible {
		outline: 2px solid var(--vx-lens-ok);
		outline-offset: -2px;
	}

/* Evaluation tab is three columns (cell / saved / computed), not the formula
   list's four. Declared after the media query so it wins at every width. */
.vx-lens-list-eval .vx-lens-list-head,
.vx-lens-list-eval .vx-lens-item {
	grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr);
}

	.vx-lens-list-eval .vx-lens-list-head > :nth-child(4),
	.vx-lens-list-eval .vx-lens-item > :nth-child(4) {
		display: none;
	}

/* Hint callout. A plain grey sentence reads as boilerplate and gets skipped;
   a tinted box with an icon reads as an instruction. Also used for the active
   filter chip, so guidance and state share one visual language.

   The tint was 7% at first, which on a near-black surface is invisible - and
   guidance nobody can see is not guidance. It is now strong enough to register
   as a distinct band, with body-weight text rather than dimmed. */
.vx-lens-hint {
	background: rgba(52, 211, 153, .1);
	border: 1px solid rgba(52, 211, 153, .22);
	border-left: 3px solid var(--vx-lens-ok);
	border-radius: .375rem;
	padding: .6rem .8rem;
	color: var(--vx-lens-text);
	font-size: .8125rem;
	line-height: 1.55;
}

	.vx-lens-hint .small {
		font-size: .8125rem;
		color: var(--vx-lens-text);
	}

/* Detail section. Full width under the overview, visibly a second section. */
.vx-lens-detail {
	border-top: 1px solid var(--vx-lens-line);
	padding-top: 1rem;
	margin-top: .25rem;
}

/* Two-column rows (names): label sits beside its subject, not flung to the far
   edge of a 1500px container. */
.vx-lens-list-two .vx-lens-pair {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 7rem;
	gap: .75rem;
	align-items: center;
	padding: .5rem .7rem;
	border-top: 1px solid var(--vx-lens-line-soft);
	min-width: 0;
}

	.vx-lens-list-two .vx-lens-pair > * {
		min-width: 0;
	}

	.vx-lens-list-two .vx-lens-pair:first-child {
		border-top: 0;
	}

/* A diagnostic row. Warning gets an amber rule and tinted ground; Information
   stays neutral. Without this, FEA2100 ("circular reference") reads exactly
   like FEA2101 ("the graph is incomplete") - the engine distinguishes them and
   the card should not flatten that. */
.vx-lens-note {
	padding: .35rem 0;
	border-left: 3px solid transparent;
}

	.vx-lens-note.is-raised {
		border-left-color: var(--vx-lens-warn);
		background: rgba(251, 191, 36, .08);
		border-radius: 0 .3rem .3rem 0;
		padding: .4rem .6rem;
		margin-bottom: .4rem;
	}

/* Diagnostic text is long. Two lines in the overview, full text in the tab. */
.vx-lens-clamp {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

/* Stacked rows (functions, names) are not the three-column grid. */
.vx-lens-stack {
	display: block;
	width: 100%;
	background: none;
	border: 0;
	border-top: 1px solid var(--vx-lens-line-soft);
	text-align: left;
	padding: .5rem .7rem;
	min-width: 0;
}

	.vx-lens-stack:hover {
		background: rgba(255, 255, 255, .03);
	}

	/* Selected function. Left rule plus tint, matching the selected formula row -
   one selection language across the whole surface. */
	.vx-lens-stack.active {
		background: rgba(52, 211, 153, .08);
		box-shadow: inset 2px 0 0 var(--vx-lens-ok);
	}

	.vx-lens-stack:focus-visible {
		outline: 2px solid var(--vx-lens-ok);
		outline-offset: -2px;
	}

/* Without an explicit cap the list renders every row and the page becomes
   thousands of pixels tall. */
.vx-lens-scroll {
	max-height: 24rem;
	overflow-y: auto;
	overscroll-behavior: contain;
}

	.vx-lens-scroll::-webkit-scrollbar {
		width: 8px;
	}

	.vx-lens-scroll::-webkit-scrollbar-thumb {
		background: rgba(255, 255, 255, .12);
		border-radius: 4px;
	}

/* ---- trace ------------------------------------------------------------- */

.vx-lens-node {
	background: rgba(52, 211, 153, .1);
	border: 1px solid rgba(52, 211, 153, .35);
	border-radius: .5rem;
	padding: .55rem .7rem;
	text-align: center;
	color: var(--vx-lens-ok);
	min-width: 0;
}

.vx-lens-leaf {
	background: rgba(255, 255, 255, .05);
	border: 0;
	border-radius: .375rem;
	padding: .45rem .35rem;
	text-align: center;
	color: var(--vx-lens-text);
	width: 100%;
	min-width: 0;
}

	.vx-lens-leaf:hover {
		background: rgba(255, 255, 255, .1);
	}

	.vx-lens-leaf:focus-visible {
		outline: 2px solid var(--vx-lens-ok);
		outline-offset: 2px;
	}

.vx-lens-crumb {
	background: none;
	border: 0;
	color: var(--vx-lens-dim);
	font-size: .75rem;
	padding: 0;
	text-decoration: underline;
	white-space: nowrap;
}

	.vx-lens-crumb:hover {
		color: var(--vx-lens-ok);
	}

.vx-lens-empty {
	color: var(--vx-lens-faint);
	font-size: .8125rem;
	text-align: center;
	padding: 1.75rem 1rem;
}

/* ---- trace ---------------------------------------------------------------

   Three layouts share this block. The tree indents by level and the chain runs
   horizontally; both cap their height and scroll internally, so the cards below
   the trace never get pushed off the page.
   ------------------------------------------------------------------------- */

.vx-lens-trace {
	max-height: 32rem;
	overflow-y: auto;
	overscroll-behavior: contain;
	border: 1px solid var(--vx-lens-line-soft);
	border-radius: .375rem;
	padding: .6rem .7rem;
}

	.vx-lens-trace::-webkit-scrollbar {
		width: 8px;
	}

	.vx-lens-trace::-webkit-scrollbar-thumb {
		background: rgba(255, 255, 255, .12);
		border-radius: 4px;
	}

.vx-lens-tree {
	min-width: 0;
}

.vx-lens-tree-row {
	display: flex;
	align-items: center;
	gap: .3rem;
	padding: .2rem 0;
	min-width: 0;
	/* Indent is capped at 12 levels in JS. Beyond that the rows stop moving
	   right - a 1,802-deep chain would otherwise be metres of indentation, and
	   that shape is rendered as a path anyway. */
}

	.vx-lens-tree-row.is-root {
		border-bottom: 1px solid var(--vx-lens-line-soft);
		padding-bottom: .35rem;
		margin-bottom: .2rem;
	}

.vx-lens-twisty {
	background: none;
	border: 0;
	color: var(--vx-lens-dim);
	padding: 0 .15rem;
	font-size: .75rem;
	line-height: 1;
	cursor: pointer;
	flex: none;
	width: 1.1rem;
	text-align: left;
}

	.vx-lens-twisty:hover {
		color: var(--vx-lens-ok);
	}

	.vx-lens-twisty:focus-visible {
		outline: 2px solid var(--vx-lens-ok);
		outline-offset: 1px;
	}

/* Keeps rows aligned where there is nothing to expand. */
.vx-lens-twisty-gap {
	width: 1.1rem;
	flex: none;
	font-size: .5rem;
	color: var(--vx-lens-faint);
	display: inline-flex;
	align-items: center;
}

.vx-lens-tree-cell {
	background: none;
	border: 0;
	padding: .1rem .3rem;
	border-radius: .25rem;
	text-align: left;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	cursor: pointer;
}

	.vx-lens-tree-cell:hover {
		background: rgba(255, 255, 255, .05);
	}

	.vx-lens-tree-cell:focus-visible {
		outline: 2px solid var(--vx-lens-ok);
		outline-offset: -1px;
	}

/* Step controls in the trace header. Inert rather than hidden when there is
   nowhere to go: an absent button says nothing, whereas "Nothing reads" and
   "Reads 4" both state a fact about the cell. */
.vx-lens-step {
	background: rgba(255, 255, 255, .06);
	border: 0;
	border-radius: .3rem;
	color: #6ea8fe;
	font-size: .75rem;
	padding: .2rem .55rem;
	white-space: nowrap;
	cursor: pointer;
}

	.vx-lens-step:hover:not(.is-inert) {
		background: rgba(13, 110, 253, .18);
	}

	.vx-lens-step:focus-visible {
		outline: 2px solid var(--vx-lens-ok);
		outline-offset: 2px;
	}

	.vx-lens-step.is-inert {
		background: rgba(255, 255, 255, .03);
		color: var(--vx-lens-faint);
		cursor: default;
	}

/* Chain nodes have their own class rather than reusing .vx-lens-leaf, which
   carries width:100% for use inside a Bootstrap column. Dropped into a flex
   row that made every step full width and stacked the path vertically - the
   exact opposite of what a path is. */
.vx-lens-chain-node {
	background: rgba(255, 255, 255, .05);
	border: 1px solid transparent;
	border-radius: .375rem;
	padding: .4rem .65rem;
	text-align: center;
	color: var(--vx-lens-text);
	cursor: pointer;
	flex: 0 0 auto;
	min-width: 4.5rem;
	max-width: 11rem;
}

	.vx-lens-chain-node:hover {
		background: rgba(255, 255, 255, .1);
	}

	.vx-lens-chain-node:focus-visible {
		outline: 2px solid var(--vx-lens-ok);
		outline-offset: 2px;
	}

	/* The cell being traced. Not clickable - it is already where you are. */
	.vx-lens-chain-node.is-centre {
		background: rgba(52, 211, 153, .1);
		border-color: rgba(52, 211, 153, .35);
		cursor: default;
	}

	/* A cell holding a typed value rather than a formula: the end of the chain. */
	.vx-lens-chain-node.is-source {
		background: rgba(251, 191, 36, .1);
		border-color: rgba(251, 191, 36, .35);
		cursor: default;
	}

.vx-lens-chain-gap {
	background: rgba(255, 255, 255, .05);
	border: 1px dashed var(--vx-lens-line);
	border-radius: .375rem;
	color: var(--vx-lens-faint);
	font-size: .75rem;
	padding: .55rem .8rem;
	flex: 0 0 auto;
	cursor: pointer;
}

	.vx-lens-chain-gap:hover {
		color: var(--vx-lens-text);
		border-color: rgba(255, 255, 255, .25);
	}

@media (max-width: 575.98px) {
	.vx-lens-trace {
		max-height: 22rem;
		padding: .5rem .4rem;
	}

	.vx-lens-tree-row {
		min-height: 32px;
	}
}

/* ---- upload (light, inherits the page theme) --------------------------- */

.vx-lens-dropzone {
	border: 2px dashed var(--bs-border-color);
	border-radius: .75rem;
	padding: 2.5rem 1rem;
	text-align: center;
	cursor: pointer;
	background: #fff;
	transition: border-color .15s ease, background-color .15s ease;
}

	.vx-lens-dropzone:hover,
	.vx-lens-dropzone.dragover {
		border-color: var(--vx-gradient-start);
		background: rgba(13, 110, 253, .03);
	}

	/* A file is staged but not sent. Solid border and a green wash say "ready",
   which is different from the dashed "drop something here" state. */
	.vx-lens-dropzone.has-file {
		border-style: solid;
		border-color: rgba(25, 135, 84, .35);
		background: rgba(25, 135, 84, .04);
	}

	/* Mid-request: the zone stops inviting input. */
	.vx-lens-dropzone.is-busy {
		pointer-events: none;
		opacity: .65;
	}

	.vx-lens-dropzone:focus-visible {
		outline: 2px solid var(--vx-gradient-start);
		outline-offset: 2px;
	}


/* ========================================================================
   Mobile
   ========================================================================
   Three things drive this block:

   1. A three or four column grid of monospace does not survive 360px. Rows
      stack into labelled lines instead of being squeezed until every column
      is an ellipsis.
   2. Touch targets. Tabs, filter pills and rows all reach 44px, which is the
      smallest reliably tappable size.
   3. The tab strip scrolls sideways rather than wrapping onto three lines and
      pushing the content below the fold.
   ======================================================================== */

@media (max-width: 575.98px) {

	.vx-lens-surface {
		border-radius: .75rem;
		padding: .75rem;
		/* A long unbroken formula must never widen the page. */
		overflow-x: hidden;
	}

	.vx-lens-head {
		padding: .7rem .75rem;
	}

	/* Header stacks: brand and file on one line, status and button below. */
	.vx-lens-head-body {
		flex-wrap: wrap;
		row-gap: .5rem;
	}

		.vx-lens-head-body > :last-child {
			width: 100%;
			justify-content: space-between;
		}

	/* ---- formula list: stacked -------------------------------------- */

	/* Column labels go, but the action cue must not - it is the only thing
	   telling a phone user the rows are interactive. The header collapses to
	   just the cue, right-aligned. */
	.vx-lens-list-head {
		display: none;
	}

		.vx-lens-list-head.has-action {
			display: flex;
			justify-content: flex-end;
			grid-template-columns: none;
		}

			.vx-lens-list-head.has-action > :not(.vx-lens-cue) {
				display: none;
			}

	/* Both selectors, deliberately: a media query adds no specificity, so a
	   bare .vx-lens-item would lose to .vx-lens-cued .vx-lens-item and the
	   desktop gutter would survive onto phones. */
	.vx-lens-item,
	.vx-lens-cued .vx-lens-item {
		grid-template-columns: minmax(0, 1fr) auto;
		grid-template-areas:
			"cell   depth"
			"formula formula"
			"result result";
		row-gap: .15rem;
		padding: .6rem .7rem;
		min-height: 44px;
		align-items: start;
	}

		.vx-lens-item > :nth-child(1) {
			grid-area: cell;
		}

		.vx-lens-item > :nth-child(2) {
			grid-area: formula;
		}

		.vx-lens-item > :nth-child(3) {
			grid-area: result;
		}

		.vx-lens-item > :nth-child(4) {
			grid-area: depth;
		}

		/* Without column headers the value needs its own marker. */
		.vx-lens-item > :nth-child(3)::before {
			content: "= ";
			color: var(--vx-lens-faint);
		}

	/* ---- evaluation list: saved above computed ----------------------- */

	.vx-lens-list-eval .vx-lens-item,
	.vx-lens-list-eval.vx-lens-cued .vx-lens-item {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
		grid-template-areas:
			"cell  cell"
			"saved computed";
		row-gap: .2rem;
	}

		.vx-lens-list-eval .vx-lens-item > :nth-child(1) {
			grid-area: cell;
		}

		.vx-lens-list-eval .vx-lens-item > :nth-child(2) {
			grid-area: saved;
		}

		.vx-lens-list-eval .vx-lens-item > :nth-child(3) {
			grid-area: computed;
		}

		.vx-lens-list-eval .vx-lens-item > :nth-child(2)::before {
			content: "saved ";
			color: var(--vx-lens-faint);
		}

		.vx-lens-list-eval .vx-lens-item > :nth-child(3)::before {
			content: "now ";
			color: var(--vx-lens-faint);
		}

	/* ---- names ------------------------------------------------------- */

	.vx-lens-list-two .vx-lens-pair {
		grid-template-columns: minmax(0, 1fr) 5rem;
		min-height: 44px;
	}

	/* ---- tabs: one scrolling line ------------------------------------ */

	.vx-lens-tabs {
		flex-wrap: nowrap;
		overflow-x: auto;
		overscroll-behavior-x: contain;
		scrollbar-width: none;
	}

		.vx-lens-tabs::-webkit-scrollbar {
			display: none;
		}

	.vx-lens-tab {
		flex: none;
		min-height: 44px;
		padding: .6rem .75rem;
	}

	/* ---- touch targets ----------------------------------------------- */

	.vx-lens-filter {
		min-height: 36px;
		padding: .4rem .8rem;
	}

	.vx-lens-stack {
		min-height: 44px;
		padding: .6rem .7rem;
	}

	.vx-lens-crumb {
		min-height: 44px;
		display: inline-flex;
		align-items: center;
	}

	/* ---- misc -------------------------------------------------------- */

	/* Shorter lists: 24rem is most of a phone viewport, and a nested scroll
	   that tall traps the page scroll. */
	.vx-lens-scroll {
		max-height: 18rem;
	}

	.vx-lens-figure {
		font-size: 1.75rem;
	}

	.vx-lens-dropzone {
		padding: 1.75rem .75rem;
	}

	/* Two trace nodes per row, not three - three is unreadable at 360px. */
	.vx-lens-trace-leaf {
		flex: 0 0 50%;
		max-width: 50%;
	}
}

@media (prefers-reduced-motion: reduce) {
	.vx-lens-dropzone,
	.vx-lens-item,
	.vx-lens-leaf,
	.vx-lens-stack {
		transition: none;
	}
}
