/*
Theme Name: Nightcord Fansite
Theme URI: https://25nightcord.com
Author: 25nightcord.com
Description: PJSK 同人 blog 主题：Unit/角色分类归档、明亮极简配色、角色专页。
Version: 0.2.0
Requires at least: 6.4
Requires PHP: 8.0
Text Domain: nightcord-fansite
*/

:root {
	--bg: #ffffff;
	--bg-secondary: #f5f5f7;
	--text: #1d1d1f;
	--text-secondary: #424245;
	--text-muted: #86868b;
	--border: #d2d2d7;
	--accent: #0071e3;

	--unit-leoneed: #0099ff;
	--unit-mmj: #ff6b9d;
	--unit-vbs: #ff4444;
	--unit-wls: #ffd700;
	--unit-niigo: #9933ff;
}

* {
	box-sizing: border-box;
}

html,
body {
	margin: 0;
	padding: 0;
}

body {
	background: var(--bg);
	color: var(--text-secondary);
	font-family: "Space Grotesk", "PingFang SC", "Hiragino Sans", "Noto Sans JP", "Noto Sans SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
	font-size: 18px;
	font-weight: 300;
	line-height: 1.6;
	-webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4 {
	color: var(--text);
	font-family: "Playfair Display", "Noto Serif JP", "Hiragino Mincho ProN", "Yu Mincho", serif;
	font-weight: 600;
	line-height: 1.125;
	letter-spacing: normal;
}

/* UI chrome (nav, tags, titles, buttons) sets its own link color per
   element below and stays non-underlined. Plain body-copy links — inside
   post content and comments, where there's no other visual cue that text
   is a link — get an explicit underline instead; see the two rules near
   the bottom of this file. */
a {
	color: var(--accent);
	text-decoration: none;
}

a:hover {
	text-decoration: underline;
}

.single-post-content a,
.comment-content a {
	color: inherit;
	text-decoration: underline;
}

.single-post-content a:hover,
.comment-content a:hover {
	text-decoration: none;
}

code,
pre,
.pns-meta {
	font-family: "JetBrains Mono", monospace;
}

/* Left-anchored, not centered — lines up with the header title on the
   left, and deliberately leaves the right side of the page empty (space
   reserved for a future right rail, à la the header's Unit nav corner). */
.site-wrap {
	max-width: 680px;
	margin: 0;
	/* Left gutter scales with viewport (same clamp() approach as
	   .timeline-item's vertical spacing below, mirroring how apple.com
	   scales its own layout gutters) instead of jumping at a fixed
	   breakpoint — still tops out at the original 122px on wide screens. */
	padding: 0 32px 0 clamp(24px, 9vw, 122px);
}

/* Single posts have no reason to reserve the right-rail gutter — there's
   no list of other items beside them, just one article to read — so its
   content fills all the way up to the same global reserved-rail boundary
   every other page respects (the #content::after line below), instead of
   stopping at the fixed 680px reading-column width. That line itself
   stays visible here too — it's a global boundary, not single-post-only. */
.single-post .site-wrap {
	max-width: none;
	padding-right: calc(32% + 40px);
}

/* #content is the full-width ancestor of the (narrow) main .site-wrap —
   it exists so the reserved-right-rail marker line below can measure
   "720px" from the true right edge of the viewport while still stretching
   its height to match the narrow content column's actual height exactly
   (top:0/bottom:0 on an absolutely-positioned child fills its positioned
   ancestor, whatever that height turns out to be). */
#content {
	position: relative;
}

#content::after {
	content: "";
	position: absolute;
	top: 0;
	/* .site-footer's margin-top (60px) sits outside #content's own box —
	   extend past it so the line reaches the footer's border instead of
	   stopping in the empty gap before it. */
	bottom: -60px;
	/* Percentage, not a fixed px offset — was right:720px, which pushed the
	   line off-screen entirely on narrow viewports. Shifted one more "tab"
	   to the right (i.e. closer to center) from the previous position. */
	right: 32%;
	width: 1px;
	background: var(--border);
	pointer-events: none;
}

/* The header spans the full viewport — title flush against the true left
   edge, Unit nav flush against the true right edge — instead of sharing
   the centered reading column, so the gap between them scales with the
   window instead of stopping at a fixed content width. */
.site-header .site-wrap {
	max-width: none;
	padding: 0 32px;
}

/* Header: title left, Unit options right, one row */
.site-header {
	position: relative;
	padding: 20px 0 21px;
}

/* The divider stops well short of the viewport edges instead of running
   full-bleed — a centered, width-capped line under the header. Width is a
   percentage (not 100%) so the inset stays proportional on narrow
   viewports too, instead of only kicking in once the fixed max-width cap
   is reached (which meant it went edge-to-edge, full-bleed, on mobile). */
.site-header::after {
	content: "";
	position: absolute;
	bottom: 0;
	left: 50%;
	transform: translateX(-50%);
	width: 85%;
	max-width: 1260px;
	border-bottom: 1px solid var(--border);
}

.site-header .site-wrap {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: 16px;
}


.site-header .site-title {
	font-family: "Playfair Display", "Noto Serif JP", "Hiragino Mincho ProN", "Yu Mincho", serif;
	font-size: 2.1rem;
	font-weight: 700;
	letter-spacing: normal;
	margin: 0;
}

.site-header .site-title a {
	color: var(--text);
}

.screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

/* Hidden on desktop — the unit list has room to sit inline next to the
   title there. Only shown (as a button that opens .unit-nav as a dropdown)
   once the breakpoint below hides the inline list instead. */
.unit-nav-toggle {
	display: none;
	background: none;
	border: none;
	padding: 8px;
	cursor: pointer;
}

.unit-nav-toggle-bar,
.unit-nav-toggle-bar::before,
.unit-nav-toggle-bar::after {
	display: block;
	width: 20px;
	height: 2px;
	background: var(--text);
}

.unit-nav-toggle-bar {
	position: relative;
}

.unit-nav-toggle-bar::before,
.unit-nav-toggle-bar::after {
	content: "";
	position: absolute;
	left: 0;
}

.unit-nav-toggle-bar::before {
	top: -6px;
}

.unit-nav-toggle-bar::after {
	top: 6px;
}

.unit-nav {
	display: flex;
	flex-wrap: wrap;
	gap: 18px;
	list-style: none;
	padding: 0;
	margin: 0;
}

.unit-nav a {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	color: var(--text-secondary);
	font-size: 0.85rem;
}

.unit-nav a::before {
	content: "";
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--unit-color, var(--text-muted));
}

.unit-nav a:hover {
	color: var(--text);
	text-decoration: none;
}

/* Each unit's members live in a flyout, not a click-through page — hover
   (or keyboard focus) reveals the character list instead of making a unit
   archive page the only way to reach one. Styled after apple.com's own
   nav flyout: a full-width panel flush under the whole header (not a
   floating card under just the hovered item), fading/sliding in instead
   of snapping open — .unit-nav-item is deliberately NOT position:relative
   so .unit-submenu's containing block is .site-header itself, letting
   left:0/right:0 span the full viewport instead of just the <li>. */
.unit-submenu {
	position: absolute;
	top: 100%;
	left: 0;
	right: 0;
	margin: 0;
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	gap: 6px 36px;
	padding: 28px 32px;
	background: var(--bg);
	border-top: 1px solid var(--border);
	box-shadow: 0 16px 32px rgba(0, 0, 0, 0.06);
	z-index: 10;
	opacity: 0;
	visibility: hidden;
	transform: translateY(-6px);
	transition: opacity 0.22s ease, transform 0.22s ease, visibility 0s linear 0.22s;
}

/* .is-open (not :hover) is what actually reveals it — assets/js/unit-submenu.js
   holds it open for a short grace period after the pointer leaves, so
   crossing the empty gap between the nav link and this panel on the way
   down to a character link doesn't slam it shut mid-move. :focus-within
   stays as an instant, JS-independent fallback for keyboard users, who
   tab straight into the submenu with no gap to cross. */
.unit-nav-item.is-open .unit-submenu,
.unit-nav-item:focus-within .unit-submenu {
	opacity: 1;
	visibility: visible;
	transform: translateY(0);
	transition: opacity 0.22s ease, transform 0.22s ease;
}

.unit-submenu a {
	padding: 4px 0;
	white-space: nowrap;
}

/* Footer */
.site-footer {
	border-top: 1px solid var(--border);
	margin-top: 60px;
	padding: 24px 0;
	color: var(--text-muted);
	font-size: 0.8rem;
}

/* Post lists: shared by the homepage timeline, unit/character archives,
   and the generic index.php fallback — one visual language for "a list
   of posts" everywhere it appears. */
.timeline {
	max-width: 680px;
	margin: 0 auto;
	padding: 20px 0;
}

/* Proportions here are pulled straight from the bundled Twenty Twenty-Five
   theme.json (the maintainer's reference): x-large heading (1.75rem),
   regular weight, clamp(30px,7vw,70px) padding around each item, and
   metadata as a single quiet line rather than a chrome-heavy header row. */
.timeline-item {
	padding: clamp(30px, 7vw, 70px) 0;
}

/* ~3 blank lines of breathing room between the header divider and the
   first title — not between the title and the excerpt below it. */
.timeline-item:first-child {
	padding-top: 86px;
}

.timeline-title {
	margin: 0 0 16px;
	font-size: 1.75rem;
	font-weight: 400;
}

.timeline-title a {
	color: var(--text);
}

.timeline-title a:hover {
	text-decoration: none;
	color: var(--accent);
}

.timeline-excerpt {
	color: var(--text-secondary);
	font-size: 1rem;
	margin: 0 0 20px;
	display: -webkit-box;
	-webkit-line-clamp: 5;
	line-clamp: 5;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.timeline-footer {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 14px;
	font-size: 0.8rem;
	color: var(--text-muted);
}

.timeline-footer .avatar-sm {
	width: 18px;
	height: 18px;
	border-radius: 50%;
	object-fit: cover;
}

.timeline-footer .avatar-dot {
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--unit-color, var(--text-muted));
}

.timeline-footer .char-name {
	color: var(--text-muted);
}

.timeline-footer a:hover {
	color: var(--text);
}

/* Byline row on the single-post page (avatar + character + date directly
   under the title) — distinct from .timeline-footer above, which is the
   feed card's own metadata line. */
.timeline-byline {
	display: flex;
	align-items: center;
	gap: 6px;
	font-size: 0.85rem;
	color: var(--text-muted);
}

.timeline-byline .avatar-sm {
	width: 20px;
	height: 20px;
	border-radius: 50%;
	object-fit: cover;
}

.timeline-byline .avatar-dot {
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--unit-color, var(--text-muted));
}

.timeline-byline .char-name {
	color: var(--text-secondary);
	font-weight: 600;
}

.like-button {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	background: none;
	border: none;
	padding: 0;
	color: var(--text-muted);
	font-size: 0.85rem;
	font-family: inherit;
	cursor: pointer;
}

.like-button:hover {
	color: #ff4444;
}

.like-button .like-icon {
	display: inline-flex;
}

.like-button.liked {
	color: #ff4444;
	cursor: default;
}

.like-button.liked .like-icon svg {
	fill: currentColor;
}

/* Inline icon + text in the timeline footer and comments heading. */
.icon {
	vertical-align: -3px;
}

/* Share dropdown — same rounded-card-with-shadow language as the mobile
   .unit-nav dropdown, but always a dropdown (not just below a breakpoint):
   there's no inline-list variant of "share to 8 different places" that
   would ever fit next to the like/comment counts. */
.share-widget {
	position: relative;
	display: inline-flex;
}

.share-toggle {
	display: inline-flex;
	align-items: center;
	background: none;
	border: none;
	padding: 0;
	color: var(--text-muted);
	cursor: pointer;
}

.share-toggle:hover {
	color: var(--text);
}

.share-menu {
	display: none;
	position: absolute;
	top: 100%;
	right: 0;
	margin-top: 8px;
	min-width: 160px;
	list-style: none;
	padding: 6px;
	background: var(--bg);
	border: 1px solid var(--border);
	border-radius: 12px;
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
	z-index: 10;
}

.share-menu.is-open {
	display: block;
}

.share-menu li + li {
	margin-top: 2px;
}

.share-menu a,
.share-menu button {
	display: block;
	width: 100%;
	text-align: left;
	padding: 8px 10px;
	border-radius: 8px;
	background: none;
	border: none;
	font-family: inherit;
	font-size: 0.85rem;
	color: var(--text-secondary);
	cursor: pointer;
}

.share-menu a:hover,
.share-menu button:hover {
	background: var(--bg-secondary);
	color: var(--text);
	text-decoration: none;
}

/* Copy-link confirmation toast — the only feedback a "复制链接"-style
   action (WeChat/Discord/iMessage included) gives, since there's nothing
   else on screen to update. */
.nightcord-toast {
	position: fixed;
	left: 50%;
	bottom: 32px;
	transform: translateX(-50%) translateY(8px);
	background: var(--text);
	color: var(--bg);
	padding: 10px 18px;
	border-radius: 999px;
	font-size: 0.85rem;
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.2s ease, transform 0.2s ease;
	z-index: 100;
}

.nightcord-toast.is-visible {
	opacity: 1;
	transform: translateX(-50%) translateY(0);
}

/* Character corner page */
.character-corner {
	margin: 40px 0;
}

.character-corner .corner-banner {
	display: flex;
	align-items: center;
	gap: 20px;
	padding-bottom: 28px;
	border-bottom: 1px solid var(--border);
}

.character-corner .corner-banner h1 {
	margin: 0;
	font-size: 1.9rem;
}

.character-corner .corner-banner .avatar {
	width: 72px;
	height: 72px;
	object-fit: cover;
	border-radius: 50%;
	flex-shrink: 0;
}

.character-info,
.status-widget,
.unit-members {
	padding: 24px 0;
	border-bottom: 1px solid var(--border);
}

.character-info dl {
	display: grid;
	grid-template-columns: max-content 1fr;
	gap: 10px 20px;
	margin: 0;
}

.character-info dt {
	color: var(--text-muted);
	font-size: 0.9rem;
}

.status-widget {
	background: var(--bg-secondary);
	border-radius: 12px;
	padding: 18px 20px;
	border-bottom: none;
}

.status-widget h3 {
	margin-top: 0;
	font-size: 0.9rem;
	color: var(--text-muted);
	font-weight: 600;
}

.status-widget ul {
	list-style: none;
	padding: 0;
	margin: 0;
	font-size: 0.9rem;
}

.status-widget li + li {
	margin-top: 6px;
}

.status-widget .badge {
	display: inline-block;
	padding: 1px 9px;
	border-radius: 999px;
	font-size: 0.75rem;
	font-weight: 600;
	margin-left: 6px;
}

.status-widget .badge.high {
	background: #d1f7dd;
	color: #1c7a3c;
}

.status-widget .badge.medium {
	background: #fff3cd;
	color: #8a6d00;
}

.status-widget .badge.low {
	background: #ffe0e0;
	color: #7a1c1c;
}

.unit-members ul {
	list-style: none;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 8px 18px;
}

/* Single post: comments sit below the article, single column */
.single-post article {
	padding: 32px 0 0;
}

.single-post-header {
	margin-bottom: 24px;
}

.single-post-title-row {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 12px;
	margin-bottom: 12px;
}

.single-post-title-row h1 {
	font-size: 2rem;
	margin: 0;
}

.unit-tag {
	display: inline-block;
	padding: 2px 12px;
	border-radius: 999px;
	background: var(--bg-secondary);
	color: var(--text-secondary);
	font-size: 0.8rem;
	font-weight: 600;
	border-left: 3px solid var(--unit-color, var(--accent));
}

.unit-tag:hover {
	text-decoration: none;
	color: var(--text);
}

.single-post-content {
	font-size: 1.05rem;
}

/* Comments */
.comments-area {
	margin-top: 48px;
	border-top: 1px solid var(--border);
	padding-top: 28px;
}

.comment-list {
	list-style: none;
	padding: 0;
}

.comment-list .children {
	list-style: none;
	padding-left: 24px;
}

.comment-body {
	background: var(--bg-secondary);
	border-radius: 12px;
	padding: 14px 18px;
	margin: 10px 0;
}

.comment-body .comment-author-row {
	display: flex;
	align-items: center;
	gap: 6px;
	margin: 0;
}

.comment-body .comment-author-row .avatar-sm {
	width: 20px;
	height: 20px;
	border-radius: 50%;
	object-fit: cover;
}

.comment-body .comment-author-row .avatar-dot {
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--unit-color, var(--text-muted));
}

.comment-body .comment-author {
	font-weight: 600;
	color: var(--text);
}

.comment-body .comment-metadata {
	color: var(--text-muted);
	font-size: 0.75rem;
}

.comment-respond {
	margin-top: 24px;
}

.comment-respond input[type="text"],
.comment-respond input[type="email"],
.comment-respond textarea {
	width: 100%;
	background: var(--bg-secondary);
	border: none;
	color: var(--text);
	border-radius: 8px;
	padding: 10px 12px;
	font-family: inherit;
	font-size: 0.95rem;
}

.comment-respond textarea {
	min-height: 100px;
}

.comment-respond .form-submit input {
	background: var(--accent);
	color: #fff;
	border: none;
	padding: 9px 22px;
	border-radius: 999px;
	cursor: pointer;
	margin-top: 10px;
	font-size: 0.9rem;
	font-weight: 600;
}

.comment-respond .form-submit input:hover {
	background: #0077ed;
}

/* ---- Responsive ----
   apple.com's own tablet/phone cutoffs (1068px / 734px). Everything that
   can scale continuously already does via clamp()/% above; these two
   breakpoints only handle things that can't — the reserved right-rail
   (line + gutter) keeps existing at half its desktop width (16%, half of
   the desktop 32%) rather than the full reservation squeezing the reading
   column too hard, and the two-column spec list collapses to one stacked
   column. */
@media (max-width: 1068px) {
	/* Half the desktop 32% rail, not a fresh guess. */
	.single-post .site-wrap {
		padding-right: calc(16% + 32px);
	}

	#content::after {
		right: 16%;
	}
}

/* Below 11-inch-tablet-portrait width (iPad Air/Pro 11" are 820-834px
   portrait; iPad mini, the largest device below that class, is 744px) —
   everything from here down loses the hover-flyout nav entirely in favor
   of a tap-driven, iOS-Settings-style drill-down: tap the toggle for a
   full-screen list of units, tap a unit to slide into its member list,
   tap "back" to return. There's no CSS media feature for physical screen
   size, so this is a viewport-width approximation, not literal device
   detection — a phone held sideways wide enough to clear 800px would still
   read as "tablet" here. */
@media (max-width: 799px) {
	.unit-nav-toggle {
		display: block;
	}

	.unit-nav-panel {
		display: flex;
		position: fixed;
		inset: 0;
		flex-direction: column;
		/* Extra top padding — the panel is fixed/inset:0 so it covers the
		   header entirely; without it the unit list started right at the
		   very top edge of the screen instead of below where the header
		   visually was. */
		padding: 80px 24px 24px;
		background: var(--bg);
		overflow-y: auto;
		z-index: 100;
		opacity: 0;
		visibility: hidden;
		transform: translateY(-10px);
		pointer-events: none;
		transition: opacity 0.24s ease, transform 0.24s ease, visibility 0s linear 0.24s;
	}

	.unit-nav-panel.is-open {
		opacity: 1;
		visibility: visible;
		transform: translateY(0);
		pointer-events: auto;
		transition: opacity 0.24s ease, transform 0.24s ease;
	}

	.unit-nav-back {
		display: flex;
		align-items: center;
		gap: 4px;
		align-self: flex-start;
		margin-bottom: 16px;
		padding: 8px 4px;
		background: none;
		border: none;
		color: var(--text-secondary);
		font-family: inherit;
		font-size: 0.95rem;
		cursor: pointer;
	}

	.unit-nav-back[hidden] {
		display: none;
	}

	.unit-nav {
		flex-direction: column;
		gap: 2px;
	}

	/* Hidden via visibility (not opacity/display) specifically because a
	   descendant can override an ancestor's visibility:hidden with its own
	   visibility:visible — which is exactly what lets .unit-submenu below
	   still render (and animate in) despite living inside this now-hidden
	   list. It's covered by the incoming submenu's opaque background
	   either way; this is just to keep it out of tab order/hit-testing. */
	.unit-nav-panel.is-drilled .unit-nav {
		visibility: hidden;
	}

	/* Level 1: each unit is a big tappable row with a ›-style affordance —
	   there's no href entry point into the unit's own archive page here at
	   all (the link only navigates at ≥800px; below it, tapping always
	   drills into the member list instead — see unit-nav-toggle.js). */
	.unit-nav-link {
		display: flex;
		justify-content: space-between;
		padding: 16px 4px;
		font-size: 1.1rem;
	}

	.unit-nav-link::after {
		content: "›";
		color: var(--text-muted);
	}

	/* Always display:flex (never display:none) specifically so this can
	   animate — a display:none→flex swap paints straight to the end state
	   with no transition, since the browser never gets a frame to
	   interpolate from. Position:absolute + inset match the panel's own
	   padding box, so it overlays the (now-hidden) unit list underneath
	   and slides in from the right like an iOS navigation push. */
	.unit-submenu {
		display: flex;
		position: absolute;
		inset: 80px 24px 24px;
		flex-direction: column;
		gap: 2px;
		margin: 0;
		padding: 0;
		border: none;
		box-shadow: none;
		background: var(--bg);
		overflow-y: auto;
		opacity: 0;
		visibility: hidden;
		transform: translateX(24px);
		pointer-events: none;
		transition: opacity 0.28s ease, transform 0.28s ease, visibility 0s linear 0.28s;
	}

	.unit-nav-panel.is-drilled .unit-nav-item.is-active .unit-submenu {
		opacity: 1;
		visibility: visible;
		transform: translateX(0);
		pointer-events: auto;
		transition: opacity 0.28s ease, transform 0.28s ease;
	}

	.unit-submenu a {
		padding: 14px 4px;
		font-size: 1.05rem;
	}
}

@media (max-width: 734px) {
	/* Left gutter gets a quarter-tab (8%, half of the 16% tablet rail
	   above) on top of the base 20px — flush-to-edge content read as
	   cramped, but a full half-tab pushed it too far right. The right
	   side does the opposite: the reserved rail is a
	   placeholder for a future feature with nothing in it yet, and phone
	   width has no room to spare for it, so it's dropped entirely instead
	   of shrunk (single-post's padding-right override is removed too —
	   it now just inherits the same 20px every other page uses here). */
	.site-wrap {
		padding: 0 20px 0 calc(8% + 20px);
	}

	#content::after {
		display: none;
	}

	body {
		font-size: 16px;
	}

	.site-header .site-title {
		font-size: 1.6rem;
	}

	.timeline-item:first-child {
		padding-top: 48px;
	}

	.character-corner .corner-banner {
		flex-wrap: wrap;
		gap: 14px;
	}

	.character-info dl {
		grid-template-columns: 1fr;
	}
}
