<script lang="ts">
	import userIdentity from '../../stores/userIdentity';
	import {
		userIdentity as getUserIdentity,
		type AniListAuthorisation
	} from '$lib/AniList/identity';
	import { onMount } from 'svelte';
	import { tops, wrapped, type TopMedia } from '$lib/AniList/wrapped';
	import {
		fullActivityHistory,
		activityHistory as getActivityHistory
	} from '$lib/AniList/activity';
	import { Type, mediaListCollection, type Media } from '$lib/AniList/media';
	import anime from '../../stores/anime';
	import lastPruneTimes from '../../stores/lastPruneTimes';
	import manga from '../../stores/manga';
	import Error from '$lib/Error/RateLimited.svelte';
	import { domToBlob } from 'modern-screenshot';
	import { browser } from '$app/environment';
	import { page } from '$app/stores';
	import { clearAllParameters } from '../Utility/parameters';
	import { nbsp } from '../Utility/html';
	import { estimatedDayReading } from '$lib/Media/Manga/time';
	import ActivityHistoryGrid from './ActivityHistory/Grid.svelte';
	import SettingHint from '$lib/Settings/SettingHint.svelte';
	import { database } from '$lib/Database/activities';
	import MediaTitleDisplay from '$lib/List/MediaTitleDisplay.svelte';
	import proxy from '$lib/Utility/proxy';

	export let user: AniListAuthorisation;

	const currentYear = new Date(Date.now()).getFullYear();
	let currentUserIdentity = { name: '', id: -1 };
	let episodes = 0;
	let chapters = 0;
	let minutesWatched = 0;
	let animeList: Media[] | undefined = undefined;
	let mangaList: Media[] | undefined = undefined;
	let originalAnimeList: Media[] | undefined = undefined;
	let originalMangaList: Media[] | undefined = undefined;
	let transparency = false;
	let lightTheme = true;
	let watermark = false;
	let includeMusic = false;
	let includeSpecials = false;
	let includeRepeats = false;
	let width = 980;
	let lightMode = false;
	let highestRatedCount = 5;
	let genreTagCount = 5;
	let mounted = false;
	let generated = false;
	let disableActivityHistory = true;
	let excludedKeywordsInput = '';
	let excludedKeywords: string[] = [];
	let useFullActivityHistory = false;
	let topGenresTags = true;
	let topMedia: TopMedia;
	let highestRatedMediaPercentage = true;
	let highestRatedGenreTagPercentage = true;

	$: {
		if (browser && mounted) {
			$page.url.searchParams.set('transparency', transparency.toString());
			$page.url.searchParams.set('lightTheme', lightTheme.toString());
			$page.url.searchParams.set('watermark', watermark.toString());
			$page.url.searchParams.set('includeMusic', includeMusic.toString());
			$page.url.searchParams.set('includeSpecials', includeSpecials.toString());
			$page.url.searchParams.set('includeRepeats', includeRepeats.toString());
			$page.url.searchParams.set('lightMode', lightMode.toString());
			$page.url.searchParams.set('highestRatedCount', highestRatedCount.toString());
			$page.url.searchParams.set('genreTagCount', genreTagCount.toString());
			$page.url.searchParams.set('disableActivityHistory', disableActivityHistory.toString());
			$page.url.searchParams.set(
				'highestRatedMediaPercentage',
				highestRatedMediaPercentage.toString()
			);
			$page.url.searchParams.set(
				'highestRatedGenreTagPercentage',
				highestRatedGenreTagPercentage.toString()
			);

			history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
		}
	}
	$: {
		includeMusic = includeMusic;
		includeSpecials = includeSpecials;
		includeRepeats = includeRepeats;
		disableActivityHistory = disableActivityHistory;
		highestRatedMediaPercentage = highestRatedMediaPercentage;
		highestRatedGenreTagPercentage = highestRatedGenreTagPercentage;
		topGenresTags = topGenresTags;

		update().then(updateWidth).catch(updateWidth);
	}
	$: {
		animeList = animeList;
		mangaList = mangaList;
		highestRatedCount = highestRatedCount;

		new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth);
	}
	$: {
		genreTagCount = genreTagCount;

		if (animeList && mangaList)
			topMedia = tops(
				[...(animeList || []), ...(mangaList || [])],
				genreTagCount,
				excludedKeywords
			);

		new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth);
	}
	$: {
		excludedKeywords = excludedKeywords;

		if (excludedKeywords.length > 0 && animeList !== undefined && mangaList !== undefined) {
			animeList = originalAnimeList;
			mangaList = originalMangaList;
			animeList = excludeKeywords(animeList as Media[]);
			mangaList = excludeKeywords(mangaList as Media[]);
		}

		updateWidth();
	}

	const updateWidth = () => {
		const wrappedContainer = document.querySelector('#wrapped') as HTMLElement;

		if (!wrappedContainer) return;

		wrappedContainer.style.width = `1920px`;

		const reset = () => {
			let topWidths = 0;
			let middleWidths = 0;
			let bottomWidths = 0;

			wrappedContainer.querySelectorAll('.category').forEach((item) => {
				const category = item as HTMLElement;
				const style = window.getComputedStyle(category);
				const width =
					category.offsetWidth +
					parseFloat(style.marginLeft) +
					parseFloat(style.marginRight) +
					parseFloat(style.paddingLeft) +
					parseFloat(style.paddingRight) +
					parseFloat(style.borderLeftWidth) +
					parseFloat(style.borderRightWidth);

				if (category.classList.contains('top-category')) {
					topWidths += width;
				} else if (category.classList.contains('middle-category')) {
					middleWidths += width;
				} else if (category.classList.contains('bottom-category')) {
					bottomWidths += width;
				}
			});

			let requiredWidth = topWidths > middleWidths ? topWidths : middleWidths;

			if (!disableActivityHistory && bottomWidths > requiredWidth) requiredWidth = bottomWidths;

			wrappedContainer.style.width = `${requiredWidth}px`;
			width = requiredWidth;
		};

		reset();
		reset();
	};

	onMount(async () => {
		clearAllParameters([
			'transparency',
			'lightTheme',
			'watermark',
			'includeMusic',
			'includeSpecials',
			'includeRepeats',
			'forceDark',
			'highestRatedCount',
			'genreTagCount',
			'disableActivityHistory',
			'highestRatedMediaPercentage',
			'highestRatedGenreTagPercentage'
		]);

		if (browser) {
			transparency = $page.url.searchParams.get('transparency') === 'true';
			lightTheme = $page.url.searchParams.get('lightTheme') === 'true';
			watermark = $page.url.searchParams.get('watermark') === 'true';
			includeMusic = $page.url.searchParams.get('includeMusic') === 'true';
			includeSpecials = $page.url.searchParams.get('includeSpecials') === 'true';
			includeRepeats = $page.url.searchParams.get('includeRepeats') === 'true';
			lightMode = $page.url.searchParams.get('lightMode') === 'true';
			highestRatedCount = parseInt($page.url.searchParams.get('highestRatedCount') || '5', 10);
			genreTagCount = parseInt($page.url.searchParams.get('genreTagCount') || '5', 10);
			disableActivityHistory = $page.url.searchParams.get('disableActivityHistory') === 'true';
			highestRatedMediaPercentage =
				$page.url.searchParams.get('highestRatedMediaPercentage') === 'true';
			highestRatedGenreTagPercentage =
				$page.url.searchParams.get('highestRatedGenreTagPercentage') === 'true';
		}

		if (user !== undefined) {
			if ($userIdentity === '') userIdentity.set(JSON.stringify(await getUserIdentity(user)));

			currentUserIdentity = JSON.parse($userIdentity);
			currentUserIdentity.name = currentUserIdentity.name;
		} else currentUserIdentity.id = -2;

		await update().then(() => (mounted = true));
	});

	const update = async () => {
		if (currentUserIdentity.id === -1) return;

		animeList = (
			await mediaListCollection(
				user,
				currentUserIdentity,
				Type.Anime,
				$anime,
				$lastPruneTimes.anime,
				{
					forcePrune: true,
					includeCompleted: true,
					all: true
				}
			)
		)
			.filter(
				(item, index, self) =>
					self.findIndex((itemToCompare) => itemToCompare.id === item.id) === index &&
					(includeMusic ? true : item.format !== 'MUSIC') &&
					(includeRepeats
						? true
						: item.startDate.year === currentYear || item.endDate.year === currentYear
						? true
						: item.mediaListEntry?.repeat === 0) &&
					(item.mediaListEntry?.startedAt.year === currentYear ||
						item.mediaListEntry?.completedAt.year === currentYear)
			)
			.sort((a, b) => {
				if (a.mediaListEntry?.score === undefined) return 1;
				else if (b.mediaListEntry?.score === undefined) return -1;
				else return b.mediaListEntry?.score - a.mediaListEntry?.score;
			});
		mangaList = (
			await mediaListCollection(
				user,
				currentUserIdentity,
				Type.Manga,
				$manga,
				$lastPruneTimes.manga,
				{
					forcePrune: true,
					includeCompleted: true,
					all: true
				}
			)
		)
			.filter(
				(item, index, self) =>
					self.findIndex((itemToCompare) => itemToCompare.id === item.id) === index &&
					(includeRepeats ? true : item.mediaListEntry?.repeat === 0) &&
					(item.mediaListEntry?.startedAt.year === currentYear ||
						item.mediaListEntry?.completedAt.year === currentYear)
			)
			.sort((a, b) => {
				if (a.mediaListEntry?.score === undefined) return 1;
				else if (b.mediaListEntry?.score === undefined) return -1;
				else return b.mediaListEntry?.score - a.mediaListEntry?.score;
			});

		episodes = 0;
		minutesWatched = 0;
		chapters = 0;

		for (const media of animeList) {
			episodes += media.mediaListEntry?.progress || 0;
			minutesWatched += (media.mediaListEntry?.progress || 0) * media.duration || 0;
		}

		for (const media of mangaList) chapters += media.mediaListEntry?.progress || 0;
	};

	/* eslint-disable @typescript-eslint/no-explicit-any */
	// const year = (statistic: { startYears: any }) =>
	// 	statistic.startYears.find((y: { startYear: number }) => y.startYear === 2023);

	const screenshot = async () => {
		let element = document.querySelector('#wrapped') as HTMLElement;

		if (element !== null) {
			domToBlob(element, {
				backgroundColor: transparency ? 'transparent' : lightTheme ? '#edf1f5' : '#0b1622',
				quality: 1,
				scale: 2,
				fetch: {
					requestInit: {
						mode: 'cors'
					},
					bypassingCache: true
				}
			}).then((blob) => {
				const downloadWrapper = document.createElement('a');
				// const wrappedImageButton = document.getElementById(
				// 	'wrapped-image-download'
				// ) as HTMLAnchorElement;
				const image = document.createElement('img');
				const object = (window.URL || window.webkitURL || window || {}).createObjectURL(blob);

				// downloadWrapper.download = `due_dot_moe_wrapped_${dark ? 'dark' : 'light'}.png`;
				downloadWrapper.href = object;
				downloadWrapper.target = '_blank';
				image.src = object;

				downloadWrapper.appendChild(image);

				// if (wrappedImageButton !== null) {
				// 	wrappedImageButton.href = object;
				// }

				const wrappedFinal = document.getElementById('wrapped-final');

				if (wrappedFinal !== null) {
					wrappedFinal.innerHTML = '';

					wrappedFinal.appendChild(downloadWrapper);

					generated = true;
				}

				downloadWrapper.click();
			});
		}
	};

	// const abbreviate = (string: string, maxLength = 40, enabled = true) => {
	// 	if (!enabled) {
	// 		return string;
	// 	}

	// 	if (string.length <= maxLength) {
	// 		return string;
	// 	}

	// 	return string.slice(0, maxLength - 3) + ' …';
	// };

	const submitExcludedKeywords = () => {
		if (excludedKeywordsInput.length <= 0 && excludedKeywords.length > 0) {
			animeList = originalAnimeList;
			mangaList = originalMangaList;
			excludedKeywords = [];
		} else if (excludedKeywordsInput.length >= 0 && excludedKeywords.length <= 0) {
			originalAnimeList = animeList;
			originalMangaList = mangaList;
		}

		if (excludedKeywordsInput.length > 0)
			excludedKeywords = excludedKeywordsInput
				.split(',')
				.map((k) => k.trim())
				.filter((k) => k.length > 0);
	};

	const excludeKeywords = (media: Media[]) => {
		if (excludedKeywords.length <= 0) return media;

		return media.filter((m) => {
			for (const keyword of excludedKeywords) {
				if (m.title.english?.toLowerCase().includes(keyword.toLowerCase())) return false;
				if (m.title.romaji?.toLowerCase().includes(keyword.toLowerCase())) return false;
				if (m.title.native?.toLowerCase().includes(keyword.toLowerCase())) return false;
			}

			return true;
		});
	};

	const pruneFullYear = async () => {
		await database.activities.bulkDelete((await database.activities.toArray()).map((m) => m.page));
	};

	// const mergeArraySort = (a: any, b: any, mode: 'tags' | 'genres') => {
	// 	let merged = [...a, ...b].sort((a, b) => b.meanScore - a.meanScore);

	// 	merged = merged.filter(
	// 		(item, index, self) =>
	// 			self.findIndex((itemToCompare) =>
	// 				mode === 'genres'
	// 					? itemToCompare.genre === item.genre
	// 					: itemToCompare.tag.name === item.tag.name
	// 			) === index
	// 	);

	// 	return merged;
	// };

	// const randomCoverFromTop10 = (
	// 	statistics: { anime: any; manga: any },
	// 	mode: 'tags' | 'genres'
	// ) => {
	// 	const top = mergeArraySort(statistics.anime[mode], statistics.manga[mode], mode);

	// 	return mediaCover(top[Math.floor(Math.random() * top.length)].mediaIds[0]);
	// };
</script>

{#if currentUserIdentity.id === -2}
	Please log in to view this page.
{:else if currentUserIdentity.id !== -1}
	{#await useFullActivityHistory ? fullActivityHistory(user, currentUserIdentity, currentYear) : getActivityHistory(currentUserIdentity)}
		{@html nbsp(`Loading${useFullActivityHistory ? ' full-year' : ''} activity history ...`)}
	{:then activities}
		{#await wrapped(user, currentUserIdentity)}
			{@html nbsp('Loading user data ...')}
		{:then wrapped}
			<div
				id="wrapped"
				class:light-theme={lightMode}
				style={`width: ${width}px;`}
				class:transparent={transparency}
			>
				<div class="categories-grid" style="padding-bottom: 0;">
					<div class="grid-item image-grid avatar-grid category top-category">
						<a href={`https://anilist.co/user/${currentUserIdentity.name}`} target="_blank">
							<img src={proxy(wrapped.avatar.large)} alt="User Avatar" on:load={updateWidth} />
						</a>
						<div>
							<div>
								<a href={`https://anilist.co/user/${currentUserIdentity.name}`} target="_blank">
									<b>
										{currentUserIdentity.name}
									</b>
								</a>
							</div>
							<div>
								Status Posts: {wrapped.activities.statusCount}
							</div>
							<div>
								Messages: {wrapped.activities.messageCount}
							</div>
							<div>
								Days Active: {activities.length}/{useFullActivityHistory ? 365 : 189}
							</div>
						</div>
					</div>
					<div class="category-grid pure-category category top-category">
						<div class="grid-item">
							<b>Anime</b>
						</div>
						<div class="grid-item">
							Time Watched: {((minutesWatched || 0) / 60 / 24).toFixed(2)} days
						</div>
						<div class="grid-item">
							Completed: {animeList?.length}
						</div>
						<div class="grid-item">Episodes: {episodes}</div>
					</div>
					<div class="category-grid pure-category category top-category">
						<div class="grid-item">
							<b>Manga</b>
						</div>
						<div class="grid-item">
							Time Read: {estimatedDayReading(chapters).toFixed(2)} days
						</div>
						<div class="grid-item">
							Completed: {mangaList?.length}
						</div>
						<div class="grid-item">
							Chapters: {chapters}
						</div>
					</div>
				</div>
				{#if animeList !== undefined || mangaList !== undefined}
					<div class="categories-grid">
						{#if animeList !== undefined && animeList.length !== 0}
							<div class="category-grid pure-category category middle-category">
								<div class="grid-item image-grid">
									<a href={`https://anilist.co/anime/${animeList[0].id}`} target="_blank">
										<img
											src={proxy(animeList[0].coverImage.extraLarge)}
											alt="Highest Rated Anime Cover"
											class="cover-image"
											on:load={updateWidth}
										/>
									</a>
									<div>
										<b>Highest Rated Anime</b>
										<ol>
											{#each animeList?.slice(0, highestRatedCount) as anime}
												<li>
													<a href={`https://anilist.co/anime/${anime.id}`} target="_blank">
														<MediaTitleDisplay title={anime.title} />
													</a>
													{highestRatedMediaPercentage ? `: ${anime.mediaListEntry?.score}%` : ''}
												</li>
											{/each}
										</ol>
									</div>
								</div>
							</div>
						{/if}
						{#if mangaList !== undefined && mangaList.length !== 0}
							<div class="category-grid pure-category category middle-category">
								<div class="grid-item image-grid">
									<a href={`https://anilist.co/manga/${mangaList[0].id}`} target="_blank">
										<img
											src={proxy(mangaList[0].coverImage.extraLarge)}
											alt="Highest Rated Manga Cover"
											class="cover-image"
											on:load={updateWidth}
										/>
									</a>
									<div>
										<b>Highest Rated Manga</b>
										<ol>
											{#each mangaList?.slice(0, highestRatedCount) as manga}
												<li>
													<a href={`https://anilist.co/manga/${manga.id}`} target="_blank">
														<MediaTitleDisplay title={manga.title} />
													</a>
													{highestRatedMediaPercentage ? `: ${manga.mediaListEntry?.score}%` : ''}
												</li>
											{/each}
										</ol>
									</div>
								</div>
							</div>
						{/if}
					</div>
				{/if}
				{#if topMedia && topGenresTags && ((topMedia.topGenreMedia && topMedia.genres.length > 0) || (topMedia.topTagMedia && topMedia.tags.length > 0))}
					<div class="categories-grid" style="padding-top: 0;">
						{#if topMedia.topGenreMedia && topMedia.genres.length > 0}
							<div class="category-grid pure-category category">
								<div class="grid-item image-grid">
									<a
										href={`https://anilist.co/${topMedia.topGenreMedia.type.toLowerCase()}/${
											topMedia.topGenreMedia.id
										}`}
										target="_blank"
									>
										<img
											src={proxy(topMedia.topGenreMedia.coverImage.extraLarge)}
											alt="Highest Rated Genre Cover"
											class="cover-image"
											on:load={updateWidth}
										/>
									</a>
									<div>
										<b>Highest Rated Genres</b>
										<ol>
											{#each topMedia.genres as genre}
												<li>
													<a
														href={`https://anilist.co/search/anime?genres=${genre.genre}`}
														target="_blank"
													>
														{genre.genre}{highestRatedGenreTagPercentage
															? `: ${genre.averageScore}%`
															: ''}
													</a>
												</li>
											{/each}
										</ol>
									</div>
								</div>
							</div>
						{/if}
						{#if topMedia.topTagMedia && topMedia.tags.length > 0}
							<div class="category-grid pure-category category">
								<div class="grid-item image-grid">
									<a
										href={`https://anilist.co/${topMedia.topTagMedia.type.toLowerCase()}/${
											topMedia.topTagMedia.id
										}`}
										target="_blank"
									>
										<img
											src={proxy(topMedia.topTagMedia.coverImage.extraLarge)}
											alt="Highest Rated Tag Cover"
											class="cover-image"
											on:load={updateWidth}
										/>
									</a>
									<div>
										<b>Highest Rated Tags</b>
										<ol>
											{#each topMedia.tags as tag}
												<li>
													<a
														href={`https://anilist.co/search/anime?genres=${tag.tag}`}
														target="_blank"
													>
														{tag.tag}{highestRatedGenreTagPercentage
															? `: ${tag.averageScore}%`
															: ''}
													</a>
												</li>
											{/each}
										</ol>
									</div>
								</div>
							</div>
						{/if}
					</div>
				{/if}
				{#if !disableActivityHistory}
					<div class="categories-grid" style="padding-top: 0;">
						<div class="category-grid bottom-category pure-category category">
							<div id="activity-history">
								<ActivityHistoryGrid {user} activityData={activities} />
							</div>
						</div>
					</div>
				{/if}
				{#if watermark}
					<div class="categories-grid" style="padding-top: 0;">
						<div class="category-grid pure-category" id="watermark">
							<a href="https://due.moe/wrapped" target="_blank">due.moe/wrapped</a>
						</div>
					</div>
				{/if}
			</div>

			<p />

			<p>
				<a href={'#'} on:click={screenshot} data-umami-event="Generate Wrapped">Generate image</a>
			</p>

			<details open>
				<summary>Options</summary>
				<div id="options">
					<details open>
						<summary>Display</summary>

						<input type="checkbox" bind:checked={watermark} /> Show watermark<br />
						<input type="checkbox" bind:checked={transparency} /> Enable background transparency<br
						/>
						<input type="checkbox" bind:checked={lightMode} />
						Enable light mode<br />
						<input type="checkbox" bind:checked={topGenresTags} />
						Show top genres and tags<br />
						<input type="checkbox" bind:checked={disableActivityHistory} /> Hide activity history<br
						/>
						<input type="checkbox" bind:checked={highestRatedMediaPercentage} /> Show highest rated
						media percentages<br />
						<input type="checkbox" bind:checked={highestRatedGenreTagPercentage} /> Show highest
						rated genre and tag percentages<br />
						<select bind:value={highestRatedCount}>
							{#each [3, 4, 5, 6, 7, 8, 9, 10] as count}
								<option value={count}>{count}</option>
							{/each}
						</select>
						Highest rated media count<br />
						<select bind:value={genreTagCount}>
							{#each [3, 4, 5, 6, 7, 8, 9, 10] as count}
								<option value={count}>{count}</option>
							{/each}
						</select>
						Highest genre and tag count<br />
						<button on:click={updateWidth}>Find best fit</button>
						<button on:click={() => (width -= 25)}>-25px</button>
						<button on:click={() => (width += 25)}>+25px</button>
						Width adjustment<br />
					</details>

					<p />

					<details open>
						<summary>Calculation</summary>

						<input type="checkbox" bind:checked={useFullActivityHistory} />
						Enable full-year activity<br />
						<SettingHint>
							If you have many activities, you may rate-limited and you may need multiple attempts
							to fully populate your local activity history database.<br />If you get rate-limited,
							wait one minute, then try again.
							<a href={'#'} on:click={pruneFullYear}>Refresh data</a>
						</SettingHint><br />
						<p />
						<input type="checkbox" bind:checked={includeMusic} /> Include music<br />
						<input type="checkbox" bind:checked={includeRepeats} /> Include rewatches & rereads<br
						/>
						<input type="checkbox" bind:checked={includeSpecials} /> Include specials and OVAs<br />
						<input
							type="text"
							bind:value={excludedKeywordsInput}
							on:keypress={(e) => {
								e.key === 'Enter' && submitExcludedKeywords();
							}}
						/>
						Excluded keywords
						<a href={`#`} on:click={submitExcludedKeywords} title="Or click your Enter key"
							>Submit</a
						>
						<br />
						<SettingHint>Comma separated list (e.g., "My Hero, Kaguya")</SettingHint>
					</details>
				</div>
			</details>

			<p />

			<div id="wrapped-final" />

			{#if generated}
				<p />

				<blockquote>
					Click on the image to download, or right click and select "Save Image As...".
				</blockquote>
			{/if}
		{:catch}
			<Error />
		{/await}
	{:catch}
		<Error
			type={`${useFullActivityHistory ? 'Full-year activity' : 'Activity'} history`}
			loginSessionError={!useFullActivityHistory}
		/>
	{/await}
{:else}
	{@html nbsp('Loading user identity ...')}
{/if}

<style>
	@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700');
	@import url('https://fonts.googleapis.com/css?family=Overpass:400,600,700,800');

	.categories-grid {
		display: flex;
		flex-wrap: wrap;
		row-gap: 1.5em;
		column-gap: 1.5em;
		padding: 2%;
		justify-content: center;
		font-family: Roboto, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Cantarell,
			Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
		background-color: #0b1622;
	}

	.categories-grid b {
		font-family: Overpass, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Cantarell,
			Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
		font-weight: 600;
	}

	.category-grid,
	.image-grid {
		background-color: #151f2e;
		border-radius: 4px;
		color: rgb(159, 173, 189);
	}

	.pure-category,
	.avatar-grid {
		padding: 1.5%;
	}

	.category-grid {
		display: grid;
	}

	.image-grid {
		display: flex;
		column-gap: 1em;
		flex-wrap: wrap;
	}

	.image-grid img {
		width: 6em;
		height: auto;
		border-radius: 3px;
	}

	.categories-grid a {
		text-decoration: none;
		color: unset;
	}

	.transparent .categories-grid {
		background-color: transparent !important;
	}

	.light-theme .categories-grid {
		background-color: #edf1f5;
	}

	.light-theme .category-grid {
		background-color: #fafafa;
		color: rgb(92, 114, 138);
	}

	.light-theme .image-grid {
		background-color: #fafafa;
		color: rgb(92, 114, 138);
	}

	ol {
		margin: 0 !important;
	}

	#watermark {
		color: rgb(61, 180, 242);
	}

	#wrapped-final {
		height: auto;
		width: 50%;
	}
</style>
