/**
 * mod_hangman - Laserworld glossary hangman
 *
 * The layout is Bootstrap 5 (card / row / col / btn / form-select). Everything here is
 * either a game-specific component Bootstrap has no equivalent for, or a small fallback
 * so the module still lays out sensibly in a template that does not ship Bootstrap.
 *
 * @copyright  (C) Laserworld
 * @license    GNU General Public License version 2 or later; see LICENSE.txt
 */

.mod-hangman {
	--mhg-accent: #19c37d;

	/*
	 * Neutral translucents rather than a light/dark pair: they read correctly on a white
	 * card and on a dark one, so the tiles and keys cannot end up light-on-light when the
	 * template's Bootstrap theme disagrees with this module's own theme setting.
	 */
	--mhg-line: rgba(128, 138, 152, .35);
	--mhg-tile: rgba(128, 138, 152, .1);
	--mhg-key: rgba(128, 138, 152, .12);

	/* The drawing needs a real backdrop, so this pair does switch. */
	--mhg-sky-1: #eef1f5;
	--mhg-sky-2: #dde3ea;
	--mhg-good: #19a463;
	--mhg-bad: #d3453f;

	/* Keeps the game compact on a wide screen instead of stretching edge to edge. */
	--mhg-figure-height: 285px;
	--mhg-tile-size: 30px;

	/*
	 * Height cap for a picture set that sizes itself. On a phone the columns stack and
	 * the picture spans the full width, so without a cap a portrait shot would push the
	 * letters and keyboard off the screen. vh keeps it sane on short landscape screens.
	 */
	--mhg-photo-max-height: min(52vh, 300px);

	font-size: .95rem;
}

.mod-hangman[data-theme="dark"],
[data-bs-theme="dark"] .mod-hangman[data-theme="auto"] {
	--mhg-sky-1: #151a20;
	--mhg-sky-2: #080b0e;
	--mhg-good: #35d48c;
	--mhg-bad: #f2665f;
}

/* "Auto" follows the visitor's system setting as well as the template's theme. */
@media (prefers-color-scheme: dark) {
	.mod-hangman[data-theme="auto"] {
		--mhg-sky-1: #151a20;
		--mhg-sky-2: #080b0e;
		--mhg-good: #35d48c;
		--mhg-bad: #f2665f;
	}
}

/* A template forced to light must not be overridden by the system preference. */
[data-bs-theme="light"] .mod-hangman[data-theme="auto"] {
	--mhg-sky-1: #eef1f5;
	--mhg-sky-2: #dde3ea;
	--mhg-good: #19a463;
	--mhg-bad: #d3453f;
}

.mod-hangman *,
.mod-hangman *::before,
.mod-hangman *::after {
	box-sizing: border-box;
}

/*
 * Author styles beat the user agent's [hidden] rule, so any element below that sets its
 * own display would ignore the attribute. The game toggles visibility with element.hidden.
 */
.mod-hangman [hidden] {
	display: none !important;
}

/* Fallbacks for templates without Bootstrap. */
.mod-hangman .mhg-shell:not(.card) {
	border: 1px solid var(--mhg-line);
	border-radius: .5rem;
	padding: 1rem;
}

.mod-hangman .row:not([class*="g-"]) {
	display: flex;
	flex-wrap: wrap;
	gap: 1rem;
}

.mhg-lang-icon {
	width: 16px;
	height: 16px;
	flex: none;
	opacity: .65;
}

.mhg-lang.form-select {
	width: auto;
	min-width: 7rem;
}

/* ------------------------------------------------------------------ score */

.mhg-score {
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

.mhg-score-item {
	display: inline-flex;
	align-items: center;
	gap: .25rem;
}

.mhg-score-dot {
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--mhg-good);
}

.mhg-score-dot--lost {
	background: var(--mhg-bad);
}

.mhg-score-flame {
	color: var(--mhg-accent);
	font-size: .65em;
}

/* ----------------------------------------------------------------- figure */

.mhg-figure {
	position: relative;
	background: radial-gradient(120% 90% at 50% 10%, var(--mhg-sky-1), var(--mhg-sky-2));
	border: 1px solid var(--mhg-line);
	border-radius: .5rem;
	padding: .4rem;
	overflow: hidden;
}

.mhg-svg,
.mhg-figure-images {
	display: block;
	width: 100%;
	height: var(--mhg-figure-height);
}

.mhg-figure-images {
	position: relative;
}

/* A photographic set brings its own backdrop, so drop the drawn one. */
.mhg-figure--photo {
	background: #0b0d10;
	padding: 0;
}

/*
 * "Match the picture": the panel takes the proportions of the frames instead of being a
 * fixed-height box, so a portrait photo is not pillarboxed inside a wide column.
 *
 * The sizing is done by an invisible in-flow copy of the first frame rather than by an
 * aspect-ratio guess. A block-level box with width:auto stretches to its container, and
 * clamping its height with max-height does not shrink the width back, which is exactly
 * how the black bars appeared. Letting a real image carry the intrinsic ratio avoids
 * that entirely and needs no server-side measurement.
 */
.mhg-figure--auto {
	background: none;
	border: 0;
	overflow: visible;
}

.mhg-figure--auto .mhg-figure-images {
	position: relative;
	display: block;
	width: fit-content;
	max-width: 100%;
	height: auto;
	margin-inline: auto;
	border: 1px solid var(--mhg-line);
	border-radius: .5rem;
	overflow: hidden;
	background: #0b0d10;
	line-height: 0;
}

.mhg-figure-sizer {
	display: block;
	width: auto;
	height: auto;
	max-width: 100%;

	/* The height cap is what keeps a tall picture usable once the columns stack. */
	max-height: var(--mhg-photo-max-height);
	visibility: hidden;
}

.mhg-figure-img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: var(--mhg-figure-fit, contain);
	object-position: center bottom;
	opacity: 0;
	transition: opacity .35s ease;
}

.mhg-figure-img.is-visible {
	opacity: 1;
}

/*
 * Parts that have not been earned yet stay as a faint ghost rather than vanishing
 * completely. Without it the frame is an empty box at the start of a round, which reads
 * as a broken image instead of a scene waiting to be drawn.
 */
.mhg-part {
	opacity: .07;
	transform: translateY(-6px);
	transform-origin: 50% 0;
	transition: opacity .35s ease, transform .35s cubic-bezier(.34, 1.4, .5, 1);
}

.mhg-part.is-visible {
	opacity: 1;
	transform: none;
}

.mhg-haze {
	opacity: .4;
}

.mod-hangman[data-theme="dark"] .mhg-haze,
[data-bs-theme="dark"] .mod-hangman[data-theme="auto"] .mhg-haze {
	opacity: .2;
}

.mhg-laser {
	opacity: 0;
	transition: opacity .6s ease;
	pointer-events: none;
}

.mhg-shell--laser .mhg-laser {
	opacity: .45;
}

.mhg-shell--laser.is-won .mhg-laser {
	opacity: 1;
}

.mhg-beam {
	stroke: var(--mhg-accent);
	stroke-width: 1.6;
	fill: none;
	opacity: .35;
}

.mhg-beam-2 {
	stroke-width: 3.4;
	opacity: .12;
}

.mhg-beam-3 {
	stroke-width: 1;
	opacity: .2;
}

/* ------------------------------------------------------------------ lives */

.mhg-life {
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--mhg-accent);
	transition: background .25s ease, transform .25s ease;
}

.mhg-life.is-gone {
	background: var(--mhg-line);
	transform: scale(.6);
}

.mhg-lives-count {
	font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------- word tiles */

.mhg-word {
	gap: 4px;
	align-content: flex-start;
	min-height: calc(var(--mhg-tile-size) * 1.25);
}

.mhg-word.is-shake {
	animation: mhg-shake .4s ease;
}

@keyframes mhg-shake {
	0%, 100% { transform: translateX(0); }
	22% { transform: translateX(-5px); }
	46% { transform: translateX(4px); }
	70% { transform: translateX(-2px); }
}

.mhg-tile {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--mhg-tile-size);
	height: calc(var(--mhg-tile-size) * 1.25);
	background: var(--mhg-tile);
	border: 1px solid var(--mhg-line);
	border-bottom-width: 3px;
	border-radius: .3rem;
	font-size: calc(var(--mhg-tile-size) * .6);
	font-weight: 700;
	line-height: 1;
	text-transform: uppercase;
	color: transparent;
	transition: color .2s ease, border-color .3s ease, transform .3s ease;
}

.mhg-tile.is-revealed {
	color: inherit;
	border-color: var(--mhg-accent);
	animation: mhg-flip .35s ease;
}

@keyframes mhg-flip {
	0% { transform: rotateX(-88deg); }
	100% { transform: rotateX(0); }
}

.mhg-tile.is-missed {
	color: var(--mhg-bad);
	border-color: var(--mhg-bad);
}

.mhg-tile--space {
	width: calc(var(--mhg-tile-size) * .45);
	border: 0;
	background: none;
}

.mhg-tile.mhg-tile--fixed {
	opacity: .65;
	border-style: dashed;
	border-color: var(--mhg-line);
	border-bottom-width: 1px;
	background: none;
}

/* ------------------------------------------------------------------- hint */

.mhg-hint {
	border-left: 3px solid var(--mhg-accent);
	padding: .35rem .6rem;
	background: rgba(128, 138, 152, .1);
	border-radius: 0 .3rem .3rem 0;
}

.mhg-hint-badge {
	font-size: .68em;
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--mhg-accent);
	margin-right: .4rem;
}

/* --------------------------------------------------------------- keyboard */

.mhg-keyboard {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(26px, 1fr));
	gap: 3px;
	max-width: 420px;
}

.mhg-key {
	appearance: none;
	border: 1px solid var(--mhg-line);
	border-bottom-width: 3px;
	border-radius: .3rem;
	background: var(--mhg-key);
	color: inherit;
	font: inherit;
	font-weight: 600;
	font-size: .82rem;
	line-height: 1;
	padding: 6px 1px;
	cursor: pointer;
	text-transform: uppercase;
	transition: transform .1s ease, background .2s ease, color .2s ease, border-color .2s ease, opacity .2s ease;
}

.mhg-key:hover:not(:disabled) {
	border-color: var(--mhg-accent);
	transform: translateY(-1px);
}

.mhg-key:active:not(:disabled) {
	transform: translateY(1px);
	border-bottom-width: 1px;
}

.mhg-key:disabled {
	cursor: default;
	opacity: .5;
}

.mhg-key.is-hit {
	background: var(--mhg-accent);
	border-color: var(--mhg-accent);
	color: #06120c;
	opacity: 1;
}

.mhg-key.is-miss {
	background: transparent;
	text-decoration: line-through;
	opacity: .45;
}

.mhg-wrong-list {
	color: var(--mhg-bad);
	font-weight: 600;
	letter-spacing: .1em;
	text-transform: uppercase;
}

/* ----------------------------------------------------------------- result */

.mhg-result {
	background: rgba(128, 138, 152, .1);
	border: 1px solid var(--mhg-line);
	animation: mhg-rise .3s ease;
}

@keyframes mhg-rise {
	from { opacity: 0; transform: translateY(6px); }
	to { opacity: 1; transform: none; }
}

.mhg-result-icon {
	width: 26px;
	height: 26px;
	border-radius: 50%;
	display: grid;
	place-items: center;
	font-size: .9rem;
	font-weight: 700;
	color: #fff;
	background: var(--mhg-good);
}

.mhg-result.is-lost .mhg-result-icon {
	background: var(--mhg-bad);
}

.mhg-result-title {
	font-size: .68rem;
	font-weight: 700;
	letter-spacing: .09em;
	text-transform: uppercase;
	opacity: .65;
}

.mhg-result-long > *:first-child {
	margin-top: 0;
}

.mhg-result-long > *:last-child {
	margin-bottom: 0;
}

.mhg-result-link {
	color: var(--mhg-accent);
}

.mhg-result-link:hover {
	text-decoration: underline !important;
}

.mhg-link-arrow {
	width: 15px;
	height: 15px;
	transition: transform .2s ease;
}

.mhg-result-link:hover .mhg-link-arrow {
	transform: translateX(3px);
}

/*
 * Bootstrap 5.3 buttons take their colours from --bs-btn-* variables that the template
 * is expected to define. Templates that do not leave .btn-primary transparent with white
 * text, which makes the main action invisible on a light card. Paint it from the module
 * accent instead so it is correct in every template.
 */
.mod-hangman .mhg-actions .btn-primary {
	--bs-btn-bg: var(--mhg-accent);
	--bs-btn-border-color: var(--mhg-accent);
	--bs-btn-color: #06120c;
	--bs-btn-hover-color: #06120c;
	--bs-btn-active-color: #06120c;

	background-color: var(--mhg-accent);
	border-color: var(--mhg-accent);
	color: #06120c;
}

.mod-hangman .mhg-actions .btn-primary:hover,
.mod-hangman .mhg-actions .btn-primary:focus {
	background-color: var(--mhg-accent);
	border-color: var(--mhg-accent);
	color: #06120c;
	filter: brightness(1.08);
}

.mod-hangman .mhg-actions .btn-primary:disabled {
	filter: none;
	opacity: .55;
}

.mod-hangman :focus-visible {
	outline: 2px solid var(--mhg-accent);
	outline-offset: 2px;
}

/* Give the drawing a little more room once the columns stack. */
@media (max-width: 575.98px) {
	.mod-hangman {
		--mhg-figure-height: 300px;
		--mhg-tile-size: 28px;
	}
}

@media (min-width: 576px) {
	.mod-hangman {
		--mhg-photo-max-height: 330px;
	}
}

@media (min-width: 992px) {
	.mod-hangman {
		--mhg-figure-height: 315px;
		--mhg-tile-size: 32px;
		--mhg-photo-max-height: 370px;
	}
}

@media (prefers-reduced-motion: reduce) {
	.mod-hangman *,
	.mod-hangman *::before,
	.mod-hangman *::after {
		animation-duration: .01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: .01ms !important;
	}
}

/* --------------------------------------------- administrator: link status */

.mod-hangman-relink .mod-hangman-status {
	max-width: 640px;
}

.mod-hangman-relink .mod-hangman-relink-result:empty {
	display: none;
}

.mod-hangman-relink .mod-hangman-relink-result ul {
	max-height: 220px;
	overflow-y: auto;
	font-size: .9em;
}
