/**
 * ECS Radio Player — card styling.
 *
 * ============================================================================
 * LAYOUT
 * ============================================================================
 *
 * The card is a CSS grid, not flexbox, and that is deliberate. On a wide
 * screen the artwork sits left of the text with the controls tucked under the
 * text; on a phone the controls break out to span the full card width beneath
 * both. Grid areas express that in two lines each way. The equivalent in
 * flexbox needs the controls nested inside the text column, which makes them
 * impossible to widen on mobile without changing the markup at runtime.
 *
 *   Wide (>560px)          Narrow (<=560px)
 *   +------+----------+    +------+-----------+
 *   | art  | body     |    | art  | body      |
 *   |      +----------+    +------+-----------+
 *   |      | controls |    | controls         |
 *   +------+----------+    +------------------+
 *
 * On mobile the artwork shrinks to a thumbnail rather than going full-bleed.
 * A full-width square is 328px tall on a typical phone, which pushes the play
 * button below the fold — and station pages can carry more than one player.
 *
 * ============================================================================
 * THEMING
 * ============================================================================
 *
 * Every colour is a custom property on .ecs-radio. --ecs-accent and
 * --ecs-accent-ink are written inline per card from the station's configured
 * accent; the rest can be overridden from the theme:
 *
 *   .ecs-radio { --ecs-card: #fff; --ecs-ink: #111; }
 *
 * ============================================================================
 * TOUCH
 * ============================================================================
 *
 * Controls grow under `@media (pointer: coarse)` rather than at a width
 * breakpoint. What matters is whether the visitor is using a finger, not how
 * wide their screen is — a tablet in landscape is wide AND touch-driven.
 */

.ecs-radio {
	--ecs-card: #17191E;
	--ecs-card-2: #1E2127;
	--ecs-ink: #ECEEF1;
	--ecs-dim: #8B929C;
	--ecs-rule: #292D34;
	--ecs-live: #FF3B30;
	--ecs-accent: #FF3B30;
	--ecs-accent-ink: #FFFFFF;
	--ecs-art: 168px;
	--ecs-pad: 16px;

	display: grid;
	grid-template-columns: var(--ecs-art) minmax(0, 1fr);
	grid-template-rows: 1fr auto auto;
	grid-template-areas:
		"art  body"
		"art  controls"
		"art  msg";
	column-gap: 18px;
	row-gap: 10px;

	background: var(--ecs-card);
	color: var(--ecs-ink);
	border: 1px solid var(--ecs-rule);
	border-radius: 10px;
	padding: var(--ecs-pad);
	box-sizing: border-box;
	font-family: inherit;
	max-width: 100%;

	/*
	 * The glow lives on the card itself. It has to be a transition target
	 * even when idle, otherwise going live would snap rather than fade.
	 */
	position: relative;
	transition: border-color 0.4s ease, box-shadow 0.4s ease;
}

/*
 * Whole-card glow while live.
 *
 * The colour comes from --ecs-accent, which is derived per station, so each
 * card glows in its own team colour. Kept wide and soft (30px blur, negative
 * spread) so it reads as a halo around the card rather than a hard ring:
 * a tight glow next to a rectangular card looks like a rendering error.
 */
.ecs-radio.is-live {
	border-color: color-mix(in srgb, var(--ecs-accent) 45%, var(--ecs-rule));
	animation: ecs-card-glow 3.4s ease-in-out infinite;
}

/*
 * color-mix is recent. Without it the border simply stays the standard rule
 * colour, which still looks correct — only the accented border is lost.
 */
@supports not (color: color-mix(in srgb, red 50%, blue)) {
	.ecs-radio.is-live {
		border-color: var(--ecs-accent);
	}
}

@keyframes ecs-card-glow {
	0%, 100% { box-shadow: 0 0 18px -8px var(--ecs-accent); }
	50%      { box-shadow: 0 0 30px -4px var(--ecs-accent); }
}

.ecs-radio *,
.ecs-radio *::before,
.ecs-radio *::after {
	box-sizing: border-box;
}

.ecs-radio-stack {
	display: grid;
	gap: 16px;
}

/* ---------- artwork ---------- */

.ecs-radio-art {
	grid-area: art;
	width: var(--ecs-art);
	aspect-ratio: 1 / 1;
	align-self: start;
	border-radius: 7px;
	overflow: hidden;
	background: var(--ecs-card-2);
	position: relative;
}

/*
 * aspect-ratio is well supported now, but a fallback keeps the artwork square
 * on older WebKit rather than collapsing it to zero height.
 */
@supports not (aspect-ratio: 1 / 1) {
	.ecs-radio-art::before {
		content: "";
		display: block;
		padding-top: 100%;
	}
	.ecs-radio-art img,
	.ecs-radio-art-fallback {
		position: absolute;
		inset: 0;
	}
}

.ecs-radio-art img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.ecs-radio-art-fallback {
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--ecs-dim);
}

/* ---------- text ---------- */

.ecs-radio-body {
	grid-area: body;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 7px;
}

.ecs-radio-top {
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
	font-size: 11px;
	letter-spacing: 0.13em;
	text-transform: uppercase;
	font-weight: 700;
	color: var(--ecs-dim);
}

/*
 * The state badge. Off air it is a quiet grey pill; live it fills with the
 * station's accent and breathes on a slow cycle.
 *
 * Breathing, not blinking. A fast pulse reads as an alarm and becomes
 * genuinely irritating on a page someone leaves open for a two-hour game.
 */
.ecs-radio-badge {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 3px 9px 3px 7px;
	border-radius: 100px;
	background: rgba(255, 255, 255, 0.06);
	border: 1px solid var(--ecs-rule);
	transition: background 0.3s ease, border-color 0.3s ease;
}

.ecs-radio-tally {
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--ecs-dim);
	flex: none;
}

.ecs-radio.is-live .ecs-radio-badge {
	background: var(--ecs-accent);
	border-color: var(--ecs-accent);
	color: var(--ecs-accent-ink);
	animation: ecs-breathe 3.4s ease-in-out infinite;
}

.ecs-radio.is-live .ecs-radio-tally {
	background: var(--ecs-accent-ink);
	opacity: 0.9;
}

.ecs-radio.is-live .ecs-radio-state {
	color: var(--ecs-accent-ink);
}

@keyframes ecs-breathe {
	0%, 100% { box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); }
	50%      { box-shadow: 0 0 14px 1px var(--ecs-accent); }
}

/*
 * The artwork gets a hairline in the accent, but no animation of its own.
 * With the whole card breathing, a second element pulsing in time reads as
 * busy rather than alive — one moving thing is the effect, two is noise.
 */
.ecs-radio.is-live .ecs-radio-art {
	box-shadow: 0 0 0 1px var(--ecs-accent);
}

/*
 * Duration sits directly after the badge rather than out at the right edge:
 * it qualifies "Live now", so the two belong together as one phrase.
 */
.ecs-radio-uptime {
	letter-spacing: 0.06em;
	font-weight: 600;
	color: var(--ecs-dim);
	white-space: nowrap;
}

.ecs-radio-uptime:empty {
	display: none;
}

.ecs-radio-listeners {
	margin-left: auto;
	letter-spacing: 0.06em;
	font-weight: 600;
	color: var(--ecs-dim);
	white-space: nowrap;
}

.ecs-radio-name {
	margin: 0;
	font-size: 19px;
	line-height: 1.25;
	font-weight: 650;
	color: var(--ecs-ink);
	/* Long station names must wrap rather than widen the grid column. */
	overflow-wrap: anywhere;
}

.ecs-radio-now {
	font-size: 13.5px;
	line-height: 1.45;
	color: var(--ecs-dim);
	display: -webkit-box;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	overflow-wrap: anywhere;
}

.ecs-radio.is-live .ecs-radio-now {
	color: var(--ecs-ink);
}

/* ---------- controls ---------- */

.ecs-radio-controls {
	grid-area: controls;
	display: flex;
	align-items: center;
	gap: 14px;
	align-self: end;
	min-width: 0;
}

.ecs-radio-play {
	position: relative;
	flex: none;
	width: 48px;
	height: 48px;
	border-radius: 50%;
	border: 0;
	background: var(--ecs-accent);
	color: var(--ecs-accent-ink);
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	padding: 0;
	transition: transform 0.12s ease, opacity 0.12s ease;
	/* Kill the blue flash and the 300ms delay on touch. */
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

.ecs-radio-play svg {
	width: 22px;
	height: 22px;
}

.ecs-radio-play:hover:not(:disabled) {
	transform: scale(1.06);
}

.ecs-radio-play:active:not(:disabled) {
	transform: scale(0.97);
}

.ecs-radio-play:focus-visible {
	outline: 2px solid var(--ecs-ink);
	outline-offset: 3px;
}

.ecs-radio-play:disabled {
	opacity: 0.3;
	cursor: not-allowed;
}

.ecs-radio-play .ecs-icon-pause,
.ecs-radio.is-playing .ecs-radio-play .ecs-icon-play {
	display: none;
}

.ecs-radio.is-playing .ecs-radio-play .ecs-icon-pause {
	display: block;
}

.ecs-radio-spinner {
	display: none;
	position: absolute;
	width: 20px;
	height: 20px;
	border: 2px solid rgba(0, 0, 0, 0.22);
	border-top-color: currentColor;
	border-radius: 50%;
	animation: ecs-spin 0.7s linear infinite;
}

.ecs-radio.is-loading .ecs-radio-spinner {
	display: block;
}

.ecs-radio.is-loading .ecs-radio-play svg {
	visibility: hidden;
}

@keyframes ecs-spin {
	to { transform: rotate(360deg); }
}

/*
 * Once playing, the glow settles to a steady ring. Motion signals "live";
 * after the listener presses play they already know, and a card animating
 * next to what they are hearing is just noise.
 */
.ecs-radio.is-playing.is-live,
.ecs-radio.is-playing.is-live .ecs-radio-badge {
	animation: none;
}

.ecs-radio.is-playing.is-live {
	box-shadow: 0 0 26px -6px var(--ecs-accent);
}

/* ---------- volume ---------- */

.ecs-radio-vol {
	display: flex;
	align-items: center;
	gap: 9px;
	flex: 1 1 auto;
	min-width: 0;
}

.ecs-radio-mute {
	background: none;
	border: 0;
	padding: 6px;
	color: var(--ecs-dim);
	cursor: pointer;
	display: flex;
	flex: none;
	border-radius: 6px;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

.ecs-radio-mute svg {
	width: 19px;
	height: 19px;
}

.ecs-radio-mute:hover {
	color: var(--ecs-ink);
}

.ecs-radio-mute:focus-visible {
	outline: 2px solid var(--ecs-ink);
	outline-offset: 1px;
}

.ecs-radio-mute .ecs-icon-muted,
.ecs-radio.is-muted .ecs-radio-mute .ecs-icon-vol {
	display: none;
}

.ecs-radio.is-muted .ecs-radio-mute .ecs-icon-muted {
	display: block;
}

/*
 * iOS ignores the volume property on media elements entirely — level is
 * hardware-only there. The script feature-tests for this and adds .no-volume,
 * so we hide a slider that would otherwise be dead UI. The mute button stays,
 * because the muted property IS honoured.
 */
.ecs-radio.no-volume .ecs-radio-slider {
	display: none;
}

.ecs-radio-slider {
	-webkit-appearance: none;
	appearance: none;
	background: transparent;
	flex: 1 1 auto;
	min-width: 60px;
	max-width: 170px;
	height: 20px;
	cursor: pointer;
	margin: 0;
	touch-action: manipulation;
}

.ecs-radio-slider::-webkit-slider-runnable-track {
	height: 4px;
	border-radius: 2px;
	background: linear-gradient(
		to right,
		var(--ecs-ink) 0 var(--fill, 80%),
		var(--ecs-rule) var(--fill, 80%) 100%
	);
}

.ecs-radio-slider::-webkit-slider-thumb {
	-webkit-appearance: none;
	appearance: none;
	width: 13px;
	height: 13px;
	border-radius: 50%;
	background: var(--ecs-ink);
	margin-top: -4.5px;
	border: 0;
}

.ecs-radio-slider::-moz-range-track {
	height: 4px;
	border-radius: 2px;
	background: var(--ecs-rule);
}

.ecs-radio-slider::-moz-range-progress {
	height: 4px;
	border-radius: 2px;
	background: var(--ecs-ink);
}

.ecs-radio-slider::-moz-range-thumb {
	width: 13px;
	height: 13px;
	border-radius: 50%;
	background: var(--ecs-ink);
	border: 0;
}

.ecs-radio-slider:focus-visible {
	outline: 2px solid var(--ecs-ink);
	outline-offset: 2px;
	border-radius: 3px;
}

/* ---------- messages ---------- */

.ecs-radio-msg {
	grid-area: msg;
	display: none;
	font-size: 12.5px;
	color: #FFB4AE;
}

.ecs-radio.has-msg .ecs-radio-msg {
	display: block;
}

.ecs-radio-note {
	padding: 12px 14px;
	background: #FFF4E5;
	border: 1px solid #F0C36D;
	border-radius: 6px;
	font-size: 13.5px;
	color: #6B4E12;
}

/* ---------- phones ---------- */

@media (max-width: 560px) {
	.ecs-radio {
		--ecs-art: 92px;
		--ecs-pad: 14px;

		/*
		 * Controls break out to their own full-width row. On a 360px screen
		 * the text column is only about 210px, which is not enough for a play
		 * button, a mute toggle and a usable volume slider side by side.
		 */
		grid-template-columns: var(--ecs-art) minmax(0, 1fr);
		grid-template-rows: auto auto auto;
		grid-template-areas:
			"art       body"
			"controls  controls"
			"msg       msg";
		column-gap: 14px;
		row-gap: 14px;
	}

	.ecs-radio-name {
		font-size: 17px;
	}

	.ecs-radio-now {
		font-size: 13px;
		-webkit-line-clamp: 3;
		line-clamp: 3;
	}

	/* The badge already says "Live now"; a listener count as well is clutter
	   at this width, and it is the least important thing on the card. */
	.ecs-radio-listeners {
		display: none;
	}

	/* Duration stays — it is the more useful of the two on a phone. */

	.ecs-radio-slider {
		max-width: none;
	}
}

/* Very narrow phones: stop the artwork crowding the name. */
@media (max-width: 380px) {
	.ecs-radio {
		--ecs-art: 76px;
		--ecs-pad: 12px;
	}

	.ecs-radio-name {
		font-size: 16px;
	}
}

/* ---------- touch input ---------- */

/*
 * Sized by input type rather than screen width. A tablet in landscape is wide
 * and finger-driven; a small desktop window is narrow and mouse-driven. The
 * 44px floor is the accessibility guideline for touch targets, and the old
 * 27px mute button was well under it.
 */
@media (pointer: coarse) {
	.ecs-radio-play {
		width: 56px;
		height: 56px;
	}

	.ecs-radio-play svg {
		width: 26px;
		height: 26px;
	}

	.ecs-radio-mute {
		padding: 12px;
	}

	.ecs-radio-slider {
		height: 34px;
	}

	.ecs-radio-slider::-webkit-slider-thumb {
		width: 18px;
		height: 18px;
		margin-top: -7px;
	}

	.ecs-radio-slider::-moz-range-thumb {
		width: 18px;
		height: 18px;
	}

	/* No hover on touch — the transform just sticks after a tap. */
	.ecs-radio-play:hover:not(:disabled) {
		transform: none;
	}
}

@media (prefers-reduced-motion: reduce) {
	.ecs-radio *,
	.ecs-radio *::before,
	.ecs-radio *::after {
		animation: none !important;
		transition: none !important;
	}
}
