/**
 * MCN Animations: Uploads (drop zone + progress ring)
 *
 * Studied from forevercomponents.com ("File Upload Dropzone") and rebuilt.
 * Theirs is a wall demo: the whole thing is one `role="img"` picture with a
 * hardcoded queue of three fake files on a loop, and no input anywhere. It
 * looks like an uploader and cannot accept a file.
 *
 * Here the real `<input type="file">` IS the component. The zone wraps it and
 * adds drag-and-drop on top, so:
 *
 *   - With no JavaScript it is a labelled file input on a form that still
 *     submits. The drop behaviour is the enhancement, never the mechanism.
 *   - A drop writes the files back into the input through a DataTransfer, so
 *     the browser posts them exactly as if they had been browsed for. Nothing
 *     has to be re-plumbed on the server.
 *   - Keyboard and screen-reader users get the native control untouched, and
 *     the chosen files are announced through a live region.
 *
 * The ring is kept separate from the zone on purpose. A ring that is not being
 * driven by a real upload event is a lie, and a plain form post has no such
 * event: the browser gives you nothing between submit and response. So on an
 * ordinary site form the zone lists what was chosen and shows no ring, and the
 * ring is driven only where genuine progress exists (XMLHttpRequest's
 * upload.onprogress, and `fetch` cannot report upload progress at all).
 *
 * Usage (Oxygen: a Code Block, or any markup with the class):
 *   <div class="anim-drop" data-anim-drop>
 *     <input type="file" id="docs" multiple accept=".pdf,image/*">
 *     <label for="docs">Drag and drop, or browse</label>
 *   </div>
 *
 * Driving a ring from your own upload code:
 *   MCNAnimations.ring(el, 42);     // 42%
 *   MCNAnimations.ring(el, 'done'); // swap to the check
 *
 * @package MCN_Animations
 */

/* This module builds its own live region, so it carries its own hider rather
   than depending on a theme class that may not exist. */
.anim-visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.anim-drop {
	--drop-accent: currentColor;
	--drop-line: rgba(0, 0, 0, 0.18);
	--drop-surface: rgba(0, 0, 0, 0.02);
	--drop-radius: 12px;

	position: relative;
	display: block;
	padding: 24px;
	border: 2px dashed var(--drop-line);
	border-radius: var(--drop-radius);
	background: var(--drop-surface);
	transition:
		border-color var(--anim-duration) var(--anim-ease),
		background-color var(--anim-duration) var(--anim-ease);
}

/* The zone is a drop target only once the script has claimed it. Until then it
   is a plain bordered box around a working input, which is the fail-open state. */
.js-anim .anim-drop[data-anim-drop] {
	cursor: pointer;
}

.anim-drop.is-over {
	border-color: var(--drop-accent);
	background: color-mix(in srgb, var(--drop-accent) 8%, transparent);
}

/* Dragging a file over the page lifts every zone slightly, so it is obvious
   where the file can go before the pointer is over one. */
.anim-drop.is-armed {
	border-color: color-mix(in srgb, var(--drop-accent) 55%, var(--drop-line));
}

.anim-drop__inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 8px;
	text-align: center;
	pointer-events: none;
}

/* The prompt is a real <label for>, so clicking it opens the picker without a
   line of script. That is also what keeps the zone usable if the JS never runs. */
.anim-drop__prompt {
	font-weight: 600;
	cursor: pointer;
	pointer-events: auto;
	text-decoration: underline;
	text-underline-offset: 3px;
}

.anim-drop__icon {
	width: 38px;
	height: 38px;
	color: var(--drop-accent);
}

.anim-drop.is-over .anim-drop__icon {
	animation: anim-drop-lift var(--anim-duration) var(--anim-ease-overshoot);
}

@keyframes anim-drop-lift {
	0%   { transform: translateY(0); }
	50%  { transform: translateY(-5px); }
	100% { transform: translateY(0); }
}

/* The input is visually hidden but stays focusable and stays THE control. The
   label is what people see and click, so the native picker still opens. */
.js-anim .anim-drop > input[type="file"] {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* Focus lives on the label, because that is what a keyboard user sees. */
.js-anim .anim-drop > input[type="file"]:focus-visible + label,
.js-anim .anim-drop > input[type="file"]:focus-visible ~ .anim-drop__inner {
	outline: var(--anim-focus-width) solid var(--anim-focus-color);
	outline-offset: var(--anim-focus-offset);
}

/* ------------------------------------------------------------------ *
 * The chosen-file list
 * ------------------------------------------------------------------ */
.anim-drop__files {
	margin: 16px 0 0;
	padding: 0;
	list-style: none;
	display: grid;
	gap: 8px;
}

.anim-drop__file {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 10px 12px;
	border: 1px solid var(--drop-line);
	border-radius: calc(var(--drop-radius) - 4px);
	background: Canvas;
	text-align: left;
}

.js-anim .anim-drop__file {
	animation: anim-drop-in var(--anim-duration) var(--anim-ease) both;
}

@keyframes anim-drop-in {
	from { opacity: 0; transform: translateY(6px); }
	to   { opacity: 1; transform: none; }
}

.anim-drop__kind {
	flex: none;
	display: grid;
	place-items: center;
	width: 34px;
	height: 34px;
	border-radius: 6px;
	background: color-mix(in srgb, var(--drop-accent) 14%, transparent);
	color: var(--drop-accent);
	font-size: 0.65rem;
	font-weight: 700;
	letter-spacing: 0.04em;
}

.anim-drop__meta {
	flex: 1;
	min-width: 0;
}

.anim-drop__name {
	display: block;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-weight: 600;
}

.anim-drop__size {
	display: block;
	font-size: 0.8rem;
	opacity: 0.7;
	font-variant-numeric: tabular-nums;
}

.anim-drop__remove {
	flex: none;
	width: 30px;
	height: 30px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: none;
	color: inherit;
	font: inherit;
	line-height: 1;
	cursor: pointer;
	opacity: 0.65;
}

.anim-drop__remove:hover,
.anim-drop__remove:focus-visible {
	opacity: 1;
	background: color-mix(in srgb, currentColor 10%, transparent);
}

/* ------------------------------------------------------------------ *
 * Progress ring
 *
 * A conic-gradient sweep with the middle punched out, driven by one custom
 * property. No SVG and no per-frame JavaScript: setting --p is the whole
 * update, and the browser interpolates it because the property is registered
 * as an <angle>-safe percentage below.
 * ------------------------------------------------------------------ */
@property --anim-ring-p {
	syntax: "<number>";
	inherits: false;
	initial-value: 0;
}

.anim-ring {
	--ring-accent: currentColor;
	--ring-done: #2e7d4f;
	--anim-ring-p: 0;

	position: relative;
	flex: none;
	display: grid;
	place-items: center;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	background: conic-gradient(
		var(--ring-accent) calc(var(--anim-ring-p) * 1%),
		color-mix(in srgb, var(--ring-accent) 14%, transparent) 0
	);
	transition: --anim-ring-p var(--anim-duration) linear;
}

.anim-ring::before {
	content: "";
	position: absolute;
	inset: 5px;
	border-radius: 50%;
	background: Canvas;
}

.anim-ring__pct {
	position: relative;
	font-size: 0.7rem;
	font-weight: 700;
	font-variant-numeric: tabular-nums;
}

.anim-ring__check {
	position: relative;
	display: none;
	width: 20px;
	height: 20px;
	color: var(--ring-done);
}

.anim-ring.is-done {
	background: var(--ring-done);
}

.anim-ring.is-done::before {
	background: Canvas;
}

.anim-ring.is-done .anim-ring__pct {
	display: none;
}

.anim-ring.is-done .anim-ring__check {
	display: block;
}

.js-anim .anim-ring.is-done .anim-ring__check {
	animation: anim-ring-pop var(--anim-duration) var(--anim-ease-overshoot) both;
}

@keyframes anim-ring-pop {
	from { transform: scale(0.5); opacity: 0; }
	to   { transform: none; opacity: 1; }
}

.anim-ring.is-error {
	background: color-mix(in srgb, #b3261e 70%, transparent);
}

/* ------------------------------------------------------------------ *
 * Reduced motion: the zone and the ring still work, they just stop moving.
 * The ring keeps its fill, because the fill is information, not decoration.
 * ------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
	.anim-drop,
	.anim-drop__file,
	.anim-ring,
	.anim-ring .anim-ring__check {
		transition: none;
		animation: none;
	}

	.anim-drop.is-over .anim-drop__icon {
		animation: none;
	}
}
