Code/TipsDisplay.razor.scss
// ============================================================================================
// THEME TOKENS. This is the whole override surface for the toast's look.
//
// A consumer that vendors this kit themes the panel by editing the values in THIS block and
// nothing else, every rule below reads these variables, so a game (World Builder, say) drops in
// its own palette here without hunting through the stylesheet. They are plain SCSS variables held
// in this one file on purpose: s&box compiles SCSS per file at runtime, and an `@import` of a
// game-side `_theme.scss` was deliberately avoided so the kit renders correctly wherever it is
// vendored. Keep tip text BRIGHT (never dim gray) so it stays legible over any HUD.
//
// The values are the approved Field Kits toast (docs/design/ui-system): a 3px accent stripe, a
// mono kicker, a wrap row of short prose runs and atomic key chips, and a 42px close x.
// ============================================================================================

$fg-tips-panel-bg:        rgba( 15, 17, 21, 0.92 );  // card background
$fg-tips-border:          rgba( 255, 255, 255, 0.08 );// card hairline
$fg-tips-radius:          10px;                        // card corner radius
$fg-tips-accent:          #e3c85e;                     // left accent stripe
$fg-tips-accent-hover:    #edd478;                     // the kit's hover accent, for a retint
$fg-tips-accent-ink:      #1a160a;                     // text that sits ON the accent

$fg-tips-glyph:           #e8eaed;                     // the leading emoji glyph
$fg-tips-kicker:          #f2f4f7;                     // the "GUIDE" label
$fg-tips-text:            #e8eaed;                     // the tip body text

// Keyboard keycap chip (square, monospace): *W* markup.
$fg-tips-key-bg:          rgba( 255, 255, 255, 0.05 );
$fg-tips-key-border:      rgba( 255, 255, 255, 0.12 );
$fg-tips-key-text:        #ffffff;
$fg-tips-key-radius:      5px;

// Gamepad button chip (pill, controller-feel): `A` backtick markup.
$fg-tips-pad-bg:          rgba( 60, 70, 90, 0.60 );
$fg-tips-pad-border:      rgba( 150, 190, 240, 0.65 );
$fg-tips-pad-text:        #ffffff;
$fg-tips-pad-radius:      99px;

$fg-tips-close:           #f2f4f7;                     // the × affordance at rest
$fg-tips-close-hover-bg:  rgba( 255, 255, 255, 0.10 );

// FONTS ARE DECLARED INLINE, never through a variable, and the UI family is the BARE name. The engine
// auto-registers fonts by family name and resolves them itself; the working precedent everywhere in the
// kits is `font-family: Poppins, sans-serif;` and `font-family: "Roboto Mono", monospace;`. A quoted
// "Poppins" behind a "Segoe UI" fallback did not resolve, and every element that should have been
// Poppins fell back to the engine default, which reads as mono. Owner caught it on a real screen; a
// capture cannot show it (font FACE resolution is not screenshot-verifiable).

// ============================================================================================
// TYPE. This sheet ships the Field Kits type scale: 12 / 13 / 14 / 16 with exact px line heights
// (12/16, 13/20, 14/18, 16/22), the same four sizes and the same pairings the Sound Mixer,
// Placement and Day/Night kits ship. Four distinct sizes, inside the engine's per-assembly budget.
// No letter-spacing anywhere, that one really does corrupt glyphs.
//
// AN EARLIER NOTE HERE CLAIMED font-size RENDERS TEXT AS SOLID BLOCKS, AND STRIPPED EVERY SIZE
// OUT OF THIS SHEET. That claim was wrong and it is retracted. It was read off MCP camera
// captures: the capture path rasterizes sized text as filled grey blocks, so a screenshot of any
// panel that declares font-size looks corrupted. On the physical screen those same panels render
// pin sharp, owner-verified on engine 26.07.22 (2026-07-31) against the Sound Mixer panels, which
// declare all four sizes.
//
// So a capture cannot verify TEXT. Geometry is judgeable in a capture (boxes, spacing, corners,
// fills); glyphs are not, and the check for text is the owner's eye on a real screen. Never
// delete a font-size on screenshot evidence again.
//
// LAYOUT. Structural rules below; colours all come from the tokens above. s&box compiles SCSS in
// the editor/runtime, not in the headless dotnet build, so style errors are invisible to a build.
// Never use `border-style` or the `border:` shorthand: a parse error there aborts the whole
// stylesheet and collapses the panel to 0x0. No box-shadow.
// ============================================================================================

.tips-root {
	position: absolute;
	width: 100%;
	height: 100%;
	pointer-events: none;

	.tip {
		position: absolute;
		// Anchored to the left edge and lifted clear of a vitals stack (bottom-left) and an action bar
		// (bottom-centre), well away from a minimap (bottom-right). Move it wherever suits your HUD.
		left: 24px;
		bottom: 220px;
		width: 500px;
		// The card grows to fit its tip. This was a hard 96px, which holds the kicker and about one and a
		// half lines, so a tip that wrapped lost the bottom of its last line: border-radius makes the
		// style engine clip children to the rounded box, so the overflow was sliced rather than spilled.
		// The demo's "Stand in the ring and press Space to switch the marker on." is the reported case.
		//
		// An older note here recorded the style engine UNDER-measuring the auto height of this shape (an
		// absolutely-positioned, transform-animated flex column), painting the chrome but dropping the
		// content. Re-verified on engine 26.07.22 (2026-07-31) across one and two line tips, repeated
		// forced re-renders, a code hotload and a fresh play session: the measurement is correct now.
		// min-height keeps a floor under it anyway, so a regression costs a slightly tall card rather
		// than an empty one.
		height: auto;
		min-height: 64px;
		// A ROW now: the accent stripe, then the content. align-items stretch is what gives the stripe
		// the full height of a card whose height is its content's.
		flex-direction: row;
		align-items: stretch;
		pointer-events: all;
		background-color: $fg-tips-panel-bg;
		border-width: 1px;
		border-color: $fg-tips-border;
		border-radius: $fg-tips-radius;
		// The card clips its own children, which is what rounds the ends of the accent stripe. Verified
		// at 8x on a live capture: without it the square stripe pokes out past the card's corner curve.
		// The card's height is its content's, so there is nothing else here for the clip to cut.
		overflow: hidden;
		// Gentle single-play rise: BuildHash only changes on tip/visibility, so this replays once per
		// new tip, not every frame.
		animation: tipRise 0.18s ease-out both;
	}

	// The accent stripe, built exactly as the mockup builds it: a square 3px bar, and the card's
	// `overflow: hidden` above rounds its ends against the card's own corner. It carried its own 9px
	// left radius before, and that is what the owner saw as a notch: a radius is scaled down until it
	// fits its own box, so 9px on a 3px-wide bar comes out as a 3px curve, and that small curve inside
	// the card's 10px one reads as a step at the top-left and bottom-left. One curve, not two.
	.stripe {
		width: 3px;
		flex-basis: 3px;
		flex-shrink: 0;
		background-color: $fg-tips-accent;
	}

	// The content beside the stripe. A little extra left padding so the text clears the stripe by the
	// same amount it clears the other three edges.
	.row {
		flex-grow: 1;
		flex-direction: row;
		align-items: flex-start;
		padding: 12px 12px 12px 14px;
		gap: 10px;
	}

	.glyph {
		font-family: Poppins, sans-serif;
		font-size: 14px;
		line-height: 18px;
		color: $fg-tips-glyph;
		flex-shrink: 0;
	}

	.body {
		flex-direction: column;
		flex-grow: 1;
	}

	.kicker {
		font-family: "Roboto Mono", monospace;
		font-size: 12px;
		line-height: 16px;
		font-weight: 700;
		text-transform: uppercase;   // hierarchy via weight + case; letter-spacing corrupts glyphs
		color: $fg-tips-kicker;
		margin-bottom: 3px;
	}

	.text {
		font-family: Poppins, sans-serif;
		font-size: 13px;
		font-weight: 500;
		// 13/20, the scale's body metric. Every child of this wrapping row carries it too, so a tip
		// that wraps onto a second line clears the key chips on the line above by the same 20px
		// rhythm the mockup draws. (The old unitless 1.25 was a workaround for having no sizes.)
		line-height: 20px;
		// Bright body text, the tip line must always read clearly.
		color: $fg-tips-text;
		// The row-of-spans layout (plain runs + key chips) needs to wrap BETWEEN spans when the line
		// runs out of room (the standard s&box wrap-row recipe), without this the row stays a single
		// nowrap line and the engine falls back to breaking mid-word/mid-chip. A wrapping row spaces
		// its children with margins; gap under-measures here.
		flex-direction: row;
		flex-wrap: wrap;
		align-items: center;

		span {
			font-size: 13px;
			line-height: 20px;
			color: $fg-tips-text;
			// The mockup spaces every child of this row by 3px. A wrapping row cannot use gap, so each
			// child carries the same right margin instead: a run against a chip gets the same air as a
			// chip against a run, and the pad pill stops touching the word after it.
			margin-right: 3px;
		}

		// Keycap: a small bright pill so key names read at a glance. flex + nowrap + no-shrink keep the
		// whole label, e.g. "RMB", one atomic, never-splitting chip even on a wrapped line. (s&box
		// panel CSS has no inline-flex; plain flex on a wrap-row parent behaves inline here.)
		.key {
			display: flex;
			align-items: center;
			flex-shrink: 0;
			white-space: nowrap;
			font-family: "Roboto Mono", monospace;
			font-size: 14px;
			line-height: 18px;
			font-weight: 700;
			color: $fg-tips-key-text;
			background-color: $fg-tips-key-bg;
			border-width: 1px;
			border-color: $fg-tips-key-border;
			border-radius: $fg-tips-key-radius;
			padding-left: 7px;
			padding-right: 7px;
			margin-left: 0px;
			margin-right: 3px;
		}

		// Gamepad button: the same atomic chip, rounded into a pill and tinted a cooler shade so a `A`
		// prompt is instantly distinct from a *W* keycap. Authors write gamepad button names in
		// backticks (see TipSegment). Same wrap-row / no-shrink rules as the keycap.
		.pad {
			display: flex;
			align-items: center;
			flex-shrink: 0;
			white-space: nowrap;
			font-family: "Roboto Mono", monospace;
			font-size: 14px;
			line-height: 18px;
			font-weight: 700;
			color: $fg-tips-pad-text;
			background-color: $fg-tips-pad-bg;
			border-width: 1px;
			border-color: $fg-tips-pad-border;
			border-radius: $fg-tips-pad-radius;
			padding-left: 10px;
			padding-right: 10px;
			margin-left: 0px;
			margin-right: 3px;
		}
	}

	// Optional "hide tips" advertise chip, beside the close affordance. Only rendered when the game set a
	// device-matching label (HideKeyLabel / HidePadLabel). It reuses the same chip language as the inline
	// key/pad runs so the label reads as a real button, plus the word itself in the kicker's voice.
	.hint {
		flex-direction: row;
		align-items: center;
		flex-shrink: 0;
		gap: 4px;
		padding: 2px 6px;
		border-radius: 5px;
		pointer-events: all;
		transition: all 0.1s ease;

		&:hover {
			background-color: $fg-tips-close-hover-bg;
		}

		.hint-label {
			font-family: "Roboto Mono", monospace;
			font-size: 12px;
			line-height: 16px;
			font-weight: 700;
			text-transform: uppercase;   // hierarchy via weight + case; letter-spacing corrupts glyphs
			color: $fg-tips-kicker;
		}

		// Same keycap / pad-chip look as the inline runs (the .text scope's chips don't reach here, so the
		// visual is restated from the shared tokens; keep the two in sync when retheming).
		.key {
			display: flex;
			align-items: center;
			flex-shrink: 0;
			white-space: nowrap;
			font-family: "Roboto Mono", monospace;
			font-size: 14px;
			line-height: 18px;
			font-weight: 700;
			color: $fg-tips-key-text;
			background-color: $fg-tips-key-bg;
			border-width: 1px;
			border-color: $fg-tips-key-border;
			border-radius: $fg-tips-key-radius;
			padding-left: 7px;
			padding-right: 7px;
		}

		.pad {
			display: flex;
			align-items: center;
			flex-shrink: 0;
			white-space: nowrap;
			font-family: "Roboto Mono", monospace;
			font-size: 14px;
			line-height: 18px;
			font-weight: 700;
			color: $fg-tips-pad-text;
			background-color: $fg-tips-pad-bg;
			border-width: 1px;
			border-color: $fg-tips-pad-border;
			border-radius: $fg-tips-pad-radius;
			padding-left: 10px;
			padding-right: 10px;
		}
	}

	// A generous target: this is the close affordance, and a small one is a fight. The negative margins
	// pull the 42px hit box back into the card's corner so the glyph still sits where the mockup puts it.
	.close {
		width: 42px;
		flex-basis: 42px;
		height: 42px;
		flex-shrink: 0;
		align-items: center;
		justify-content: center;
		margin-top: -8px;
		margin-right: -8px;
		margin-bottom: -8px;
		border-radius: 6px;
		font-family: Poppins, sans-serif;
		font-size: 16px;
		line-height: 22px;
		color: $fg-tips-close;
		pointer-events: all;
		transition: all 0.1s ease;

		&:hover {
			background-color: $fg-tips-close-hover-bg;
		}
	}
}

@keyframes tipRise {
	0%   { opacity: 0; transform: translateY( 8px ); }
	100% { opacity: 1; transform: translateY( 0px ); }
}