/* ==========================================================================
   Al-Azhar Academy — animations.css
   The ONLY file that defines motion. Structure and appearance live elsewhere.

   Rules honoured here:
   - Only `transform`, `opacity` and two registered custom properties (which
     drive the navigation pill's geometry) are animated. Shadows fade via a
     pseudo-element, never by animating box-shadow itself.
   - Decorative motion sits behind `prefers-reduced-motion: no-preference`, so
     "reduce" users simply get the finished state.
   - Hover effects require `(hover: hover) and (pointer: fine)`; touch devices
     get `:active` feedback instead.
   - Scroll triggers use IntersectionObserver and viewport changes use
     matchMedia (see assets/js/main.js). There are no scroll listeners and no
     resize handlers anywhere in this project.
   ========================================================================== */

/* -------------------------------------------------------------------------
   0. Registered geometry properties for the sliding navigation pill
   Registering them gives the browser a type, which is what makes them
   transitionable. Where `@property` is unsupported the pill still moves — it
   just resizes in one step instead of springing.
   ------------------------------------------------------------------------- */
@property --pill-x {
	syntax: '<length>';
	inherits: true;
	initial-value: 0px;
}

@property --pill-w {
	syntax: '<length>';
	inherits: true;
	initial-value: 0px;
}

/* -------------------------------------------------------------------------
   1. Reveal on scroll
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
	html {
		scroll-behavior: smooth;
	}

	/* Gated on html.js so that without JavaScript the content is simply
	   visible instead of stuck at opacity 0. */
	html.js .reveal {
		opacity: 0;
		transform: translateY(12px);
		transition:
			opacity var(--dur-reveal) var(--ease),
			transform var(--dur-reveal) var(--ease);
	}

	html.js .reveal.is-visible {
		opacity: 1;
		transform: none;
	}

	/* Page transition: a short fade and an 8px slide on load. */
	.page-enter {
		animation: page-enter var(--dur-300) var(--ease) both;
	}

	@keyframes page-enter {
		from {
			opacity: 0;
			transform: translateY(8px);
		}
		to {
			opacity: 1;
			transform: none;
		}
	}
}

/* Mobile: fade only. Translation on small screens causes jank and
   contributes to motion sickness. */
@media (max-width: 47.99em) {
	@media (prefers-reduced-motion: no-preference) {
		html.js .reveal {
			transform: none;
		}
	}

	.stagger > .reveal {
		transition-delay: 0ms;
	}
}

/* -------------------------------------------------------------------------
   2. Stagger — set --i on children with JS
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
	@media (min-width: 48em) {
		.stagger > .reveal {
			transition-delay: calc(var(--i, 0) * var(--stagger-step));
		}
	}
}

/* -------------------------------------------------------------------------
   3. Ambient motion — slow, almost invisible
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
	@media (min-width: 48em) {
		.canvas::after {
			animation: ambient-drift var(--dur-ambient) var(--ease-in-out) infinite alternate;
		}
	}

	@keyframes ambient-drift {
		from {
			transform: translate3d(-1.5%, -1%, 0);
		}
		to {
			transform: translate3d(1.5%, 1%, 0);
		}
	}
}

/* -------------------------------------------------------------------------
   4. Navigation pill — the bouncy indicator under the desktop nav
   JS writes --pill-x and --pill-w on the rail; the pill inherits both. The
   spring curve overshoots slightly, which is what makes it feel physical.
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
	.nav__rail {
		transition:
			--pill-x var(--dur-spring) var(--ease-spring),
			--pill-w var(--dur-spring) var(--ease-spring);
	}
}

/* Measuring is not moving. JS drops this flag, parks, and sets it again in the
   same frame, so the very first measurement — and the correction that arrives
   when the webfonts land — land in place instead of springing in from a stale
   position. (Higher specificity than the rule above, so it wins.) */
.nav__rail:not([data-pill='ready']) {
	transition: none;
}

/* -------------------------------------------------------------------------
   5. Header — the six-hue stripe firms up as the page scrolls
   It is already on at the top of the page (see base.css); scrolling past the
   first screen only takes it to full strength.
   ------------------------------------------------------------------------- */
.site-header::after {
	transition: opacity var(--dur-200) var(--ease);
}

/* -------------------------------------------------------------------------
   6. Buttons
   ------------------------------------------------------------------------- */
.btn {
	transition:
		background-color var(--dur-200) var(--ease),
		border-color var(--dur-200) var(--ease),
		color var(--dur-200) var(--ease),
		filter var(--dur-200) var(--ease),
		transform var(--dur-150) var(--ease);
}

.link-arrow__icon {
	transition: transform var(--dur-200) var(--ease);
}

@media (hover: hover) and (pointer: fine) {
	.btn:hover {
		transform: translateY(-1px);
	}

	/* Brightening the fill would mean swapping background-image, which cannot
	   be interpolated. A filter is smooth and stays on the compositor. */
	.btn--primary:hover {
		filter: brightness(1.07) saturate(1.06);
	}

	.btn--secondary:hover {
		background-color: var(--tone-soft);
		border-color: var(--tone);
	}

	.btn--inverse:hover {
		background-color: var(--tone-soft);
		border-color: var(--tone);
	}

	.link-arrow:hover .link-arrow__icon {
		transform: translateX(3px);
	}

	/* The menu button fills in with its own hue, the way the primary button
	   does, so the chip is obviously a control on hover. */
	.nav-toggle:hover {
		background-color: var(--tone-fill);
		border-color: var(--tone-fill);
		color: var(--white);
	}
}

@media (hover: none), (pointer: coarse) {
	.btn:active {
		transform: scale(0.98);
	}
}

/* The menu button wears a button's colours, so it moves like one. */
.nav-toggle {
	transition:
		background-color var(--dur-200) var(--ease),
		border-color var(--dur-200) var(--ease),
		color var(--dur-200) var(--ease);
}

/* -------------------------------------------------------------------------
   7. Header condensation — the surface firms up as the page scrolls
   The glass itself is the header's ::before layer, so that is what changes.
   ------------------------------------------------------------------------- */
.site-header::before {
	transition: background-color var(--dur-200) var(--ease);
}

/* -------------------------------------------------------------------------
   8. Cards, tiles, quotes, campus cards
   Nothing floats, so nothing lifts. The only pointer cue left is the media
   inside a card, which zooms a little.
   ------------------------------------------------------------------------- */
.card,
.tile,
.campus-card,
.quote,
.flip-card {
	transition: border-color var(--dur-200) var(--ease);
}

/* The pill table's row tint fades in like any other surface colour. */
.table--pills tbody tr > * {
	transition: background-color var(--dur-200) var(--ease);
}

@media (prefers-reduced-motion: no-preference) {
	.campus-card__media img,
	.tile__media img,
	.media-frame img {
		transition: transform var(--dur-300) var(--ease);
	}
}

@media (hover: hover) and (pointer: fine) {
	.campus-card:hover .campus-card__media img,
	.tile:hover .tile__media img,
	.media-frame:hover img {
		transform: scale(1.03);
	}

	.footer__link:hover {
		color: var(--text);
		text-decoration: underline;
	}
}

/* Inside the mobile sheet there is no sliding pill, so a link still needs a
   hover wash of its own: one step deeper than the tile it already sits on. */
@media (max-width: 61.99em) and (hover: hover) and (pointer: fine) {
	.nav__link:hover {
		background-color: var(--tone-edge);
		border-color: var(--tone-fill);
		color: var(--text);
	}
}

/* -------------------------------------------------------------------------
   9. Flip cards — rotation only, and only on an explicit toggle
   ------------------------------------------------------------------------- */
.flip-card__group {
	transition: transform var(--dur-flip) var(--ease-back);
}

/* -------------------------------------------------------------------------
   10. Accordion (native <details>) — icon rotation only
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
	.faq__icon {
		transition: transform var(--dur-200) var(--ease);
	}
}

/* -------------------------------------------------------------------------
   11. Mobile bottom-sheet navigation (slides up in 200ms)
   Same breakpoint as the sheet itself in base.css, and as MOBILE/DESKTOP in
   main.js: below it the nav is a sheet, above it the nav is the bar.
   ------------------------------------------------------------------------- */
@media (max-width: 61.99em) {
	.nav {
		transition:
			transform var(--dur-200) var(--ease),
			opacity var(--dur-200) var(--ease),
			visibility 0s linear var(--dur-200);
	}

	.nav.is-open {
		transition-delay: 0s;
	}

	.nav-backdrop {
		transition: opacity var(--dur-200) var(--ease);
	}
}

/* -------------------------------------------------------------------------
   12. Quick-action button — the floating menu
   The child actions spring out with a short stagger, and retract together so
   closing always feels quick.
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
	.fab__trigger {
		transition: transform var(--dur-300) var(--ease-spring);
	}

	.fab__action {
		transition:
			transform var(--dur-300) var(--ease-spring),
			opacity var(--dur-200) var(--ease);
	}

	.fab.is-open .fab__action {
		transition-delay: calc(var(--i, 0) * 40ms);
	}

	.fab.is-open .fab__trigger {
		transform: rotate(135deg);
	}

	/* Opacity on top of the visibility switch in components.css, so the menu
	   fades out of the way instead of blinking. */
	.fab {
		transition: opacity var(--dur-200) var(--ease);
	}
}

/* -------------------------------------------------------------------------
   13. Form feedback — a notification card that slides in
   ------------------------------------------------------------------------- */
.form-status {
	transition:
		opacity var(--dur-200) var(--ease),
		transform var(--dur-200) var(--ease);
}

.form-status:not(:empty) {
	animation: status-in var(--dur-300) var(--ease-back) both;
}

@keyframes status-in {
	from {
		opacity: 0;
		transform: translateY(-6px);
	}
	to {
		opacity: 1;
		transform: none;
	}
}

/* -------------------------------------------------------------------------
   14. Safety net — when a visitor asks for less motion, ship the end state.
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.001ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.001ms !important;
		scroll-behavior: auto !important;
	}

	.reveal,
	.page-enter {
		opacity: 1;
		transform: none;
	}

	.fab__action {
		transform: none;
	}
}
