/* ===================================================================
   Zerossin OS — Home Screen
   =================================================================== */

* { margin: 0; padding: 0; box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

:root {
	/* single source of truth: every app icon is exactly this square,
	   and every grid track (column width, row height) is derived from it —
	   this is what keeps icons from ever stretching and keeps widgets
	   aligned to a whole number of icon-cells, like a real OS home screen. */
	--icon: clamp(56px, 8vw, 124px);
	--gap: clamp(14px, 1.8vw, 28px);
}

body {
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
		"Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", sans-serif;
	color: rgba(255, 255, 255, 0.85);
	background: linear-gradient(180deg,
		#060814 0%,
		#0c1130 22%,
		#1a1f47 42%,
		#33305f 62%,
		#5c3f66 80%,
		#2c2a4a 100%);
	min-height: 100vh;
	position: relative;
}

a { color: inherit; }

/* ----------------------------------------------------------------
   Night sky — pure CSS stand-in for a photo background: scales to
   any viewport with no image download, so it stays cheap on mobile.
   Fixed behind the home screen (DOM order puts #os on top; no
   z-index needed since both are auto-stacked in that order).
   ---------------------------------------------------------------- */

.sky {
	position: fixed;
	inset: 0;
	overflow: hidden;
	pointer-events: none;
}

.sky-glow {
	position: absolute;
	inset: 0;
	background:
		radial-gradient(ellipse 60% 40% at 18% 100%, rgba(230, 110, 150, 0.35), transparent 70%),
		radial-gradient(ellipse 60% 40% at 85% 100%, rgba(90, 210, 190, 0.3), transparent 70%);
}

.sky-moon {
	position: absolute;
	top: 8%;
	left: 10%;
	width: clamp(46px, 6vw, 84px);
	height: clamp(46px, 6vw, 84px);
	border-radius: 50%;
	background: radial-gradient(circle at 35% 35%, #fdf6e3, #e8dcc4 60%, #cbb98f 100%);
	box-shadow: 0 0 50px 14px rgba(253, 246, 227, 0.25);
}

.sky-stars {
	position: absolute;
	inset: 0;
	background-image:
		radial-gradient(1.6px 1.6px at 8% 12%, #fff, transparent),
		radial-gradient(1.2px 1.2px at 22% 8%, #fff, transparent),
		radial-gradient(1.4px 1.4px at 35% 22%, #fff, transparent),
		radial-gradient(1px 1px at 48% 6%, #fff, transparent),
		radial-gradient(1.6px 1.6px at 58% 15%, #fff, transparent),
		radial-gradient(1.2px 1.2px at 68% 26%, #fff, transparent),
		radial-gradient(1px 1px at 76% 9%, #fff, transparent),
		radial-gradient(1.4px 1.4px at 85% 18%, #fff, transparent),
		radial-gradient(1.2px 1.2px at 93% 30%, #fff, transparent),
		radial-gradient(1px 1px at 12% 34%, #fff, transparent),
		radial-gradient(1.4px 1.4px at 42% 38%, #fff, transparent),
		radial-gradient(1px 1px at 63% 40%, #fff, transparent),
		radial-gradient(1.2px 1.2px at 90% 44%, #fff, transparent),
		radial-gradient(1px 1px at 28% 48%, #fff, transparent);
	opacity: 0.8;
}

.sky-hills {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 22vh;
}

.sky-hills::before,
.sky-hills::after {
	content: "";
	position: absolute;
	inset: 0;
}

.sky-hills::before {
	background: #241a3d;
	opacity: 0.9;
	clip-path: polygon(0 55%, 15% 42%, 35% 56%, 55% 38%, 75% 52%, 100% 40%, 100% 100%, 0 100%);
}

.sky-hills::after {
	background: #130d24;
	clip-path: polygon(0 75%, 20% 64%, 45% 78%, 70% 60%, 100% 72%, 100% 100%, 0 100%);
}

/* Fireflies — glowing dots that drift and flicker independently.
   `transform` (drift) and `opacity`/`filter` (flicker) are animated
   separately so the two loops don't fight over the same property. */

.firefly {
	position: absolute;
	left: var(--x);
	top: var(--y);
	width: 5px;
	height: 5px;
	border-radius: 50%;
	background: #d9f56e;
	box-shadow: 0 0 6px 2px rgba(217, 245, 110, 0.85), 0 0 16px 6px rgba(217, 245, 110, 0.35);
	opacity: 0.5;
	animation:
		firefly-drift var(--dur, 9s) ease-in-out infinite,
		firefly-flicker calc(var(--dur, 9s) / 2.2) ease-in-out infinite;
	animation-delay: var(--delay, 0s), var(--delay, 0s);
	will-change: transform, opacity;
}

.firefly--pink {
	background: #f6a6d9;
	box-shadow: 0 0 6px 2px rgba(246, 166, 217, 0.85), 0 0 16px 6px rgba(246, 166, 217, 0.35);
}

.firefly--cyan {
	background: #8ff0e0;
	box-shadow: 0 0 6px 2px rgba(143, 240, 224, 0.85), 0 0 16px 6px rgba(143, 240, 224, 0.35);
}

@keyframes firefly-drift {
	0% { transform: translate(0, 0); }
	25% { transform: translate(10px, -16px); }
	50% { transform: translate(-8px, -6px); }
	75% { transform: translate(12px, 8px); }
	100% { transform: translate(0, 0); }
}

@keyframes firefly-flicker {
	0%, 100% { opacity: 0.15; filter: brightness(0.8); }
	50% { opacity: 1; filter: brightness(1.3); }
}

@media (prefers-reduced-motion: reduce) {
	.firefly { animation: none; opacity: 0.75; }
}

img { max-width: 100%; display: block; }

button {
	font: inherit;
	color: inherit;
	background: none;
	border: none;
	cursor: pointer;
}

/* ----------------------------------------------------------------
   Frame
   ---------------------------------------------------------------- */

#os {
	max-width: 1560px;
	margin: 0 auto;
	padding: 0 clamp(16px, 3vw, 48px) 64px;
}

.os-statusbar {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: clamp(18px, 2vw, 28px) 6px clamp(26px, 3vw, 44px);
	font-size: clamp(0.85rem, 1vw, 1.05rem);
	font-weight: 600;
	color: rgba(255, 255, 255, 0.75);
	letter-spacing: 0.02em;
}

.os-footer {
	margin-top: 56px;
	text-align: center;
	font-size: 0.75rem;
	color: rgba(255, 255, 255, 0.45);
}

.os-footer a { text-decoration: none; }
.os-footer a:hover { text-decoration: underline; }

.os-clock.os-done-btn {
	cursor: pointer;
	font-weight: 800;
	color: #6cb0ff;
}

/* ----------------------------------------------------------------
   Home Grid — every element occupies grid units
   ---------------------------------------------------------------- */

.home-grid {
	display: grid;
	/* Column COUNT is fixed per breakpoint (see media queries below) rather than
	   fluid auto-fill — real OS home screens re-flow into a different number of
	   columns at a few discrete sizes, they don't shrink icons continuously
	   forever before finally changing the arrangement. Icon SIZE (--icon) is a
	   single continuous formula shared by every tier, so crossing a breakpoint
	   never makes icons visibly jump — only the column count changes.
	   row height is left to size itself off actual content (icon + gap + label),
	   so a row is always exactly "one icon tall" no matter the viewport width */
	grid-template-columns: repeat(6, 1fr);
	grid-auto-rows: auto;
	/* dense가 아니라 기본(순서대로) 배치 — 재배치 드래그가 "DOM 순서상 어디에
	   끼워 넣을지"만 정하고 나머지는 grid가 알아서 자리를 계산하게 하는데,
	   dense를 쓰면 이후 항목이 앞의 빈 칸을 채우려고 순서를 건너뛸 수 있어
	   "커서 위치 → DOM 순서" 판단이 어긋날 수 있다. 대신 큰 항목 옆에 자투리
	   칸이 남으면(예: 위젯 하나만 있는 줄) 그 칸은 그냥 비워두고 다음 항목은
	   다음 줄부터 시작 — 이미지 옆에 글이 흐르다 안 맞으면 다음 줄로 넘어가는
	   것과 같은 원리, 예측 가능함이 우선이라 이 쪽을 택함. */
	gap: var(--gap);
	justify-items: stretch;
	align-items: stretch;
}

@media (max-width: 767px) {
	.home-grid { grid-template-columns: repeat(5, 1fr); }
}

@media (max-width: 479px) {
	.home-grid { grid-template-columns: repeat(4, 1fr); }
}

/* ----------------------------------------------------------------
   Widgets are not a special grid citizen — they're just a big .app:
   the same wrapper (icon-slot + .app-label below it) as every other
   app, just spanning more than 1x1. .widget-slot carries the grid
   span (the "how many app-cells wide/tall" part); .widget is the
   visible card that sits where an .app-icon normally would, and it
   picks up an .app-label underneath exactly like any other app —
   so the label's own gap/line-height IS the row's real content,
   no separate approximation needed to match icon-row spacing.
   3 columns x 2 rows on desktop, full-width x 1 row on narrow screens.
   ---------------------------------------------------------------- */

.widget-slot {
	grid-column: span 3;
	grid-row: span 2;
}

.widget {
	/* .app centers its child at the child's own (shrink-to-fit) size —
	   right for a fixed-size .app-icon, wrong here. align-self:stretch
	   fills the wrapper's width instead, correctly leaving room for the
	   margin-inline below (unlike width:100%, which would overflow by
	   2x the margin since it doesn't know about it). flex:1 does the same
	   job for HEIGHT — without it, whichever widget has less content just
	   renders shorter than its slot instead of matching its sibling, since
	   the two widget-slots share the same grid row height but nothing was
	   telling the card itself to fill it. */
	align-self: stretch;
	flex: 1 1 auto;
	min-height: 0;
	/* An app-icon is a FIXED size, smaller than its grid column, and sits
	   centered in the leftover space — so a column's visible content doesn't
	   reach the column's true track edge. A widget card, with no inset of
	   its own, was filling its track edge-to-edge, so it stuck out past
	   where an icon's own right/left edge actually sits. This computes that
	   same per-column inset — (columnWidth − icon) / 2 — from the widget's
	   OWN resolved width (100% here = this widget's k-column span), and
	   applies it as the widget's outer margin so both edges land exactly on
	   the icon edges, for any span width k: solve columnWidth from
	   100% = k·columnWidth + (k−1)·gap, then inset = (columnWidth − icon)/2. */
	margin-inline: calc((100% - 2 * var(--gap) - 3 * var(--icon)) / 6);
	border-radius: clamp(26px, 2.6vw, 38px);
	padding: clamp(18px, 2.2vw, 34px);
	overflow: hidden;
	background: linear-gradient(150deg, rgba(255, 255, 255, 0.72), rgba(255, 255, 255, 0.45));
	-webkit-backdrop-filter: blur(18px) saturate(1.5);
	backdrop-filter: blur(18px) saturate(1.5);
	border: 1px solid rgba(255, 255, 255, 0.75);
	box-shadow:
		0 12px 28px rgba(29, 29, 31, 0.08),
		inset 0 1px 0 rgba(255, 255, 255, 0.9);
}

.widget-title {
	font-size: clamp(0.72rem, 0.85vw, 0.9rem);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: rgba(29, 29, 31, 0.45);
	margin-bottom: 12px;
}

/* About widget */

.widget-about {
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 10px;
}

.about-head {
	display: flex;
	align-items: center;
	gap: 14px;
}

.about-avatar {
	width: clamp(64px, 7vw, 104px);
	height: clamp(64px, 7vw, 104px);
	border-radius: 50%;
	object-fit: cover;
	flex: none;
	border: 2px solid rgba(255, 255, 255, 0.9);
	box-shadow: 0 4px 10px rgba(29, 29, 31, 0.12);
}

.about-name {
	font-size: clamp(1.25rem, 2vw, 1.9rem);
	font-weight: 800;
	letter-spacing: -0.01em;
	color: #1d1d1f;
}

.about-intro {
	font-size: clamp(0.82rem, 1.05vw, 1.02rem);
	line-height: 1.45;
	color: rgba(29, 29, 31, 0.65);
}

.about-links {
	display: flex;
	gap: 8px;
}

.about-links a {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	font-size: clamp(0.75rem, 0.85vw, 0.92rem);
	font-weight: 600;
	text-decoration: none;
	color: rgba(29, 29, 31, 0.75);
	background: rgba(255, 255, 255, 0.65);
	border: 1px solid rgba(29, 29, 31, 0.08);
	border-radius: 999px;
	padding: 0.35em 0.9em;
	transition: transform 0.15s ease, background 0.15s ease;
}

.about-links a:hover {
	transform: scale(1.05);
	background: rgba(255, 255, 255, 0.95);
}

.about-links svg {
	width: 1.15em;
	height: 1.15em;
	fill: currentColor;
}

/* Skill widget */

.widget-skill {
	display: flex;
	flex-direction: column;
	justify-content: center;
}

.skill-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: clamp(10px, 1.5vw, 24px) clamp(8px, 1vw, 16px);
}

.skill-item {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
}

.skill-item img {
	width: clamp(40px, 4.2vw, 68px);
	height: clamp(40px, 4.2vw, 68px);
	object-fit: contain;
	border-radius: 22.5%;
	background: rgba(255, 255, 255, 0.75);
	padding: clamp(6px, 0.7vw, 11px);
	border: 1px solid rgba(29, 29, 31, 0.06);
	box-shadow: 0 2px 6px rgba(29, 29, 31, 0.07);
}

.skill-item span {
	font-size: clamp(0.66rem, 0.75vw, 0.82rem);
	font-weight: 600;
	color: rgba(29, 29, 31, 0.6);
	white-space: nowrap;
}

/* ----------------------------------------------------------------
   App icons — every app is exactly 1 x 1
   ---------------------------------------------------------------- */

.app {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 7px;
	text-decoration: none;
	-webkit-tap-highlight-color: transparent;
	/* img/a의 브라우저 기본 드래그(끌어서 빼기)가 커스텀 재배치 드래그와
	   충돌하는 걸 막는 CSS 쪽 안전장치 (JS의 draggable=false / dragstart
	   preventDefault와 이중으로 걸어둠) */
	-webkit-user-drag: none;
	user-select: none;
	-webkit-user-select: none;
}

.app img {
	-webkit-user-drag: none;
}

.app-icon {
	/* fixed square, always — never a percentage of a fluid parent, so it
	   can't be stretched/squished by grid or flex recalculating around it */
	width: var(--icon);
	height: var(--icon);
	flex: none;
	border-radius: 22.5%;
	overflow: hidden;
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	background: linear-gradient(150deg, rgba(255, 255, 255, 0.85), rgba(255, 255, 255, 0.55));
	border: 1px solid rgba(255, 255, 255, 0.8);
	box-shadow:
		0 8px 18px rgba(29, 29, 31, 0.1),
		inset 0 1px 0 rgba(255, 255, 255, 0.85);
	transition: transform 0.18s ease, box-shadow 0.18s ease, filter 0.18s ease;
}

.app-icon svg {
	width: 58%;
	height: 58%;
}

.app-icon > img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.app:hover .app-icon {
	transform: scale(1.08);
	box-shadow: 0 12px 24px rgba(29, 29, 31, 0.14);
}

.app:active .app-icon {
	transform: scale(0.92);
	filter: blur(0.6px) brightness(0.96);
}

.app-label {
	font-size: clamp(0.74rem, 0.95vw, 0.95rem);
	font-weight: 600;
	color: #1d1d1f;
	text-align: center;
	max-width: 100%;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* Home-screen icons sit directly on the dark night sky, so their
   labels need light text — but this same .app-label class is reused
   inside folder windows (.win-app-grid), which are light cards, so
   that context keeps the dark default above instead. */
.home-grid .app-label {
	color: rgba(255, 255, 255, 0.9);
	text-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
}

/* ----------------------------------------------------------------
   Jiggle mode — long-press an icon to rearrange, like a real OS.
   Widgets are just big apps, so they jiggle/drag/reflow exactly like
   any other app — same rotation, just a smaller angle, since `rotate`
   pivots around the element's own center and a widget's edges sit much
   further from that center than a 1x1 icon's do (same angle → much more
   linear travel at the corners). Scaling the amplitude down keeps the
   apparent wobble similar regardless of size — not a different
   animation, just a smaller number plugged into the same one. */

@keyframes icon-jiggle {
	0%, 100% { rotate: -1.4deg; }
	50% { rotate: 1.4deg; }
}

.home-grid.jiggle > .app {
	animation: icon-jiggle 0.16s ease-in-out infinite;
	touch-action: none; /* 흔들기 모드에서만 터치 스크롤을 막아 드래그와 안 겹치게 */
}

.home-grid.jiggle > .app.widget-slot {
	animation-name: icon-jiggle-wide;
}

@keyframes icon-jiggle-wide {
	0%, 100% { rotate: -0.45deg; }
	50% { rotate: 0.45deg; }
}

.home-grid.jiggle > .app:nth-child(even) {
	animation-delay: -0.08s;
}

.home-grid.jiggle > .app.dragging {
	animation: none;
	z-index: 5;
	filter: brightness(1.04);
}

.home-grid.jiggle > .app.dragging .app-icon {
	box-shadow: 0 16px 30px rgba(29, 29, 31, 0.22);
}

@media (prefers-reduced-motion: reduce) {
	.home-grid.jiggle > .app { animation: none; }
}

/* Folder icon: same outline as an app icon, 3x3 mini apps inside */

.app-icon.is-folder {
	background: linear-gradient(150deg, rgba(255, 255, 255, 0.6), rgba(240, 241, 244, 0.45));
	-webkit-backdrop-filter: blur(14px) saturate(1.4);
	backdrop-filter: blur(14px) saturate(1.4);
	padding: 11%;
}

.folder-minis {
	width: 100%;
	height: 100%;
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	grid-template-rows: repeat(3, 1fr);
	gap: 7%;
}

.folder-minis .mini {
	border-radius: 22%;
	overflow: hidden;
	background: rgba(255, 255, 255, 0.85);
	box-shadow: 0 1px 2px rgba(29, 29, 31, 0.12);
	display: flex;
	align-items: center;
	justify-content: center;
}

.folder-minis .mini img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.folder-minis .mini.letter {
	font-size: 8px;
	font-weight: 800;
	color: #fff;
}

.folder-minis .mini.empty {
	background: rgba(29, 29, 31, 0.05);
	box-shadow: none;
}

/* ----------------------------------------------------------------
   OS Window
   ---------------------------------------------------------------- */

.os-modal {
	position: fixed;
	inset: 0;
	z-index: 100;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 20px;
}

.os-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(230, 231, 235, 0.35);
	-webkit-backdrop-filter: blur(10px) saturate(1.2);
	backdrop-filter: blur(10px) saturate(1.2);
	opacity: 0;
	transition: opacity 0.3s ease;
}

.os-modal.open .os-backdrop { opacity: 1; }

.os-window {
	position: relative;
	width: min(78vw, 1040px);
	max-height: min(80vh, 820px);
	display: flex;
	flex-direction: column;
	color: #1d1d1f;
	border-radius: clamp(22px, 1.6vw, 30px);
	background: linear-gradient(160deg, rgba(255, 255, 255, 0.92), rgba(250, 250, 252, 0.85));
	-webkit-backdrop-filter: blur(24px) saturate(1.6);
	backdrop-filter: blur(24px) saturate(1.6);
	border: 1px solid rgba(255, 255, 255, 0.85);
	box-shadow: 0 30px 70px rgba(29, 29, 31, 0.22);
	opacity: 0;
	transition: transform 0.34s cubic-bezier(0.32, 0.9, 0.35, 1), opacity 0.26s ease;
	will-change: transform, opacity;
}

.os-modal.open .os-window {
	transform: none !important;
	opacity: 1;
}

.os-window-bar {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: clamp(14px, 1.4vw, 22px) clamp(18px, 1.8vw, 28px);
	border-bottom: 1px solid rgba(29, 29, 31, 0.07);
	flex: none;
}

.os-window-bar .win-close {
	width: clamp(13px, 1vw, 16px);
	height: clamp(13px, 1vw, 16px);
	border-radius: 50%;
	background: #ff5f57;
	border: 1px solid rgba(0, 0, 0, 0.12);
	flex: none;
	transition: transform 0.15s ease;
}

.os-window-bar .win-close:hover { transform: scale(1.15); }

.os-window-bar h2 {
	font-size: clamp(0.92rem, 1.15vw, 1.15rem);
	font-weight: 700;
	flex: 1;
	text-align: center;
	/* keep the title optically centered despite the close dot */
	margin-right: 23px;
}

.os-window-body {
	padding: clamp(22px, 2.2vw, 36px);
	overflow-y: auto;
}

/* Folder window contents — same 1x1 app icons */

.win-app-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(var(--icon), 1fr));
	grid-auto-rows: auto;
	gap: clamp(24px, 2.4vw, 38px);
}

.win-app-grid .app-icon.letter-tile {
	font-size: clamp(1.5rem, 2vw, 2rem);
	font-weight: 800;
	color: #fff;
}

/* Gallery window */

.win-gallery {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(clamp(140px, 15vw, 220px), 1fr));
	gap: clamp(10px, 1.2vw, 18px);
}

.win-gallery img {
	width: 100%;
	aspect-ratio: 1;
	object-fit: cover;
	border-radius: 12px;
	cursor: zoom-in;
	transition: transform 0.15s ease;
}

.win-gallery img:hover { transform: scale(1.03); }

/* Lightbox — click a gallery photo to see it full-size. Sits above
   the folder/gallery modal, so it needs a higher z-index than
   .os-modal. */

.img-lightbox {
	position: fixed;
	inset: 0;
	z-index: 200;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 24px;
	background: rgba(6, 8, 20, 0.85);
	-webkit-backdrop-filter: blur(6px);
	backdrop-filter: blur(6px);
	opacity: 0;
	cursor: zoom-out;
	transition: opacity 0.2s ease;
}

.img-lightbox.open { opacity: 1; }

.img-lightbox img {
	max-width: 90vw;
	max-height: 90vh;
	object-fit: contain;
	border-radius: 12px;
	box-shadow: 0 30px 70px rgba(0, 0, 0, 0.5);
	transform: scale(0.94);
	transition: transform 0.2s ease;
}

.img-lightbox.open img { transform: scale(1); }

/* History window */

.win-timeline {
	list-style: none;
	position: relative;
	padding-left: 22px;
}

.win-timeline::before {
	content: "";
	position: absolute;
	left: 5px;
	top: 6px;
	bottom: 6px;
	width: 2px;
	background: rgba(29, 29, 31, 0.12);
	border-radius: 1px;
}

.win-timeline li {
	position: relative;
	padding: 0 0 18px;
}

.win-timeline li:last-child { padding-bottom: 0; }

.win-timeline li::before {
	content: "";
	position: absolute;
	left: -22px;
	top: 5px;
	width: 12px;
	height: 12px;
	border-radius: 50%;
	background: #fff;
	border: 3px solid #5f9c68;
	box-shadow: 0 1px 3px rgba(29, 29, 31, 0.15);
}

.win-timeline .when {
	font-size: 0.74rem;
	font-weight: 700;
	color: rgba(29, 29, 31, 0.45);
	letter-spacing: 0.03em;
}

.win-timeline .what {
	font-size: 0.92rem;
	font-weight: 600;
	margin-top: 2px;
}

/* Placeholder window (Canvas) */

.win-placeholder {
	text-align: center;
	padding: 36px 10px;
	color: rgba(29, 29, 31, 0.5);
	font-size: 0.92rem;
	line-height: 1.6;
}

.win-placeholder .big {
	font-size: 2rem;
	margin-bottom: 10px;
}

/* Cat window — a deliberately pointless app: a screen-saver-style room
   where the (already-animated) cat gif just wanders to a random spot and
   pulses bigger/smaller. No added color/rotation — the gif is doing
   plenty on its own, and tinting it turned out to look unsettling. */

.win-catstage {
	position: relative;
	min-height: min(52vh, 420px);
	border-radius: 16px;
	overflow: hidden;
	background: #ffffff;
	border: 1px solid rgba(29, 29, 31, 0.08);
}

/* party lighting behind the cats — a slowly spinning, blurred conic
   rainbow plus a couple of pulsing spotlight blobs. Lives on its own layer
   BEHIND the cats (added to the DOM first, so default stacking order puts
   it underneath) so it never tints the cats themselves. */

.win-catstage-lights {
	position: absolute;
	inset: -30%;
	background:
		radial-gradient(circle at 22% 28%, rgba(255, 40, 120, 0.75), transparent 38%),
		radial-gradient(circle at 78% 30%, rgba(255, 210, 30, 0.7), transparent 38%),
		radial-gradient(circle at 30% 78%, rgba(40, 220, 255, 0.7), transparent 38%),
		radial-gradient(circle at 78% 72%, rgba(150, 60, 255, 0.7), transparent 38%),
		conic-gradient(from 0deg, #ff2e63, #ffd23f, #35e08a, #2ec4ff, #b24cff, #ff2e63);
	filter: blur(34px) saturate(2.1);
	opacity: 0.7;
	animation: party-spin 6s linear infinite, party-pulse 1.6s ease-in-out infinite;
}

@keyframes party-spin {
	to { transform: rotate(360deg); }
}

@keyframes party-pulse {
	0%, 100% { opacity: 0.55; }
	50% { opacity: 0.85; }
}

.cat-dancer {
	position: absolute;
	width: 64px;
	pointer-events: none;
	/* sit above .win-catstage-lights regardless of paint order */
	z-index: 1;
	/* JS sets left/top to a new random point every so often, and a matching
	   transition-duration, so it glides there screen-saver style */
	transition-property: left, top;
	transition-timing-function: ease-in-out;
	transform: translate(-50%, -50%);
	animation-name: cat-pulse;
	animation-timing-function: ease-in-out;
	animation-iteration-count: infinite;
}

/* the individual `scale` property (not the transform shorthand) so it
   composes with the static transform:translate(-50%,-50%) centering above */
@keyframes cat-pulse {
	0%, 100% { scale: 1; }
	50% { scale: 1.4; }
}

@media (prefers-reduced-motion: reduce) {
	.cat-dancer { animation: none; transition: none; }
	.win-catstage-lights { animation: none; }
}

/* Home dims slightly while a window is open */

#os.window-open .home-grid {
	transform: scale(0.985);
}

.home-grid { transition: transform 0.34s ease; }

/* ----------------------------------------------------------------
   Narrow screens (5-col and 4-col tiers): widgets go full-width x 1 row
   and stack (About above Skill) instead of sitting side by side — a
   half-width 2-row box is too cramped for the avatar + intro + links.
   ---------------------------------------------------------------- */

@media (max-width: 767px) {
	.widget-slot {
		grid-column: span 5;
		grid-row: span 1;
	}

	.widget {
		/* k=5 here (this tier's widget spans all 5 columns) */
		margin-inline: calc((100% - 4 * var(--gap) - 5 * var(--icon)) / 10);
		padding: 12px 14px;
		border-radius: 22px;
	}

	.widget-title { margin-bottom: 8px; }

	.widget-about { gap: 6px; }

	.widget-about .widget-title { display: none; }

	.about-avatar { width: 46px; height: 46px; }

	.about-name { font-size: 1.05rem; }

	.about-intro { display: none; }

	.about-head { gap: 10px; flex: 1; }

	.widget-about {
		flex-direction: row;
		align-items: center;
	}

	.about-links { flex-direction: column; gap: 5px; }

	.about-links a { padding: 3px 9px; font-size: 0.68rem; }

	.skill-grid { grid-template-columns: repeat(6, 1fr); gap: 6px 4px; }

	.skill-item img { width: 30px; height: 30px; padding: 4px; }

	/* "PostgreSQL" etc. are wider than a 30px icon while nowrap — with 6
	   columns that forced the widget wider than its own grid-column span
	   (a CSS Grid item's default min-width:auto lets content overflow its
	   track). Icon-only in compact mode avoids the overflow entirely and
	   matches how real OS widgets don't caption every item anyway. */
	.skill-item span { display: none; }
}

@media (max-width: 479px) {
	:root {
		/* phones get their own fixed size class — bigger icon, tighter gap —
		   rather than inheriting the tablet tier's shared formula pinned at
		   its floor, which left too much dead space around each icon.
		   --gap lives here (not a one-off `.home-grid{gap:10px}`) so the
		   widget's margin-inline formula below picks up the same value. */
		--icon: clamp(64px, 17vw, 84px);
		--gap: 10px;
	}

	.widget-slot { grid-column: span 4; }

	.widget {
		/* k=4 here (this tier's widget spans all 4 columns) */
		margin-inline: calc((100% - 3 * var(--gap) - 4 * var(--icon)) / 8);
	}

	.skill-item img { width: 34px; height: 34px; }
}

@media (prefers-reduced-motion: reduce) {
	.os-window, .os-backdrop, .home-grid, .app-icon { transition: none !important; }
}
