/* Sandbox preview styling. Standalone by design: the untrusted origin shares no build,
   no bundle and no tokens file with the app. */
:root {
	color-scheme: dark;
	--bg: #0d0a12;
	--cell: #1b1626;
	--edge: #2c2439;
	--ink: #ece6f2;
	--dim: #8d81a1;
	--hit: #f0a93a;
}

* {
	box-sizing: border-box;
}

html,
body {
	margin: 0;
	height: 100%;
	background: var(--bg);
	color: var(--ink);
	font-family: ui-sans-serif, system-ui, sans-serif;
}

.frame {
	height: 100%;
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
	padding: 0.75rem;
}

/* `minmax(0, …)` and `min-height: 0` throughout, and they are not tidying.
   A grid track sized `1fr` means `minmax(AUTO, 1fr)`, and the auto minimum floors at the
   content's min-content size. With text in the cells that was a few pixels and nothing
   showed. With an <img> it is the image's INTRINSIC height -- 500px -- so every row grew
   to 500px, the board overflowed its flex parent, and the HUD ended up painted on top of
   the reels. The board still contained exactly 15 cells, so the browser test that counts
   them passed against a page that looked broken. */
.board {
	flex: 1;
	display: grid;
	grid-template-columns: repeat(var(--reels, 5), minmax(0, 1fr));
	gap: 0.35rem;
	min-height: 12rem;
}

.reel {
	display: grid;
	grid-auto-rows: minmax(0, 1fr);
	gap: 0.35rem;
	min-height: 0;
	min-width: 0;
}

.cell {
	display: flex;
	align-items: center;
	justify-content: center;
	/* Positioning context for the wild-rung badge. Without it the badge resolves against
	   whatever ancestor happens to be positioned and lands off the cell it describes. */
	position: relative;
	background: var(--cell);
	border: 1px solid var(--edge);
	border-radius: 3px;
	font-size: 0.9rem;
	font-weight: 600;
	letter-spacing: 0.02em;
	min-height: 1.75rem;
	/* Same reason as the tracks above: without this the cell's own auto minimum is the
	   image's intrinsic size and the row cannot shrink to its share of the board. */
	min-width: 0;
	overflow: hidden;
	transition: background 90ms ease, border-color 90ms ease;
}

.cell.scatter {
	border-color: var(--hit);
	color: var(--hit);
}

.cell.win {
	background: #2a2033;
	border-color: var(--hit);
}

.hud {
	display: flex;
	justify-content: space-between;
	align-items: center;
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 0.8rem;
	color: var(--dim);
}

.win {
	color: var(--hit);
}

.origin-note {
	margin: 0;
	font-size: 0.65rem;
	color: #5f5573;
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* symbolUpgradeMeter. A masked fill and a width tween -- the same shape the studio
   original uses (a sprite masked by a runtime rectangle), which is why this mechanic
   renders at `basic` fidelity without any bespoke art. */
.meter {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	margin: 0.5rem 0 0;
	font: 500 0.75rem/1 ui-monospace, monospace;
}
.meter[hidden] { display: none; }
.meter-bar {
	flex: 1;
	height: 0.5rem;
	border-radius: 999px;
	background: rgba(255, 255, 255, 0.12);
	overflow: hidden;
}
.meter-fill {
	display: block;
	height: 100%;
	width: var(--fill, 0%);
	border-radius: inherit;
	background: linear-gradient(90deg, #f5a524, #f97316);
	transition: width 180ms ease-out;
}
.meter-label { opacity: 0.75; white-space: nowrap; }

/* A cell named by winInfo. Distinct from .win (a tumble explosion) because they are
   different events and conflating them hid which one had fired. */
.cell.winning {
	outline: 2px solid #f5a524;
	outline-offset: -2px;
}
/* A kind the meter promoted. */
.cell.upgraded { color: #f5a524; }

/* Generated symbol art. Sized by the cell so a 5x3 and a 7x9 grid both fit without the
   renderer knowing which it is drawing. */
.cell .sym {
	/* max-* rather than width/height 100%: the image scales DOWN to its cell and never
	   asks the cell to grow to it. `object-fit` alone does not prevent that -- it decides
	   how a box is filled, not how big the box is allowed to become. */
	max-width: 100%;
	max-height: 100%;
	width: auto;
	height: auto;
	object-fit: contain;
	display: block;
}

/* The three wild mechanics, distinguished on purpose: a cell can be held from an earlier
   spin, filled by this spin's expansion, or part of the roaming column, and those are
   different facts. One shared highlight would make the board agree with the book while
   telling the player the wrong story about why. */
.cell.sticky   { box-shadow: inset 0 0 0 2px #7dd3fc; }
.cell.expanded { box-shadow: inset 0 0 0 2px #a78bfa; }
.cell.roaming  { background: rgba(167, 139, 250, 0.14); }

/* A held wild that climbed a rung, with the rung itself drawn in the corner. The ring is
   a warmer colour than `.sticky` and sits INSIDE it, so a cell that is both reads as one
   thing that grew rather than as two unrelated highlights. */
.cell.upgraded-wild { box-shadow: inset 0 0 0 2px #fbbf24; }
/* BOTH RINGS when the cell is both, and this needed saying twice. `box-shadow` is one
   property, so the gold rule alone REPLACED the blue one and an upgraded wild stopped
   looking held — the mechanic that earned the rung became invisible at the moment it paid.
   Held is the outer ring, the rung grows inside it. */
.cell.sticky.upgraded-wild {
	box-shadow: inset 0 0 0 2px #7dd3fc, inset 0 0 0 4px #fbbf24;
}
.cell.upgraded-wild::after {
	content: '\00d7' attr(data-mult);
	position: absolute;
	/* Clear of the rings, not just inside the box. At 2px/1px the glyph sat under the
	   4px inset ring and was clipped by `overflow: hidden` — legible enough to look
	   intentional in a screenshot and not legible enough to read the rung off. */
	right: 5px;
	bottom: 4px;
	color: #fbbf24;
	font: 700 0.7rem/1 ui-monospace, monospace;
	text-shadow: 0 1px 3px #000, 0 0 3px #000;
}
.roaming { color: #a78bfa; font: 600 0.75rem/1 ui-monospace, monospace; }
.roaming[hidden] { display: none; }

.freespins { color: #7dd3fc; font: 600 0.75rem/1 ui-monospace, monospace; }
.freespins[hidden] { display: none; }

/* The max-win mark. Gold and heavier than the plain win readout: the ceiling is the goal
   of the game, so it must not look like an ordinary large number. */
.win.capped { color: #fbbf24; font-weight: 700; }

/* A held prize symbol and the value it carries. Green rather than gold: a prize is not a
   multiplier on someone else's win, it is its own amount, and the collection at the end of
   the feature pays the sum of these. */
.cell.prize { box-shadow: inset 0 0 0 2px #34d399; }
.cell.prize::before {
	content: attr(data-prize);
	position: absolute;
	left: 5px;
	top: 4px;
	color: #34d399;
	font: 700 0.7rem/1 ui-monospace, monospace;
	text-shadow: 0 1px 3px #000, 0 0 3px #000;
}
